Skip to content

Commit

Permalink
Support tags GET and POST arg in addition to tag
Browse files Browse the repository at this point in the history
  • Loading branch information
caronc committed Jan 13, 2024
1 parent a80ca0a commit 9a5c19a
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 62 deletions.
8 changes: 8 additions & 0 deletions apprise_api/api/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ class NotifyForm(forms.Form):
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):
"""
We just ensure there is a type always set
Expand Down
168 changes: 155 additions & 13 deletions apprise_api/api/tests/test_notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,33 @@ def test_notify_with_tags(self, mock_post):
'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()

# Send our notification by specifying the tag in the parameters
Expand Down Expand Up @@ -365,6 +391,9 @@ def test_notify_with_tags_via_apprise(self, mock_post):
{'config': config})
assert response.status_code == 200

# Reset our mock object
mock_post.reset_mock()

# Preare our form data
form_data = {
'body': 'test notifiction',
Expand All @@ -384,16 +413,44 @@ def test_notify_with_tags_via_apprise(self, mock_post):
assert response.status_code == 424
assert mock_post.call_count == 0

# Reset our mock object
mock_post.reset_mock()

# Update our tags
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()

# 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
Expand All @@ -405,6 +462,94 @@ def test_notify_with_tags_via_apprise(self, mock_post):
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)
# All of the rest of the variables can actually be over-ridden
# by the GET Parameter (ONLY if not otherwise identified in the
Expand All @@ -414,9 +559,6 @@ def test_notify_with_tags_via_apprise(self, mock_post):
'body': 'test notifiction',
}

# Reset our count
mock_post.reset_mock()

# Send our notification by specifying the tag in the parameters
response = self.client.post(
'/notify/{}?tag=home&format={}&type={}&title={}&body=ignored'
Expand Down Expand Up @@ -721,7 +863,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert response.status_code == 200
assert mock_notify.call_count == 1

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# Test referencing a key that doesn't exist
Expand Down Expand Up @@ -798,7 +940,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert response.status_code == 500
assert mock_notify.call_count == 0

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# Test with invalid format
Expand All @@ -817,7 +959,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert response.status_code == 400
assert mock_notify.call_count == 0

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# If an empty format is specified, it is accepted and
Expand All @@ -837,7 +979,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert response.status_code == 200
assert mock_notify.call_count == 1

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# Same results for any empty string:
Expand All @@ -851,7 +993,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert response.status_code == 200
assert mock_notify.call_count == 1

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

headers = {
Expand Down Expand Up @@ -1101,7 +1243,7 @@ def test_stateful_notify_recursion(self, mock_notify):
# No Recursion value specified
}

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# Recursion limit reached
Expand All @@ -1116,7 +1258,7 @@ def test_stateful_notify_recursion(self, mock_notify):
'HTTP_X-APPRISE-RECURSION-COUNT': str(2),
}

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# Recursion limit reached
Expand All @@ -1131,7 +1273,7 @@ def test_stateful_notify_recursion(self, mock_notify):
'HTTP_X-APPRISE-RECURSION-COUNT': str(-1),
}

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# invalid recursion specified
Expand All @@ -1146,7 +1288,7 @@ def test_stateful_notify_recursion(self, mock_notify):
'HTTP_X-APPRISE-RECURSION-COUNT': 'invalid',
}

# Reset our count
# Reset our mock object
mock_notify.reset_mock()

# invalid recursion specified
Expand Down
Loading

0 comments on commit 9a5c19a

Please sign in to comment.