mirror of
https://github.com/caronc/apprise-api.git
synced 2024-12-14 02:41:38 +01:00
Drop unnecessary file I/O handling + Auto-Detect Configuration Format (#18)
This commit is contained in:
parent
2be187acc4
commit
289fbd8640
@ -108,6 +108,10 @@ As an example, the `/json/urls/{KEY}` response might return something like this:
|
|||||||
Here is an example using `curl` as to how someone might send a notification to everyone associated with the tag `abc123` (using `/notify/{key}`):
|
Here is an example using `curl` as to how someone might send a notification to everyone associated with the tag `abc123` (using `/notify/{key}`):
|
||||||
```bash
|
```bash
|
||||||
# Send notification(s) to a {key} defined as 'abc123'
|
# Send notification(s) to a {key} defined as 'abc123'
|
||||||
|
curl -X POST -d "body=test message" \
|
||||||
|
http://localhost:8000/notify/abc123
|
||||||
|
|
||||||
|
# Here is the same request but using JSON instead:
|
||||||
curl -X POST -d '{"body":"test message"}' \
|
curl -X POST -d '{"body":"test message"}' \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://localhost:8000/notify/abc123
|
http://localhost:8000/notify/abc123
|
||||||
@ -118,6 +122,10 @@ curl -X POST -d '{"body":"test message"}' \
|
|||||||
```bash
|
```bash
|
||||||
# Send notification(s) to a {key} defined as 'abc123'
|
# Send notification(s) to a {key} defined as 'abc123'
|
||||||
# but only notify the URLs associated with the 'devops' tag
|
# but only notify the URLs associated with the 'devops' tag
|
||||||
|
curl -X POST -d 'tag=devops&body=test message' \
|
||||||
|
http://localhost:8000/notify/abc123
|
||||||
|
|
||||||
|
# Here is the same request but using JSON instead:
|
||||||
curl -X POST -d '{"tag":"devops", "body":"test message"}' \
|
curl -X POST -d '{"tag":"devops", "body":"test message"}' \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
http://localhost:8000/notify/abc123
|
http://localhost:8000/notify/abc123
|
||||||
|
@ -27,8 +27,12 @@ import apprise
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
# Auto-Detect Keyword
|
||||||
|
AUTO_DETECT_CONFIG_KEYWORD = 'auto'
|
||||||
|
|
||||||
# Define our potential configuration types
|
# Define our potential configuration types
|
||||||
CONFIG_FORMATS = (
|
CONFIG_FORMATS = (
|
||||||
|
(AUTO_DETECT_CONFIG_KEYWORD, _('Auto-Detect')),
|
||||||
(apprise.ConfigFormat.TEXT, _('TEXT')),
|
(apprise.ConfigFormat.TEXT, _('TEXT')),
|
||||||
(apprise.ConfigFormat.YAML, _('YAML')),
|
(apprise.ConfigFormat.YAML, _('YAML')),
|
||||||
)
|
)
|
||||||
|
@ -117,9 +117,11 @@ async function update() {
|
|||||||
// perform our status check
|
// perform our status check
|
||||||
let response = await fetch('{% url "get" key %}', {
|
let response = await fetch('{% url "get" key %}', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = await response;
|
|
||||||
if(response.status == 204)
|
if(response.status == 204)
|
||||||
{
|
{
|
||||||
// no problem; we simply have no content to retrieve
|
// no problem; we simply have no content to retrieve
|
||||||
@ -133,10 +135,22 @@ async function update() {
|
|||||||
document.querySelector('.config-overview li a[href="#notify"]')
|
document.querySelector('.config-overview li a[href="#notify"]')
|
||||||
.parentNode.classList.remove('disabled');
|
.parentNode.classList.remove('disabled');
|
||||||
|
|
||||||
|
// get our results
|
||||||
|
let result = await response.json();
|
||||||
|
|
||||||
// Set our configuration so it's visible
|
// Set our configuration so it's visible
|
||||||
response.text().then(function (text) {
|
document.querySelector('#id_config').value = result.config;
|
||||||
document.querySelector('#id_config').value = text;
|
// Set our format
|
||||||
});
|
document.querySelector('#id_format').value = result.format;
|
||||||
|
|
||||||
|
// dispatch our event to update our select box
|
||||||
|
if (typeof(Event) === 'function') {
|
||||||
|
var event = new Event('change');
|
||||||
|
} else { // for IE11
|
||||||
|
var event = document.createEvent('Event');
|
||||||
|
event.initEvent('change', true, true);
|
||||||
|
}
|
||||||
|
document.querySelector('#id_format').dispatchEvent(event);
|
||||||
|
|
||||||
// Ensure has-config sections are visible
|
// Ensure has-config sections are visible
|
||||||
document.querySelector('.has-config')
|
document.querySelector('.has-config')
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from apprise import ConfigFormat
|
from apprise import ConfigFormat
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
from ..forms import AUTO_DETECT_CONFIG_KEYWORD
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ class AddTests(SimpleTestCase):
|
|||||||
|
|
||||||
# Empty Text Configuration
|
# Empty Text Configuration
|
||||||
config = """
|
config = """
|
||||||
|
|
||||||
""" # noqa W293
|
""" # noqa W293
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/add/{}'.format(key), {
|
'/add/{}'.format(key), {
|
||||||
@ -138,7 +139,7 @@ class AddTests(SimpleTestCase):
|
|||||||
|
|
||||||
# Valid Text Configuration
|
# Valid Text Configuration
|
||||||
config = """
|
config = """
|
||||||
browser,media=notica://VToken
|
browser,media=notica://VTokenC
|
||||||
home=mailto://user:pass@hotmail.com
|
home=mailto://user:pass@hotmail.com
|
||||||
"""
|
"""
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@ -154,6 +155,27 @@ class AddTests(SimpleTestCase):
|
|||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Valid Yaml Configuration
|
||||||
|
config = """
|
||||||
|
urls:
|
||||||
|
- notica://VTokenD:
|
||||||
|
tag: browser,media
|
||||||
|
- mailto://user:pass@hotmail.com:
|
||||||
|
tag: home
|
||||||
|
"""
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
{'format': ConfigFormat.YAML, 'config': config})
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Test with JSON
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
data=json.dumps({'format': ConfigFormat.YAML, 'config': config}),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Test invalid config format
|
# Test invalid config format
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/add/{}'.format(key),
|
'/add/{}'.format(key),
|
||||||
@ -162,20 +184,6 @@ class AddTests(SimpleTestCase):
|
|||||||
)
|
)
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
|
|
||||||
with patch('tempfile.NamedTemporaryFile') as mock_ntf:
|
|
||||||
mock_ntf.side_effect = OSError
|
|
||||||
# we won't be able to write our retrieved configuration
|
|
||||||
# to disk for processing; we'll get a 500 error
|
|
||||||
response = self.client.post(
|
|
||||||
'/add/{}'.format(key),
|
|
||||||
data=json.dumps(
|
|
||||||
{'format': ConfigFormat.TEXT, 'config': config}),
|
|
||||||
content_type='application/json',
|
|
||||||
)
|
|
||||||
|
|
||||||
# internal errors are correctly identified
|
|
||||||
assert response.status_code == 500
|
|
||||||
|
|
||||||
# Test the handling of underlining disk/write exceptions
|
# Test the handling of underlining disk/write exceptions
|
||||||
with patch('gzip.open') as mock_open:
|
with patch('gzip.open') as mock_open:
|
||||||
mock_open.side_effect = OSError()
|
mock_open.side_effect = OSError()
|
||||||
@ -183,13 +191,89 @@ class AddTests(SimpleTestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/add/{}'.format(key),
|
'/add/{}'.format(key),
|
||||||
data=json.dumps(
|
data=json.dumps(
|
||||||
{'format': ConfigFormat.TEXT, 'config': config}),
|
{'format': ConfigFormat.YAML, 'config': config}),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
)
|
)
|
||||||
|
|
||||||
# internal errors are correctly identified
|
# internal errors are correctly identified
|
||||||
assert response.status_code == 500
|
assert response.status_code == 500
|
||||||
|
|
||||||
|
def test_save_auto_detect_config_format(self):
|
||||||
|
"""
|
||||||
|
Test adding an configuration and using the autodetect feature
|
||||||
|
"""
|
||||||
|
|
||||||
|
# our key to use
|
||||||
|
key = 'test_save_auto_detect_config_format'
|
||||||
|
|
||||||
|
# Empty Text Configuration
|
||||||
|
config = """
|
||||||
|
|
||||||
|
""" # noqa W293
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key), {
|
||||||
|
'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config})
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
# Valid Text Configuration
|
||||||
|
config = """
|
||||||
|
browser,media=notica://VTokenA
|
||||||
|
home=mailto://user:pass@hotmail.com
|
||||||
|
"""
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
{'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config})
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Test with JSON
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
data=json.dumps({'format': ConfigFormat.TEXT, 'config': config}),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Valid Yaml Configuration
|
||||||
|
config = """
|
||||||
|
urls:
|
||||||
|
- notica://VTokenB:
|
||||||
|
tag: browser,media
|
||||||
|
|
||||||
|
- mailto://user:pass@hotmail.com:
|
||||||
|
tag: home
|
||||||
|
"""
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
{'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config})
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Test with JSON
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
data=json.dumps(
|
||||||
|
{'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config}),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Test invalid config format that can not be auto-detected
|
||||||
|
config = """
|
||||||
|
42
|
||||||
|
"""
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
{'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config})
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
# Test with JSON
|
||||||
|
response = self.client.post(
|
||||||
|
'/add/{}'.format(key),
|
||||||
|
data=json.dumps(
|
||||||
|
{'format': AUTO_DETECT_CONFIG_KEYWORD, 'config': config}),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
def test_save_with_bad_input(self):
|
def test_save_with_bad_input(self):
|
||||||
"""
|
"""
|
||||||
Test adding with bad input in general
|
Test adding with bad input in general
|
||||||
|
@ -136,25 +136,6 @@ class JsonUrlsTests(SimpleTestCase):
|
|||||||
assert 'tags' in response.json()['urls'][0]
|
assert 'tags' in response.json()['urls'][0]
|
||||||
assert len(response.json()['urls'][0]['tags']) == 2
|
assert len(response.json()['urls'][0]['tags']) == 2
|
||||||
|
|
||||||
# Handle case when we try to retrieve our content but we have no idea
|
|
||||||
# what the format is in. Essentialy there had to have been disk
|
|
||||||
# corruption here or someone meddling with the backend.
|
|
||||||
with patch('tempfile.NamedTemporaryFile') as mock_ntf:
|
|
||||||
mock_ntf.side_effect = OSError
|
|
||||||
# Now retrieve our JSON resonse
|
|
||||||
response = self.client.get('/json/urls/{}'.format(key))
|
|
||||||
assert response.status_code == 500
|
|
||||||
assert response['Content-Type'].startswith('application/json')
|
|
||||||
assert 'tags' in response.json()
|
|
||||||
assert 'urls' in response.json()
|
|
||||||
|
|
||||||
# has error directive
|
|
||||||
assert 'error' in response.json()
|
|
||||||
|
|
||||||
# entries exist by are empty
|
|
||||||
assert len(response.json()['tags']) == 0
|
|
||||||
assert len(response.json()['urls']) == 0
|
|
||||||
|
|
||||||
# Verify that the correct Content-Type is set in the header of the
|
# Verify that the correct Content-Type is set in the header of the
|
||||||
# response
|
# response
|
||||||
assert 'Content-Type' in response
|
assert 'Content-Type' in response
|
||||||
|
@ -212,20 +212,6 @@ class NotifyTests(SimpleTestCase):
|
|||||||
'body': 'test message'
|
'body': 'test message'
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch('tempfile.NamedTemporaryFile') as mock_ntf:
|
|
||||||
mock_ntf.side_effect = OSError
|
|
||||||
# we won't be able to write our retrieved configuration
|
|
||||||
# to disk for processing; we'll get a 500 error
|
|
||||||
response = self.client.post(
|
|
||||||
'/notify/{}'.format(key),
|
|
||||||
data=json.dumps(json_data),
|
|
||||||
content_type='application/json',
|
|
||||||
)
|
|
||||||
|
|
||||||
# internal errors are correctly identified
|
|
||||||
assert response.status_code == 500
|
|
||||||
assert mock_notify.call_count == 0
|
|
||||||
|
|
||||||
# Test the handling of underlining disk/write exceptions
|
# Test the handling of underlining disk/write exceptions
|
||||||
with patch('gzip.open') as mock_open:
|
with patch('gzip.open') as mock_open:
|
||||||
mock_open.side_effect = OSError()
|
mock_open.side_effect = OSError()
|
||||||
|
@ -38,8 +38,9 @@ from .forms import AddByUrlForm
|
|||||||
from .forms import AddByConfigForm
|
from .forms import AddByConfigForm
|
||||||
from .forms import NotifyForm
|
from .forms import NotifyForm
|
||||||
from .forms import NotifyByUrlForm
|
from .forms import NotifyByUrlForm
|
||||||
|
from .forms import CONFIG_FORMATS
|
||||||
|
from .forms import AUTO_DETECT_CONFIG_KEYWORD
|
||||||
|
|
||||||
import tempfile
|
|
||||||
import apprise
|
import apprise
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
@ -175,7 +176,7 @@ class AddView(View):
|
|||||||
|
|
||||||
elif 'config' in content:
|
elif 'config' in content:
|
||||||
fmt = content.get('format', '').lower()
|
fmt = content.get('format', '').lower()
|
||||||
if fmt not in apprise.CONFIG_FORMATS:
|
if fmt not in [i[0] for i in CONFIG_FORMATS]:
|
||||||
# Format must be one supported by apprise
|
# Format must be one supported by apprise
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('The format specified is invalid.'),
|
_('The format specified is invalid.'),
|
||||||
@ -185,41 +186,32 @@ class AddView(View):
|
|||||||
# prepare our apprise config object
|
# prepare our apprise config object
|
||||||
ac_obj = apprise.AppriseConfig()
|
ac_obj = apprise.AppriseConfig()
|
||||||
|
|
||||||
try:
|
if fmt == AUTO_DETECT_CONFIG_KEYWORD:
|
||||||
# Write our file to a temporary file
|
# By setting format to None, it is automatically detected from
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
# within the add_config() call
|
||||||
# Write our content to disk
|
fmt = None
|
||||||
f.write(content['config'].encode())
|
|
||||||
f.flush()
|
|
||||||
|
|
||||||
if not ac_obj.add(
|
# Load our configuration
|
||||||
'file://{}?format={}'.format(f.name, fmt)):
|
if not ac_obj.add_config(content['config'], format=fmt):
|
||||||
|
# The format could not be detected
|
||||||
# Bad Configuration
|
|
||||||
return HttpResponse(
|
|
||||||
_('The configuration specified is invalid.'),
|
|
||||||
status=ResponseCode.bad_request,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add our configuration
|
|
||||||
a_obj.add(ac_obj)
|
|
||||||
|
|
||||||
if not len(a_obj):
|
|
||||||
# No specified URL(s) were loaded due to
|
|
||||||
# mis-configuration on the caller's part
|
|
||||||
return HttpResponse(
|
|
||||||
_('No valid URL(s) were specified.'),
|
|
||||||
status=ResponseCode.bad_request,
|
|
||||||
)
|
|
||||||
|
|
||||||
except OSError:
|
|
||||||
# We could not write the temporary file to disk
|
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('The configuration could not be loaded.'),
|
_('The configuration format could not be detected.'),
|
||||||
status=ResponseCode.internal_server_error,
|
status=ResponseCode.bad_request,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not ConfigCache.put(key, content['config'], fmt=fmt):
|
# Add our configuration
|
||||||
|
a_obj.add(ac_obj)
|
||||||
|
|
||||||
|
if not len(a_obj):
|
||||||
|
# No specified URL(s) were loaded due to
|
||||||
|
# mis-configuration on the caller's part
|
||||||
|
return HttpResponse(
|
||||||
|
_('No valid URL(s) were specified.'),
|
||||||
|
status=ResponseCode.bad_request,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not ConfigCache.put(
|
||||||
|
key, content['config'], fmt=ac_obj[0].config_format):
|
||||||
# Something went very wrong; return 500
|
# Something went very wrong; return 500
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('An error occured saving configuration.'),
|
_('An error occured saving configuration.'),
|
||||||
@ -281,6 +273,9 @@ class GetView(View):
|
|||||||
Handle a POST request
|
Handle a POST request
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Detect the format our response should be in
|
||||||
|
json_response = MIME_IS_JSON.match(request.content_type) is not None
|
||||||
|
|
||||||
config, format = ConfigCache.get(key)
|
config, format = ConfigCache.get(key)
|
||||||
if config is None:
|
if config is None:
|
||||||
# The returned value of config and format tell a rather cryptic
|
# The returned value of config and format tell a rather cryptic
|
||||||
@ -294,12 +289,24 @@ class GetView(View):
|
|||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('There was no configuration found.'),
|
_('There was no configuration found.'),
|
||||||
status=ResponseCode.no_content,
|
status=ResponseCode.no_content,
|
||||||
|
) if not json_response else JsonResponse({
|
||||||
|
'error': _('There was no configuration found.')
|
||||||
|
},
|
||||||
|
encoder=JSONEncoder,
|
||||||
|
safe=False,
|
||||||
|
status=ResponseCode.no_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Something went very wrong; return 500
|
# Something went very wrong; return 500
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('An error occured accessing configuration.'),
|
_('An error occured accessing configuration.'),
|
||||||
status=ResponseCode.internal_server_error,
|
status=ResponseCode.internal_server_error,
|
||||||
|
) if not json_response else JsonResponse({
|
||||||
|
'error': _('There was no configuration found.')
|
||||||
|
},
|
||||||
|
encoder=JSONEncoder,
|
||||||
|
safe=False,
|
||||||
|
status=ResponseCode.internal_server_error,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Our configuration was retrieved; now our response varies on whether
|
# Our configuration was retrieved; now our response varies on whether
|
||||||
@ -315,6 +322,13 @@ class GetView(View):
|
|||||||
config,
|
config,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
status=ResponseCode.okay,
|
status=ResponseCode.okay,
|
||||||
|
) if not json_response else JsonResponse({
|
||||||
|
'format': format,
|
||||||
|
'config': config,
|
||||||
|
},
|
||||||
|
encoder=JSONEncoder,
|
||||||
|
safe=False,
|
||||||
|
status=ResponseCode.okay,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -391,42 +405,26 @@ class NotifyView(View):
|
|||||||
# Create an apprise config object
|
# Create an apprise config object
|
||||||
ac_obj = apprise.AppriseConfig()
|
ac_obj = apprise.AppriseConfig()
|
||||||
|
|
||||||
try:
|
# Load our configuration
|
||||||
# Write our file to a temporary file containing our configuration
|
ac_obj.add_config(config, format=format)
|
||||||
# so that we can read it back. In the future a change will be to
|
|
||||||
# Apprise so that we can just directly write the configuration as
|
|
||||||
# is to the AppriseConfig() object... but for now...
|
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
|
||||||
# Write our content to disk
|
|
||||||
f.write(config.encode())
|
|
||||||
f.flush()
|
|
||||||
|
|
||||||
# Read our configuration back in to our configuration
|
# Add our configuration
|
||||||
ac_obj.add('file://{}?format={}'.format(f.name, format))
|
a_obj.add(ac_obj)
|
||||||
|
|
||||||
# Add our configuration
|
# Perform our notification at this point
|
||||||
a_obj.add(ac_obj)
|
result = a_obj.notify(
|
||||||
|
content.get('body'),
|
||||||
|
title=content.get('title', ''),
|
||||||
|
notify_type=content.get('type', apprise.NotifyType.INFO),
|
||||||
|
tag=content.get('tag'),
|
||||||
|
)
|
||||||
|
|
||||||
# Perform our notification at this point
|
if not result:
|
||||||
result = a_obj.notify(
|
# If at least one notification couldn't be sent; change up
|
||||||
content.get('body'),
|
# the response to a 424 error code
|
||||||
title=content.get('title', ''),
|
|
||||||
notify_type=content.get('type', apprise.NotifyType.INFO),
|
|
||||||
tag=content.get('tag'),
|
|
||||||
)
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
# If at least one notification couldn't be sent; change up
|
|
||||||
# the response to a 424 error code
|
|
||||||
return HttpResponse(
|
|
||||||
_('One or more notification could not be sent.'),
|
|
||||||
status=ResponseCode.failed_dependency)
|
|
||||||
|
|
||||||
except OSError:
|
|
||||||
# We could not write the temporary file to disk
|
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
_('The configuration could not be loaded.'),
|
_('One or more notification could not be sent.'),
|
||||||
status=ResponseCode.internal_server_error)
|
status=ResponseCode.failed_dependency)
|
||||||
|
|
||||||
# Return our retrieved content
|
# Return our retrieved content
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
@ -582,41 +580,21 @@ class JsonUrlView(View):
|
|||||||
# Create an apprise config object
|
# Create an apprise config object
|
||||||
ac_obj = apprise.AppriseConfig()
|
ac_obj = apprise.AppriseConfig()
|
||||||
|
|
||||||
try:
|
# Load our configuration
|
||||||
# Write our file to a temporary file containing our configuration
|
ac_obj.add_config(config, format=format)
|
||||||
# so that we can read it back. In the future a change will be to
|
|
||||||
# Apprise so that we can just directly write the configuration as
|
|
||||||
# is to the AppriseConfig() object... but for now...
|
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
|
||||||
# Write our content to disk
|
|
||||||
f.write(config.encode())
|
|
||||||
f.flush()
|
|
||||||
|
|
||||||
# Read our configuration back in to our configuration
|
# Add our configuration
|
||||||
ac_obj.add('file://{}?format={}'.format(f.name, format))
|
a_obj.add(ac_obj)
|
||||||
|
|
||||||
# Add our configuration
|
for notification in a_obj:
|
||||||
a_obj.add(ac_obj)
|
# Set Notification
|
||||||
|
response['urls'].append({
|
||||||
|
'url': notification.url(privacy=privacy),
|
||||||
|
'tags': notification.tags,
|
||||||
|
})
|
||||||
|
|
||||||
for notification in a_obj:
|
# Store Tags
|
||||||
# Set Notification
|
response['tags'] |= notification.tags
|
||||||
response['urls'].append({
|
|
||||||
'url': notification.url(privacy=privacy),
|
|
||||||
'tags': notification.tags,
|
|
||||||
})
|
|
||||||
|
|
||||||
# Store Tags
|
|
||||||
response['tags'] |= notification.tags
|
|
||||||
|
|
||||||
except OSError:
|
|
||||||
# We could not write the temporary file to disk
|
|
||||||
response['error'] = _('The configuration could not be loaded.'),
|
|
||||||
return JsonResponse(
|
|
||||||
response,
|
|
||||||
encoder=JSONEncoder,
|
|
||||||
safe=False,
|
|
||||||
status=ResponseCode.internal_server_error,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Return our retrieved content
|
# Return our retrieved content
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
django
|
django
|
||||||
apprise
|
apprise >= 0.8.8
|
||||||
|
Loading…
Reference in New Issue
Block a user