diff --git a/apprise_api/api/templates/welcome.html b/apprise_api/api/templates/welcome.html index bc80f0e..b242300 100644 --- a/apprise_api/api/templates/welcome.html +++ b/apprise_api/api/templates/welcome.html @@ -52,7 +52,7 @@

{% trans "Stateless Endpoints" %}

body - {% blocktrans %}Defines the message body. This field is required!{% endblocktrans %} + {% blocktrans %}Defines the message body. This field is required (unless body_not_required GET parameter is provided - only used with templated notifications).{% endblocktrans %} title @@ -453,7 +453,7 @@

{% trans "Persistent Store Endpoints" %}

body - {% blocktrans %}Defines the message body. This field is required!{% endblocktrans %} + {% blocktrans %}Defines the message body. This field is required (unless body_not_required GET parameter is provided - only used with templated notifications).{% endblocktrans %} title diff --git a/apprise_api/api/tests/test_notify.py b/apprise_api/api/tests/test_notify.py index 74ddeff..41ca2ac 100644 --- a/apprise_api/api/tests/test_notify.py +++ b/apprise_api/api/tests/test_notify.py @@ -1483,3 +1483,40 @@ def test_stateful_notify_recursion(self, mock_notify): '/notify/{}'.format(key), data=form_data, **headers) assert response.status_code == 400 assert mock_notify.call_count == 0 + + + @mock.patch('apprise.Apprise.notify') + def test_notify_no_body(self, mock_notify): + """ + Test sending a notification without a body (if supported) + """ + + key = "dummy" + + # Add some content + response = self.client.post( + '/add/{}'.format(key), + {'urls': 'onesignal://template_id:account_id@app_key/target_player_id'}) + assert response.status_code == 200 + + # Set our return value + mock_notify.return_value = True + + # Expect to fail because body is not provided + response = self.client.post( + '/notify/{}'.format(key), + data=json.dumps({}), + content_type='application/json', + ) + assert response.status_code == 400 and response.json() == { + "error": "Bad FORM Payload provided" + } + + # This now succeeds because body is set to not required explicitly + response = self.client.post( + '/notify/{}?body_not_required=true'.format(key), + data=json.dumps({}), + content_type='application/json', + ) + assert response.status_code == 200 and mock_notify.call_count == 1 + diff --git a/apprise_api/api/tests/test_stateless_notify.py b/apprise_api/api/tests/test_stateless_notify.py index db87416..e6eef43 100644 --- a/apprise_api/api/tests/test_stateless_notify.py +++ b/apprise_api/api/tests/test_stateless_notify.py @@ -757,3 +757,35 @@ def test_notify_with_filters(self, mock_send): # nothing was changed assert N_MGR['json'].enabled is True + + @mock.patch('apprise.Apprise.notify') + def test_notify_no_body(self, mock_notify): + """ + Test sending a notification without a body (if supported) + """ + + # Set our return value + mock_notify.return_value = True + + # Prepare our JSON data + json_data = { + 'urls': 'onesignal://template_id:account_id@app_key/target_player_id', + } + + # Expect to fail because body is not provided + response = self.client.post( + '/notify/', + data=json.dumps(json_data), + content_type='application/json', + ) + assert response.status_code == 400 and response.json() == { + "error": "Payload lacks minimum requirements" + } + + # This now succeeds because body is set to not required explicitly + response = self.client.post( + '/notify/?body_not_required=true', + data=json.dumps(json_data), + content_type='application/json', + ) + assert response.status_code == 200 and mock_notify.call_count == 1 diff --git a/apprise_api/api/views.py b/apprise_api/api/views.py index e286b9b..bda4e4b 100644 --- a/apprise_api/api/views.py +++ b/apprise_api/api/views.py @@ -707,6 +707,9 @@ def post(self, request, key): # rules rules = {k[1:]: v for k, v in request.GET.items() if k[0] == ':'} + body_not_required = request.GET.get('body_not_required') == 'true' + title_not_required = request.GET.get('title_not_required') == 'true' + # our content content = {} if not json_payload: @@ -761,7 +764,7 @@ def post(self, request, key): 'error': msg, }, encoder=JSONEncoder, safe=False, status=status) - if not content: + if not content and not body_not_required: # We could not handle the Content-Type logger.warning( 'NOTIFY - %s - Invalid FORM Payload provided using KEY: %s', @@ -912,7 +915,7 @@ def post(self, request, key): content['title'] = request.GET['title'] # Some basic error checking - if not content.get('body') and not attach or \ + if (not content.get('body') and not body_not_required and not attach) or \ content.get('type', apprise.NotifyType.INFO) \ not in apprise.NOTIFY_TYPES: @@ -1124,12 +1127,15 @@ def post(self, request, key): if content_type == 'text/html' else \ settings.LOGGING['formatters']['standard']['format'] + notif_body = content.get('body') if not body_not_required else apprise.NOT_REQUIRED + notif_title = content.get('title', '') if not title_not_required else apprise.NOT_REQUIRED + # Now specify our format (and over-ride the default): with apprise.LogCapture(level=level, fmt=fmt) as logs: # Perform our notification at this point result = a_obj.notify( - content.get('body'), - title=content.get('title', ''), + notif_body, + title=notif_title, notify_type=content.get('type', apprise.NotifyType.INFO), tag=content.get('tag'), attach=attach, @@ -1230,6 +1236,9 @@ def post(self, request): # rules rules = {k[1:]: v for k, v in request.GET.items() if k[0] == ':'} + body_not_required = request.GET.get('body_not_required') == 'true' + title_not_required = request.GET.get('title_not_required') == 'true' + # our content content = {} if not json_payload: @@ -1324,7 +1333,7 @@ def post(self, request): content['title'] = request.GET['title'] # Some basic error checking - if not content.get('body') or \ + if (not content.get('body') and not body_not_required) or \ content.get('type', apprise.NotifyType.INFO) \ not in apprise.NOTIFY_TYPES: @@ -1492,14 +1501,17 @@ def post(self, request): elif level == 'TRACE': level = logging.DEBUG - 1 + notif_body = content.get('body') if not body_not_required else apprise.NOT_REQUIRED + notif_title = content.get('title', '') if not title_not_required else apprise.NOT_REQUIRED + if settings.APPRISE_WEBHOOK_URL: esc = '' fmt = f'["%(levelname)s","%(asctime)s","{esc}%(message)s{esc}"]' with apprise.LogCapture(level=level, fmt=fmt) as logs: # Perform our notification at this point result = a_obj.notify( - content.get('body'), - title=content.get('title', ''), + notif_body, + title=notif_title, notify_type=content.get('type', apprise.NotifyType.INFO), tag='all', attach=attach, @@ -1527,8 +1539,8 @@ def post(self, request): else: # Perform our notification at this point result = a_obj.notify( - content.get('body'), - title=content.get('title', ''), + notif_body, + title=notif_title, notify_type=content.get('type', apprise.NotifyType.INFO), tag='all', attach=attach, diff --git a/requirements.txt b/requirements.txt index 5f78d9f..3b678db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ # apprise @ git+https://github.com/caronc/apprise@custom-tag-or-version ## 3. The below grabs our stable version (generally the best choice): -apprise == 1.8.0 +apprise == 1.9.0 ## Apprise API Minimum Requirements django