mirror of
https://github.com/caronc/apprise-api.git
synced 2025-03-04 17:43:01 +01:00
Support tags
GET and POST arg in addition to tag
This commit is contained in:
parent
a80ca0ae60
commit
9a5c19a7e9
@ -152,6 +152,14 @@ class NotifyForm(forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Allow support for tags keyword in addition to tag; the 'tag' field will always take priority over this
|
||||||
|
# however adding `tags` gives the user more flexibilty to use either/or keyword
|
||||||
|
tags = forms.CharField(
|
||||||
|
label=_('Tags'),
|
||||||
|
widget=forms.HiddenInput(),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
def clean_type(self):
|
def clean_type(self):
|
||||||
"""
|
"""
|
||||||
We just ensure there is a type always set
|
We just ensure there is a type always set
|
||||||
|
@ -312,7 +312,33 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'body': 'test notifiction',
|
'body': 'test notifiction',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# tags keyword is also supported
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}?tags=home'.format(key), form_data)
|
||||||
|
|
||||||
|
# Our notification was sent
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
|
||||||
|
# Test our posted data
|
||||||
|
response = json.loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
assert response['title'] == ''
|
||||||
|
assert response['message'] == form_data['body']
|
||||||
|
assert response['type'] == apprise.NotifyType.INFO
|
||||||
|
|
||||||
|
# Preare our form data (body is actually the minimum requirement)
|
||||||
|
# All of the rest of the variables can actually be over-ridden
|
||||||
|
# by the GET Parameter (ONLY if not otherwise identified in the
|
||||||
|
# payload). The Payload contents of the POST request always take
|
||||||
|
# priority to eliminate any ambiguity
|
||||||
|
form_data = {
|
||||||
|
'body': 'test notifiction',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
mock_post.reset_mock()
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Send our notification by specifying the tag in the parameters
|
# Send our notification by specifying the tag in the parameters
|
||||||
@ -365,6 +391,9 @@ class NotifyTests(SimpleTestCase):
|
|||||||
{'config': config})
|
{'config': config})
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Preare our form data
|
# Preare our form data
|
||||||
form_data = {
|
form_data = {
|
||||||
'body': 'test notifiction',
|
'body': 'test notifiction',
|
||||||
@ -384,17 +413,20 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 424
|
assert response.status_code == 424
|
||||||
assert mock_post.call_count == 0
|
assert mock_post.call_count == 0
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Update our tags
|
# Update our tags
|
||||||
form_data['tag'] = ['home', 'summer-home']
|
form_data['tag'] = ['home', 'summer-home']
|
||||||
|
|
||||||
# Now let's send our notification by specifying the tag in the
|
# Now let's send our notification by specifying the tag in the
|
||||||
# parameters
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/notify/{}/'.format(key), content_type='application/json',
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
data=form_data)
|
data=form_data)
|
||||||
|
|
||||||
# Send our notification
|
|
||||||
|
|
||||||
# Our notification was sent (as we matched 'home' OR' 'summer-home')
|
# Our notification was sent (as we matched 'home' OR' 'summer-home')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert mock_post.call_count == 1
|
assert mock_post.call_count == 1
|
||||||
@ -405,6 +437,119 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response['message'] == form_data['body']
|
assert response['message'] == form_data['body']
|
||||||
assert response['type'] == apprise.NotifyType.INFO
|
assert response['type'] == apprise.NotifyType.INFO
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# use the `tags` keyword instead which is also supported
|
||||||
|
del form_data['tag']
|
||||||
|
form_data['tags'] = ['home', 'summer-home']
|
||||||
|
|
||||||
|
# Now let's send our notification by specifying the tag in the
|
||||||
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
|
data=form_data)
|
||||||
|
|
||||||
|
# Our notification was sent (as we matched 'home' OR' 'summer-home')
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
|
||||||
|
# Test our posted data
|
||||||
|
response = json.loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
assert response['title'] == ''
|
||||||
|
assert response['message'] == form_data['body']
|
||||||
|
assert response['type'] == apprise.NotifyType.INFO
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# use the `tag` and `tags` keyword causes tag to always take priority
|
||||||
|
form_data['tag'] = ['invalid']
|
||||||
|
form_data['tags'] = ['home', 'summer-home']
|
||||||
|
|
||||||
|
# Now let's send our notification by specifying the tag in the
|
||||||
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
|
data=form_data)
|
||||||
|
|
||||||
|
# Our notification failed because 'tag' took priority over 'tags' and
|
||||||
|
# it contains an invalid entry
|
||||||
|
assert response.status_code == 424
|
||||||
|
assert mock_post.call_count == 0
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# integers or non string not accepted
|
||||||
|
form_data['tag'] = 42
|
||||||
|
del form_data['tags']
|
||||||
|
|
||||||
|
# Now let's send our notification by specifying the tag in the
|
||||||
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
|
data=form_data)
|
||||||
|
|
||||||
|
# Our notification failed because no tags were loaded
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert mock_post.call_count == 0
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# integers or non string not accepted
|
||||||
|
form_data['tag'] = [42, 'valid', 5.4]
|
||||||
|
|
||||||
|
# Now let's send our notification by specifying the tag in the
|
||||||
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
|
data=form_data)
|
||||||
|
|
||||||
|
# Our notification makes it through the list check and into the
|
||||||
|
# Apprise library. It will be at that level that the tags will fail
|
||||||
|
# validation so there will be no match
|
||||||
|
assert response.status_code == 424
|
||||||
|
assert mock_post.call_count == 0
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# continued to verify the use of the `tag` and `tags` keyword
|
||||||
|
# where tag priorities over tags
|
||||||
|
form_data['tags'] = ['invalid']
|
||||||
|
form_data['tag'] = ['home', 'summer-home']
|
||||||
|
|
||||||
|
# Now let's send our notification by specifying the tag in the
|
||||||
|
# parameters
|
||||||
|
|
||||||
|
# Send our notification
|
||||||
|
response = self.client.post(
|
||||||
|
'/notify/{}/'.format(key), content_type='application/json',
|
||||||
|
data=form_data)
|
||||||
|
|
||||||
|
# Our notification was sent (as we matched 'home' OR' 'summer-home')
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
|
||||||
|
# Test our posted data
|
||||||
|
response = json.loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
assert response['title'] == ''
|
||||||
|
assert response['message'] == form_data['body']
|
||||||
|
assert response['type'] == apprise.NotifyType.INFO
|
||||||
|
|
||||||
|
# Reset our mock object
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Preare our form data (body is actually the minimum requirement)
|
# Preare our form data (body is actually the minimum requirement)
|
||||||
# All of the rest of the variables can actually be over-ridden
|
# All of the rest of the variables can actually be over-ridden
|
||||||
# by the GET Parameter (ONLY if not otherwise identified in the
|
# by the GET Parameter (ONLY if not otherwise identified in the
|
||||||
@ -414,9 +559,6 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'body': 'test notifiction',
|
'body': 'test notifiction',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
|
||||||
mock_post.reset_mock()
|
|
||||||
|
|
||||||
# Send our notification by specifying the tag in the parameters
|
# Send our notification by specifying the tag in the parameters
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/notify/{}?tag=home&format={}&type={}&title={}&body=ignored'
|
'/notify/{}?tag=home&format={}&type={}&title={}&body=ignored'
|
||||||
@ -721,7 +863,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert mock_notify.call_count == 1
|
assert mock_notify.call_count == 1
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# Test referencing a key that doesn't exist
|
# Test referencing a key that doesn't exist
|
||||||
@ -798,7 +940,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 500
|
assert response.status_code == 500
|
||||||
assert mock_notify.call_count == 0
|
assert mock_notify.call_count == 0
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# Test with invalid format
|
# Test with invalid format
|
||||||
@ -817,7 +959,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
assert mock_notify.call_count == 0
|
assert mock_notify.call_count == 0
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# If an empty format is specified, it is accepted and
|
# If an empty format is specified, it is accepted and
|
||||||
@ -837,7 +979,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert mock_notify.call_count == 1
|
assert mock_notify.call_count == 1
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# Same results for any empty string:
|
# Same results for any empty string:
|
||||||
@ -851,7 +993,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert mock_notify.call_count == 1
|
assert mock_notify.call_count == 1
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
@ -1101,7 +1243,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
# No Recursion value specified
|
# No Recursion value specified
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# Recursion limit reached
|
# Recursion limit reached
|
||||||
@ -1116,7 +1258,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'HTTP_X-APPRISE-RECURSION-COUNT': str(2),
|
'HTTP_X-APPRISE-RECURSION-COUNT': str(2),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# Recursion limit reached
|
# Recursion limit reached
|
||||||
@ -1131,7 +1273,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'HTTP_X-APPRISE-RECURSION-COUNT': str(-1),
|
'HTTP_X-APPRISE-RECURSION-COUNT': str(-1),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# invalid recursion specified
|
# invalid recursion specified
|
||||||
@ -1146,7 +1288,7 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'HTTP_X-APPRISE-RECURSION-COUNT': 'invalid',
|
'HTTP_X-APPRISE-RECURSION-COUNT': 'invalid',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reset our count
|
# Reset our mock object
|
||||||
mock_notify.reset_mock()
|
mock_notify.reset_mock()
|
||||||
|
|
||||||
# invalid recursion specified
|
# invalid recursion specified
|
||||||
|
@ -370,7 +370,6 @@ class AddView(View):
|
|||||||
# Something went very wrong; return 500
|
# Something went very wrong; return 500
|
||||||
msg = _('An error occured saving configuration.')
|
msg = _('An error occured saving configuration.')
|
||||||
status = ResponseCode.internal_server_error
|
status = ResponseCode.internal_server_error
|
||||||
|
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
if not json_response else JsonResponse({
|
if not json_response else JsonResponse({
|
||||||
'error': msg,
|
'error': msg,
|
||||||
@ -487,17 +486,14 @@ class GetView(View):
|
|||||||
|
|
||||||
if settings.APPRISE_CONFIG_LOCK:
|
if settings.APPRISE_CONFIG_LOCK:
|
||||||
# General Access Control
|
# General Access Control
|
||||||
return HttpResponse(
|
msg = _('The site has been configured to deny this request.')
|
||||||
_('The site has been configured to deny this request.'),
|
status = ResponseCode.no_access
|
||||||
status=ResponseCode.no_access,
|
return HttpResponse(msg, status=status) \
|
||||||
) if not json_response else JsonResponse({
|
if not json_response else JsonResponse(
|
||||||
'error':
|
{'error': msg},
|
||||||
_('The site has been configured to deny this request.')
|
encoder=JSONEncoder,
|
||||||
},
|
safe=False,
|
||||||
encoder=JSONEncoder,
|
status=status)
|
||||||
safe=False,
|
|
||||||
status=ResponseCode.no_access,
|
|
||||||
)
|
|
||||||
|
|
||||||
config, format = ConfigCache.get(key)
|
config, format = ConfigCache.get(key)
|
||||||
if config is None:
|
if config is None:
|
||||||
@ -509,15 +505,14 @@ class GetView(View):
|
|||||||
# config != None: we simply have no data
|
# config != None: we simply have no data
|
||||||
if format is not None:
|
if format is not None:
|
||||||
# no content to return
|
# no content to return
|
||||||
return HttpResponse(
|
msg = _('There was no configuration found.')
|
||||||
_('There was no configuration found.'),
|
status = ResponseCode.no_content
|
||||||
status=ResponseCode.no_content,
|
return HttpResponse(msg, status=status) \
|
||||||
) if not json_response else JsonResponse({
|
if not json_response else JsonResponse(
|
||||||
'error': _('There was no configuration found.')},
|
{'error': msg},
|
||||||
encoder=JSONEncoder,
|
encoder=JSONEncoder,
|
||||||
safe=False,
|
safe=False,
|
||||||
status=ResponseCode.no_content,
|
status=status)
|
||||||
)
|
|
||||||
|
|
||||||
# Something went very wrong; return 500
|
# Something went very wrong; return 500
|
||||||
msg = _('An error occured accessing configuration.')
|
msg = _('An error occured accessing configuration.')
|
||||||
@ -586,15 +581,23 @@ class NotifyView(View):
|
|||||||
|
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
# could not parse JSON response...
|
# could not parse JSON response...
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Invalid JSON Payload provided',
|
||||||
|
request.META['REMOTE_ADDR'])
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
_('Invalid JSON specified.'),
|
_('Invalid JSON provided.'),
|
||||||
encoder=JSONEncoder,
|
encoder=JSONEncoder,
|
||||||
safe=False,
|
safe=False,
|
||||||
status=ResponseCode.bad_request)
|
status=ResponseCode.bad_request)
|
||||||
|
|
||||||
if not content:
|
if not content:
|
||||||
# We could not handle the Content-Type
|
# We could not handle the Content-Type
|
||||||
msg = _('The message format is not supported.')
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Invalid FORM Payload provided',
|
||||||
|
request.META['REMOTE_ADDR'])
|
||||||
|
|
||||||
|
msg = _('Bad FORM Payload provided.')
|
||||||
status = ResponseCode.bad_request
|
status = ResponseCode.bad_request
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
if not json_response else JsonResponse({
|
if not json_response else JsonResponse({
|
||||||
@ -602,7 +605,7 @@ class NotifyView(View):
|
|||||||
},
|
},
|
||||||
encoder=JSONEncoder,
|
encoder=JSONEncoder,
|
||||||
safe=False,
|
safe=False,
|
||||||
status=status,
|
status=status
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle Attachments
|
# Handle Attachments
|
||||||
@ -613,6 +616,11 @@ class NotifyView(View):
|
|||||||
content.get('attachment'), request.FILES)
|
content.get('attachment'), request.FILES)
|
||||||
|
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
# Invalid entry found in list
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Bad attachment specified',
|
||||||
|
request.META['REMOTE_ADDR'])
|
||||||
|
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('Bad attachment'),
|
_('Bad attachment'),
|
||||||
status=ResponseCode.bad_request)
|
status=ResponseCode.bad_request)
|
||||||
@ -621,26 +629,36 @@ class NotifyView(View):
|
|||||||
# Allow 'tag' value to be specified as part of the URL parameters
|
# Allow 'tag' value to be specified as part of the URL parameters
|
||||||
# if not found otherwise defined.
|
# if not found otherwise defined.
|
||||||
#
|
#
|
||||||
if not content.get('tag') and 'tag' in request.GET:
|
tag = content.get('tag', content.get('tags'))
|
||||||
content['tag'] = request.GET['tag']
|
if not tag:
|
||||||
|
# Allow GET parameter over-rides
|
||||||
|
if 'tag' in request.GET:
|
||||||
|
tag = request.GET['tag']
|
||||||
|
|
||||||
if content.get('tag'):
|
elif 'tags' in request.GET:
|
||||||
# Validation - Tag Logic:
|
tag = request.GET['tags']
|
||||||
# "TagA" : TagA
|
|
||||||
# "TagA, TagB" : TagA OR TagB
|
|
||||||
# "TagA TagB" : TagA AND TagB
|
|
||||||
# "TagA TagC, TagB" : (TagA AND TagC) OR TagB
|
|
||||||
# ['TagA', 'TagB'] : TagA OR TagB
|
|
||||||
# [('TagA', 'TagC'), 'TagB'] : (TagA AND TagC) OR TagB
|
|
||||||
# [('TagB', 'TagC')] : TagB AND TagC
|
|
||||||
|
|
||||||
tag = content.get('tag')
|
# Validation - Tag Logic:
|
||||||
|
# "TagA" : TagA
|
||||||
|
# "TagA, TagB" : TagA OR TagB
|
||||||
|
# "TagA TagB" : TagA AND TagB
|
||||||
|
# "TagA TagC, TagB" : (TagA AND TagC) OR TagB
|
||||||
|
# ['TagA', 'TagB'] : TagA OR TagB
|
||||||
|
# [('TagA', 'TagC'), 'TagB'] : (TagA AND TagC) OR TagB
|
||||||
|
# [('TagB', 'TagC')] : TagB AND TagC
|
||||||
|
if tag:
|
||||||
if isinstance(tag, (list, set, tuple)):
|
if isinstance(tag, (list, set, tuple)):
|
||||||
# Assign our tags as they were provided
|
# Assign our tags as they were provided
|
||||||
tags = tag
|
content['tag'] = tag
|
||||||
|
|
||||||
elif isinstance(tag, str):
|
elif isinstance(tag, str):
|
||||||
if not TAG_VALIDATION_RE.match(content.get('tag')):
|
if not TAG_VALIDATION_RE.match(tag):
|
||||||
|
# Invalid entry found in list
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Ignored invalid tag specified '
|
||||||
|
'(type %s): %s', request.META['REMOTE_ADDR'],
|
||||||
|
str(type(tag)), str(tag)[:12])
|
||||||
|
|
||||||
msg = _('Unsupported characters found in tag definition.')
|
msg = _('Unsupported characters found in tag definition.')
|
||||||
status = ResponseCode.bad_request
|
status = ResponseCode.bad_request
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
@ -654,7 +672,7 @@ class NotifyView(View):
|
|||||||
|
|
||||||
# If we get here, our specified tag was valid
|
# If we get here, our specified tag was valid
|
||||||
tags = []
|
tags = []
|
||||||
for _tag in TAG_DETECT_RE.findall(content.get('tag')):
|
for _tag in TAG_DETECT_RE.findall(tag):
|
||||||
tag = _tag.strip()
|
tag = _tag.strip()
|
||||||
if not tag:
|
if not tag:
|
||||||
continue
|
continue
|
||||||
@ -666,9 +684,25 @@ class NotifyView(View):
|
|||||||
else:
|
else:
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
|
|
||||||
# Update our tag block
|
# Assign our tags
|
||||||
content['tag'] = tags
|
content['tag'] = tags
|
||||||
|
|
||||||
|
else: # Could be int, float or some other unsupported type
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Ignored invalid tag specified (type %s): '
|
||||||
|
'%s', request.META['REMOTE_ADDR'],
|
||||||
|
str(type(tag)), str(tag)[:12])
|
||||||
|
|
||||||
|
msg = _('Unsupported characters found in tag definition.')
|
||||||
|
status = ResponseCode.bad_request
|
||||||
|
return HttpResponse(msg, status=status) \
|
||||||
|
if not json_response else JsonResponse({
|
||||||
|
'error': msg,
|
||||||
|
},
|
||||||
|
encoder=JSONEncoder,
|
||||||
|
safe=False,
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
#
|
#
|
||||||
# Allow 'format' value to be specified as part of the URL
|
# Allow 'format' value to be specified as part of the URL
|
||||||
# parameters if not found otherwise defined.
|
# parameters if not found otherwise defined.
|
||||||
@ -695,20 +729,26 @@ class NotifyView(View):
|
|||||||
content.get('type', apprise.NotifyType.INFO) \
|
content.get('type', apprise.NotifyType.INFO) \
|
||||||
not in apprise.NOTIFY_TYPES:
|
not in apprise.NOTIFY_TYPES:
|
||||||
|
|
||||||
msg = _('An invalid payload was specified.')
|
logger.warning(
|
||||||
status = ResponseCode.bad_request
|
'NOTIFY - %s - Payload lacks minimum requirements',
|
||||||
|
request.META['REMOTE_ADDR'])
|
||||||
|
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
if not json_response else JsonResponse({
|
if not json_response else JsonResponse({
|
||||||
'error': msg,
|
'error': _('Payload lacks minimum requirements.'),
|
||||||
},
|
},
|
||||||
encoder=JSONEncoder,
|
encoder=JSONEncoder,
|
||||||
safe=False,
|
safe=False,
|
||||||
status=status,
|
status=ResponseCode.bad_request,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Acquire our body format (if identified)
|
# Acquire our body format (if identified)
|
||||||
body_format = content.get('format', apprise.NotifyFormat.TEXT)
|
body_format = content.get('format', apprise.NotifyFormat.TEXT)
|
||||||
if body_format and body_format not in apprise.NOTIFY_FORMATS:
|
if body_format and body_format not in apprise.NOTIFY_FORMATS:
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Format parameter contains an unsupported '
|
||||||
|
'value (%s)', request.META['REMOTE_ADDR'], str(body_format))
|
||||||
|
|
||||||
msg = _('An invalid body input format was specified.')
|
msg = _('An invalid body input format was specified.')
|
||||||
status = ResponseCode.bad_request
|
status = ResponseCode.bad_request
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
@ -732,6 +772,9 @@ class NotifyView(View):
|
|||||||
# config != None: we simply have no data
|
# config != None: we simply have no data
|
||||||
if format is not None:
|
if format is not None:
|
||||||
# no content to return
|
# no content to return
|
||||||
|
logger.debug(
|
||||||
|
'NOTIFY - %s - Empty configuration found using KEY: %s',
|
||||||
|
request.META['REMOTE_ADDR'], key)
|
||||||
msg = _('There was no configuration found.')
|
msg = _('There was no configuration found.')
|
||||||
status = ResponseCode.no_content
|
status = ResponseCode.no_content
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
@ -746,6 +789,9 @@ class NotifyView(View):
|
|||||||
# Something went very wrong; return 500
|
# Something went very wrong; return 500
|
||||||
msg = _('An error occured accessing configuration.')
|
msg = _('An error occured accessing configuration.')
|
||||||
status = ResponseCode.internal_server_error
|
status = ResponseCode.internal_server_error
|
||||||
|
logger.error(
|
||||||
|
'NOTIFY - %s - I/O error accessing configuration '
|
||||||
|
'using KEY: %s', request.META['REMOTE_ADDR'], key)
|
||||||
return HttpResponse(msg, status=status) \
|
return HttpResponse(msg, status=status) \
|
||||||
if not json_response else JsonResponse({
|
if not json_response else JsonResponse({
|
||||||
'error': msg,
|
'error': msg,
|
||||||
@ -766,15 +812,19 @@ class NotifyView(View):
|
|||||||
kwargs['body_format'] = body_format
|
kwargs['body_format'] = body_format
|
||||||
|
|
||||||
# Acquire our recursion count (if defined)
|
# Acquire our recursion count (if defined)
|
||||||
|
recursion = request.headers.get('X-Apprise-Recursion-Count', 0)
|
||||||
try:
|
try:
|
||||||
recursion = \
|
recursion = int(recursion)
|
||||||
int(request.headers.get('X-Apprise-Recursion-Count', 0))
|
|
||||||
|
|
||||||
if recursion < 0:
|
if recursion < 0:
|
||||||
# We do not accept negative numbers
|
# We do not accept negative numbers
|
||||||
raise TypeError("Invalid Recursion Value")
|
raise TypeError("Invalid Recursion Value")
|
||||||
|
|
||||||
if recursion > settings.APPRISE_RECURSION_MAX:
|
if recursion > settings.APPRISE_RECURSION_MAX:
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Recursion limit reached (%d > %d)',
|
||||||
|
request.META['REMOTE_ADDR'], recursion,
|
||||||
|
settings.APPRISE_RECURSION_MAX)
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('The recursion limit has been reached.'),
|
_('The recursion limit has been reached.'),
|
||||||
status=ResponseCode.method_not_accepted)
|
status=ResponseCode.method_not_accepted)
|
||||||
@ -783,6 +833,9 @@ class NotifyView(View):
|
|||||||
kwargs['_recursion'] = recursion
|
kwargs['_recursion'] = recursion
|
||||||
|
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - Invalid recursion value (%s) provided',
|
||||||
|
request.META['REMOTE_ADDR'], str(recursion))
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('An invalid recursion value was specified.'),
|
_('An invalid recursion value was specified.'),
|
||||||
status=ResponseCode.bad_request)
|
status=ResponseCode.bad_request)
|
||||||
@ -907,6 +960,10 @@ class NotifyView(View):
|
|||||||
# the response to a 424 error code
|
# the response to a 424 error code
|
||||||
msg = _('One or more notification could not be sent.')
|
msg = _('One or more notification could not be sent.')
|
||||||
status = ResponseCode.failed_dependency
|
status = ResponseCode.failed_dependency
|
||||||
|
logger.warning(
|
||||||
|
'NOTIFY - %s - One or more notifications not '
|
||||||
|
'sent%s using KEY: %s', request.META['REMOTE_ADDR'],
|
||||||
|
'' if not tag else f' (Tags: {tag})', key)
|
||||||
return HttpResponse(response if response else msg, status=status) \
|
return HttpResponse(response if response else msg, status=status) \
|
||||||
if not json_response else JsonResponse({
|
if not json_response else JsonResponse({
|
||||||
'error': msg,
|
'error': msg,
|
||||||
@ -916,6 +973,10 @@ class NotifyView(View):
|
|||||||
status=status,
|
status=status,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
'NOTIFY - %s - Proccessed%s KEY: %s', request.META['REMOTE_ADDR'],
|
||||||
|
'' if not tag else f' (Tags: {tag}),', key)
|
||||||
|
|
||||||
# Return our retrieved content
|
# Return our retrieved content
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
response if response is not None else
|
response if response is not None else
|
||||||
|
Loading…
Reference in New Issue
Block a user