Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logging improvements and code tidying #184

Merged
merged 1 commit into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ curl -X POST -d '{"tag":"leaders teamA, leaders teamB", "body":"meeting now"}' \
| 400 | bad request | Your API call did not conform to what was documented here
| 405 | method not accepted | Your API call identified an action that has been disabled due to the Server configuration (such as a `apprise://` `APPRISE_RECURSION_MAX` being exceeded).
| 424 | failed dependency | At least one notification could not be sent. This can be due to<br/> - Not all notifications intended to be actioned could follow through (due to upstrem failures).<br/>You didn't idenify a tag associated with what was defined in your configuration.<br/>The tag(s) you specified do not match with those defined in your configuration.
| 431 | fields too large | This can happen if you're payload is larger then 3MB (default value). See `APPRISE_UPLOAD_MAX_MEMORY_SIZE` to adjust this.
| 500 | internal server error | This can occur if there was an issue saving your configuration to disk (usually the cause of permission issues).


Expand All @@ -358,6 +359,7 @@ The use of environment variables allow you to provide over-rides to default sett
| `APPRISE_CONFIG_DIR` | Defines an (optional) persistent store location of all configuration files saved. By default:<br/> - Configuration is written to the `apprise_api/var/config` directory when just using the _Django_ `manage runserver` script. However for the path for the container is `/config`.
| `APPRISE_ATTACH_DIR` | The directory the uploaded attachments are placed in. By default:<br/> - Attachments are written to the `apprise_api/var/attach` directory when just using the _Django_ `manage runserver` script. However for the path for the container is `/attach`.
| `APPRISE_ATTACH_SIZE` | Over-ride the attachment size (defined in MB). By default it is set to `200` (Megabytes). You can set this up to a maximum value of `500` which is the restriction in place for NginX (internal hosting ervice) at this time. If you set this to zero (`0`) then attachments will not be passed along even if provided.
| `APPRISE_UPLOAD_MAX_MEMORY_SIZE` | Over-ride the in-memory accepted payload size (defined in MB). By default it is set to `3` (Megabytes). There is no reason the HTTP payload (excluding attachments) should exceed this limit. This value is only configurable for those who have edge cases where there are exceptions to this rule.
| `APPRISE_STATELESS_URLS` | For a non-persistent solution, you can take advantage of this global variable. Use this to define a default set of Apprise URLs to notify when using API calls to `/notify`. If no `{KEY}` is defined when calling `/notify` then the URLs defined here are used instead. By default, nothing is defined for this variable.
| `APPRISE_STATEFUL_MODE` | This can be set to the following possible modes:<br/>📌 **hash**: This is also the default. It stores the server configuration in a hash formatted that can be easily indexed and compressed.<br/>📌 **simple**: Configuration is written straight to disk using the `{KEY}.cfg` (if `TEXT` based) and `{KEY}.yml` (if `YAML` based).<br/>📌 **disabled**: Straight up deny any read/write queries to the servers stateful store. Effectively turn off the Apprise Stateful feature completely.
| `APPRISE_CONFIG_LOCK` | Locks down your API hosting so that you can no longer delete/update/access stateful information. Your configuration is still referenced when stateful calls are made to `/notify`. The idea of this switch is to allow someone to set their (Apprise) configuration up and then as an added security tactic, they may choose to lock their configuration down (in a read-only state). Those who use the Apprise CLI tool may still do it, however the `--config` (`-c`) switch will not successfully reference this access point anymore. You can however use the `apprise://` plugin without any problem ([see here for more details](https://github.com/caronc/apprise/wiki/Notify_apprise_api)). This defaults to `no` and can however be set to `yes` by simply defining the global variable as such.
Expand Down
14 changes: 14 additions & 0 deletions apprise_api/api/tests/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from django.test import SimpleTestCase
from django.core.exceptions import RequestDataTooBig
from apprise import ConfigFormat
from unittest.mock import patch
from unittest import mock
from django.test.utils import override_settings
from ..forms import AUTO_DETECT_CONFIG_KEYWORD
import json
Expand Down Expand Up @@ -139,6 +141,18 @@ def test_save_config_by_urls(self):
)
assert response.status_code == 200

with mock.patch('json.loads') as mock_loads:
mock_loads.side_effect = RequestDataTooBig()
# Send our notification by specifying the tag in the parameters
response = self.client.post(
'/add/{}'.format(key),
data=json.dumps({'urls': 'mailto://user:[email protected]'}),
content_type='application/json',
)

# Our notification failed
assert response.status_code == 431

# Test with JSON (and no payload provided)
response = self.client.post(
'/add/{}'.format(key),
Expand Down
68 changes: 66 additions & 2 deletions apprise_api/api/tests/test_notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# THE SOFTWARE.
from django.test import SimpleTestCase, override_settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.exceptions import RequestDataTooBig
from unittest import mock
import requests
from ..forms import NotifyForm
Expand Down Expand Up @@ -577,6 +578,22 @@ def test_notify_with_tags_via_apprise(self, mock_post):
assert response['message'] == form_data['body']
assert response['type'] == apprise.NotifyType.WARNING

# Test case where RequestDataTooBig thrown
# Reset our mock object
mock_post.reset_mock()

with mock.patch('json.loads') as mock_loads:
mock_loads.side_effect = RequestDataTooBig()
# Send our notification by specifying the tag in the parameters
response = self.client.post(
f'/notify/{key}?tag=home&body=test',
form_data,
content_type='application/json')

# Our notification failed
assert response.status_code == 431
assert mock_post.call_count == 0

@mock.patch('requests.post')
def test_advanced_notify_with_tags(self, mock_post):
"""
Expand Down Expand Up @@ -883,7 +900,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):

# Preare our JSON data
json_data = {
'body': 'test notifiction',
'body': 'test notification',
'type': apprise.NotifyType.WARNING,
}

Expand Down Expand Up @@ -1059,6 +1076,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):

headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'debug',
# Accept is over-ridden to be that of the content type
'HTTP_ACCEPT': 'text/plain',
}

Expand All @@ -1074,13 +1092,46 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/plain'

mock_notify.reset_mock()

headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'debug',
# Accept is over-ridden to be that of the content type
'HTTP_ACCEPT': 'text/html',
}

# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
content_type='application/json',
**headers,
)

assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/html'

mock_notify.reset_mock()

# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data={'body': 'test'},
**headers,
)

assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'].startswith('text/html')

mock_notify.reset_mock()

headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'debug',
'HTTP_ACCEPT': '*/*',
}

# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
Expand All @@ -1091,7 +1142,7 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):

assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/html'
assert response['content-type'] == 'application/json'

headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'invalid',
Expand All @@ -1112,6 +1163,19 @@ def test_notify_by_loaded_urls_with_json(self, mock_notify):
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/html'

mock_notify.reset_mock()

# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data=json_data,
**headers,
)

assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'].startswith('text/html')

@mock.patch('apprise.plugins.NotifyEmail.NotifyEmail.send')
def test_notify_with_filters(self, mock_send):
"""
Expand Down
17 changes: 17 additions & 0 deletions apprise_api/api/tests/test_stateless_notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# THE SOFTWARE.
from django.test import SimpleTestCase
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.exceptions import RequestDataTooBig
from django.test.utils import override_settings
from unittest import mock
from ..forms import NotifyByUrlForm
Expand Down Expand Up @@ -442,6 +443,22 @@ def test_notify_with_get_parameters(self, mock_notify):
assert response.status_code == 200
assert mock_notify.call_count == 1

# Reset our count
mock_notify.reset_mock()

with mock.patch('json.loads') as mock_loads:
mock_loads.side_effect = RequestDataTooBig()
# Send our notification
response = self.client.post(
'/notify/?title=my%20title&format=text&type=info',
data=json.dumps(json_data),
content_type='application/json',
)

# Our notification failed
assert response.status_code == 431
assert mock_notify.call_count == 0

@mock.patch('apprise.Apprise.notify')
def test_notify_by_loaded_urls_with_json(self, mock_notify):
"""
Expand Down
Loading
Loading