mirror of https://github.com/caronc/apprise
more updates + test coverage added
parent
2c6eb4a8b2
commit
8373b5c297
|
@ -39,10 +39,9 @@
|
||||||
# - For Large Attachments: Mail.ReadWrite
|
# - For Large Attachments: Mail.ReadWrite
|
||||||
#
|
#
|
||||||
import requests
|
import requests
|
||||||
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from json import loads
|
|
||||||
from json import dumps
|
|
||||||
from .base import NotifyBase
|
from .base import NotifyBase
|
||||||
from .. import exception
|
from .. import exception
|
||||||
from ..url import PrivacyMode
|
from ..url import PrivacyMode
|
||||||
|
@ -432,7 +431,7 @@ class NotifyOffice365(NotifyBase):
|
||||||
else '{}: '.format(self.names[e]), e) for e in bcc])))
|
else '{}: '.format(self.names[e]), e) for e in bcc])))
|
||||||
|
|
||||||
# Perform upstream fetch
|
# Perform upstream fetch
|
||||||
postokay, response = self._fetch(url=url, payload=dumps(payload))
|
postokay, response = self._fetch(url=url, payload=payload)
|
||||||
|
|
||||||
# Test if we were okay
|
# Test if we were okay
|
||||||
if not postokay:
|
if not postokay:
|
||||||
|
@ -498,7 +497,9 @@ class NotifyOffice365(NotifyBase):
|
||||||
# "correlation_id": "fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7"
|
# "correlation_id": "fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7"
|
||||||
# }
|
# }
|
||||||
|
|
||||||
postokay, response = self._fetch(url=url, payload=payload)
|
postokay, response = self._fetch(
|
||||||
|
url=url, payload=payload,
|
||||||
|
content_type='application/x-www-form-urlencoded')
|
||||||
if not postokay:
|
if not postokay:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -525,7 +526,8 @@ class NotifyOffice365(NotifyBase):
|
||||||
# We're authenticated
|
# We're authenticated
|
||||||
return True if self.token else False
|
return True if self.token else False
|
||||||
|
|
||||||
def _fetch(self, url, payload, method='POST'):
|
def _fetch(self, url, payload, content_type='application/json',
|
||||||
|
method='POST'):
|
||||||
"""
|
"""
|
||||||
Wrapper to request object
|
Wrapper to request object
|
||||||
|
|
||||||
|
@ -534,6 +536,7 @@ class NotifyOffice365(NotifyBase):
|
||||||
# Prepare our headers:
|
# Prepare our headers:
|
||||||
headers = {
|
headers = {
|
||||||
'User-Agent': self.app_id,
|
'User-Agent': self.app_id,
|
||||||
|
'Content-Type': content_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.token:
|
if self.token:
|
||||||
|
@ -556,7 +559,8 @@ class NotifyOffice365(NotifyBase):
|
||||||
try:
|
try:
|
||||||
r = req(
|
r = req(
|
||||||
url,
|
url,
|
||||||
data=payload,
|
data=json.dumps(payload)
|
||||||
|
if content_type.endswith('/json') else payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
timeout=self.request_timeout,
|
timeout=self.request_timeout,
|
||||||
|
@ -591,6 +595,16 @@ class NotifyOffice365(NotifyBase):
|
||||||
# }}
|
# }}
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
# Another response type (error 415):
|
||||||
|
# {
|
||||||
|
# "error": {
|
||||||
|
# "code": "RequestBodyRead",
|
||||||
|
# "message": "A missing or empty content type header was \
|
||||||
|
# found when trying to read a message. The content \
|
||||||
|
# type header is required.",
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
'Response Details:\r\n{}'.format(r.content))
|
'Response Details:\r\n{}'.format(r.content))
|
||||||
|
|
||||||
|
@ -598,7 +612,7 @@ class NotifyOffice365(NotifyBase):
|
||||||
return (False, content)
|
return (False, content)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
content = loads(r.content)
|
content = json.loads(r.content)
|
||||||
|
|
||||||
except (AttributeError, TypeError, ValueError):
|
except (AttributeError, TypeError, ValueError):
|
||||||
# ValueError = r.content is Unparsable
|
# ValueError = r.content is Unparsable
|
||||||
|
|
|
@ -74,35 +74,14 @@ apprise_url_tests = (
|
||||||
tenant='tenant',
|
tenant='tenant',
|
||||||
# invalid client id
|
# invalid client id
|
||||||
cid='ab.',
|
cid='ab.',
|
||||||
aid='user@example.com',
|
aid='user2@example.com',
|
||||||
secret='abcd/123/3343/@jack/test',
|
secret='abcd/123/3343/@jack/test',
|
||||||
targets='/'.join(['email1@test.ca'])), {
|
targets='/'.join(['email1@test.ca'])), {
|
||||||
|
|
||||||
# Expected failure
|
# Expected failure
|
||||||
'instance': TypeError,
|
'instance': TypeError,
|
||||||
}),
|
}),
|
||||||
('o365://{aid}/{tenant}/{cid}/{secret}/{targets}?mode=invalid'.format(
|
('o365://{tenant}/{cid}/{secret}/{targets}'.format(
|
||||||
# Invalid mode
|
|
||||||
tenant='tenant',
|
|
||||||
cid='ab-cd-ef-gh',
|
|
||||||
aid='user@example.com',
|
|
||||||
secret='abcd/123/3343/@jack/test',
|
|
||||||
targets='/'.join(['email1@test.ca'])), {
|
|
||||||
|
|
||||||
# Expected failure
|
|
||||||
'instance': TypeError,
|
|
||||||
}),
|
|
||||||
('o365://{tenant}/{cid}/{secret}/{targets}?mode=user'.format(
|
|
||||||
# Invalid mode when no email specified
|
|
||||||
tenant='tenant',
|
|
||||||
cid='ab-cd-ef-gh',
|
|
||||||
secret='abcd/123/3343/@jack/test',
|
|
||||||
targets='/'.join(['email1@test.ca'])), {
|
|
||||||
|
|
||||||
# Expected failure
|
|
||||||
'instance': TypeError,
|
|
||||||
}),
|
|
||||||
('o365://{tenant}/{cid}/{secret}/{targets}?mode=self'.format(
|
|
||||||
# email not required if mode is set to self
|
# email not required if mode is set to self
|
||||||
tenant='tenant',
|
tenant='tenant',
|
||||||
cid='ab-cd-ef-gh',
|
cid='ab-cd-ef-gh',
|
||||||
|
@ -137,6 +116,49 @@ apprise_url_tests = (
|
||||||
# Our expected url(privacy=True) startswith() response:
|
# Our expected url(privacy=True) startswith() response:
|
||||||
'privacy_url': 'azure://user@example.edu/t...t/a...h/'
|
'privacy_url': 'azure://user@example.edu/t...t/a...h/'
|
||||||
'****/email1@test.ca/'}),
|
'****/email1@test.ca/'}),
|
||||||
|
('o365://{aid}/{tenant}/{cid}/{secret}/{targets}'.format(
|
||||||
|
tenant='tenant',
|
||||||
|
cid='ab-cd-ef-gh',
|
||||||
|
# Source can also be Object ID
|
||||||
|
aid='hg-fe-dc-ba',
|
||||||
|
secret='abcd/123/3343/@jack/test',
|
||||||
|
targets='/'.join(['email1@test.ca'])), {
|
||||||
|
|
||||||
|
# We're valid and good to go
|
||||||
|
'instance': NotifyOffice365,
|
||||||
|
|
||||||
|
# Test what happens if a batch send fails to return a messageCount
|
||||||
|
'requests_response_text': {
|
||||||
|
'expires_in': 2000,
|
||||||
|
'access_token': 'abcd1234',
|
||||||
|
},
|
||||||
|
|
||||||
|
# Our expected url(privacy=True) startswith() response:
|
||||||
|
'privacy_url': 'azure://hg-fe-dc-ba/t...t/a...h/'
|
||||||
|
'****/email1@test.ca/'}),
|
||||||
|
|
||||||
|
# ObjectID Specified, but no targets
|
||||||
|
('o365://{aid}/{tenant}/{cid}/{secret}/'.format(
|
||||||
|
tenant='tenant',
|
||||||
|
cid='ab-cd-ef-gh',
|
||||||
|
# Source can also be Object ID
|
||||||
|
aid='hg-fe-dc-ba',
|
||||||
|
secret='abcd/123/3343/@jack/test'), {
|
||||||
|
|
||||||
|
# We're valid and good to go
|
||||||
|
'instance': NotifyOffice365,
|
||||||
|
|
||||||
|
# Test what happens if a batch send fails to return a messageCount
|
||||||
|
'requests_response_text': {
|
||||||
|
'expires_in': 2000,
|
||||||
|
'access_token': 'abcd1234',
|
||||||
|
},
|
||||||
|
# No emails detected
|
||||||
|
'notify_response': False,
|
||||||
|
|
||||||
|
# Our expected url(privacy=True) startswith() response:
|
||||||
|
'privacy_url': 'azure://hg-fe-dc-ba/t...t/a...h/****'}),
|
||||||
|
|
||||||
# test our arguments
|
# test our arguments
|
||||||
('o365://_/?oauth_id={cid}&oauth_secret={secret}&tenant={tenant}'
|
('o365://_/?oauth_id={cid}&oauth_secret={secret}&tenant={tenant}'
|
||||||
'&to={targets}&from={aid}'.format(
|
'&to={targets}&from={aid}'.format(
|
||||||
|
@ -297,26 +319,6 @@ def test_plugin_office365_general(mock_post):
|
||||||
targets=None,
|
targets=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
# Invalid email
|
|
||||||
NotifyOffice365(
|
|
||||||
email='invalid',
|
|
||||||
client_id=client_id,
|
|
||||||
tenant=tenant,
|
|
||||||
secret=secret,
|
|
||||||
targets=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
# Invalid email
|
|
||||||
NotifyOffice365(
|
|
||||||
email='garbage',
|
|
||||||
client_id=client_id,
|
|
||||||
tenant=tenant,
|
|
||||||
secret=secret,
|
|
||||||
targets=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# One of the targets are invalid
|
# One of the targets are invalid
|
||||||
obj = NotifyOffice365(
|
obj = NotifyOffice365(
|
||||||
email=email,
|
email=email,
|
||||||
|
@ -479,19 +481,52 @@ def test_plugin_office365_attachments(mock_post):
|
||||||
body='body', title='title', notify_type=NotifyType.INFO,
|
body='body', title='title', notify_type=NotifyType.INFO,
|
||||||
attach=attach) is True
|
attach=attach) is True
|
||||||
|
|
||||||
|
assert mock_post.call_count == 2
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://login.microsoftonline.com/{}/oauth2/v2.0/token'.format(tenant)
|
||||||
|
assert mock_post.call_args_list[0][1]['headers'] \
|
||||||
|
.get('Content-Type') == 'application/x-www-form-urlencoded'
|
||||||
|
assert mock_post.call_args_list[1][0][0] == \
|
||||||
|
'https://graph.microsoft.com/v1.0/users/{}/sendMail'.format(email)
|
||||||
|
assert mock_post.call_args_list[1][1]['headers'] \
|
||||||
|
.get('Content-Type') == 'application/json'
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Test invalid attachment
|
# Test invalid attachment
|
||||||
path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg')
|
path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg')
|
||||||
assert obj.notify(
|
assert obj.notify(
|
||||||
body='body', title='title', notify_type=NotifyType.INFO,
|
body='body', title='title', notify_type=NotifyType.INFO,
|
||||||
attach=path) is False
|
attach=path) is False
|
||||||
|
assert mock_post.call_count == 0
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
with mock.patch('base64.b64encode', side_effect=OSError()):
|
with mock.patch('base64.b64encode', side_effect=OSError()):
|
||||||
# We can't send the message if we fail to parse the data
|
# We can't send the message if we fail to parse the data
|
||||||
assert obj.notify(
|
assert obj.notify(
|
||||||
body='body', title='title', notify_type=NotifyType.INFO,
|
body='body', title='title', notify_type=NotifyType.INFO,
|
||||||
attach=attach) is False
|
attach=attach) is False
|
||||||
|
assert mock_post.call_count == 0
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# Force a smaller attachment size forcing us to create an attachment
|
||||||
|
obj.outlook_attachment_inline_max = 50
|
||||||
|
# We can't create an attachment now..
|
||||||
|
assert obj.notify(
|
||||||
|
body='body', title='title', notify_type=NotifyType.INFO,
|
||||||
|
attach=attach) is True
|
||||||
|
|
||||||
|
# Can't send attachment
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://graph.microsoft.com/v1.0/users/{}/sendMail'.format(email)
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
assert obj.notify(
|
assert obj.notify(
|
||||||
body='body', title='title', notify_type=NotifyType.INFO,
|
body='body', title='title', notify_type=NotifyType.INFO,
|
||||||
attach=attach) is True
|
attach=attach) is True
|
||||||
assert mock_post.call_count == 3
|
|
||||||
|
# already authenticated
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://graph.microsoft.com/v1.0/users/{}/sendMail'.format(email)
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
Loading…
Reference in New Issue