mirror of
https://github.com/caronc/apprise.git
synced 2025-06-24 19:51:21 +02:00
Support custom field mappings for JSON, FORM and XML Services (#876)
This commit is contained in:
parent
1e30be32d9
commit
b0e64126e6
@ -40,6 +40,16 @@ from ..common import NotifyType
|
|||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class FORMPayloadField:
|
||||||
|
"""
|
||||||
|
Identifies the fields available in the FORM Payload
|
||||||
|
"""
|
||||||
|
VERSION = 'version'
|
||||||
|
TITLE = 'title'
|
||||||
|
MESSAGE = 'message'
|
||||||
|
MESSAGETYPE = 'type'
|
||||||
|
|
||||||
|
|
||||||
# Defines the method to send the notification
|
# Defines the method to send the notification
|
||||||
METHODS = (
|
METHODS = (
|
||||||
'POST',
|
'POST',
|
||||||
@ -96,6 +106,12 @@ class NotifyForm(NotifyBase):
|
|||||||
# local anyway
|
# local anyway
|
||||||
request_rate_per_sec = 0
|
request_rate_per_sec = 0
|
||||||
|
|
||||||
|
# Define the FORM version to place in all payloads
|
||||||
|
# Version: Major.Minor, Major is only updated if the entire schema is
|
||||||
|
# changed. If just adding new items (or removing old ones, only increment
|
||||||
|
# the Minor!
|
||||||
|
form_version = '1.0'
|
||||||
|
|
||||||
# Define object templates
|
# Define object templates
|
||||||
templates = (
|
templates = (
|
||||||
'{schema}://{host}',
|
'{schema}://{host}',
|
||||||
@ -218,6 +234,18 @@ class NotifyForm(NotifyBase):
|
|||||||
self.attach_as += self.attach_as_count
|
self.attach_as += self.attach_as_count
|
||||||
self.attach_multi_support = True
|
self.attach_multi_support = True
|
||||||
|
|
||||||
|
# A payload map allows users to over-ride the default mapping if
|
||||||
|
# they're detected with the :overide=value. Normally this would
|
||||||
|
# create a new key and assign it the value specified. However
|
||||||
|
# if the key you specify is actually an internally mapped one,
|
||||||
|
# then a re-mapping takes place using the value
|
||||||
|
self.payload_map = {
|
||||||
|
FORMPayloadField.VERSION: FORMPayloadField.VERSION,
|
||||||
|
FORMPayloadField.TITLE: FORMPayloadField.TITLE,
|
||||||
|
FORMPayloadField.MESSAGE: FORMPayloadField.MESSAGE,
|
||||||
|
FORMPayloadField.MESSAGETYPE: FORMPayloadField.MESSAGETYPE,
|
||||||
|
}
|
||||||
|
|
||||||
self.params = {}
|
self.params = {}
|
||||||
if params:
|
if params:
|
||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
@ -228,10 +256,20 @@ class NotifyForm(NotifyBase):
|
|||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
self.headers.update(headers)
|
self.headers.update(headers)
|
||||||
|
|
||||||
|
self.payload_overrides = {}
|
||||||
self.payload_extras = {}
|
self.payload_extras = {}
|
||||||
if payload:
|
if payload:
|
||||||
# Store our extra payload entries
|
# Store our extra payload entries
|
||||||
self.payload_extras.update(payload)
|
self.payload_extras.update(payload)
|
||||||
|
for key in list(self.payload_extras.keys()):
|
||||||
|
# Any values set in the payload to alter a system related one
|
||||||
|
# alters the system key. Hence :message=msg maps the 'message'
|
||||||
|
# variable that otherwise already contains the payload to be
|
||||||
|
# 'msg' instead (containing the payload)
|
||||||
|
if key in self.payload_map:
|
||||||
|
self.payload_map[key] = self.payload_extras[key]
|
||||||
|
self.payload_overrides[key] = self.payload_extras[key]
|
||||||
|
del self.payload_extras[key]
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -257,6 +295,8 @@ class NotifyForm(NotifyBase):
|
|||||||
# Append our payload extra's into our parameters
|
# Append our payload extra's into our parameters
|
||||||
params.update(
|
params.update(
|
||||||
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
||||||
|
params.update(
|
||||||
|
{':{}'.format(k): v for k, v in self.payload_overrides.items()})
|
||||||
|
|
||||||
if self.attach_as != self.attach_as_default:
|
if self.attach_as != self.attach_as_default:
|
||||||
# Provide Attach-As extension details
|
# Provide Attach-As extension details
|
||||||
@ -337,15 +377,18 @@ class NotifyForm(NotifyBase):
|
|||||||
'form:// Multi-Attachment Support not enabled')
|
'form:// Multi-Attachment Support not enabled')
|
||||||
|
|
||||||
# prepare Form Object
|
# prepare Form Object
|
||||||
payload = {
|
payload = {}
|
||||||
# Version: Major.Minor, Major is only updated if the entire
|
|
||||||
# schema is changed. If just adding new items (or removing
|
for key, value in (
|
||||||
# old ones, only increment the Minor!
|
(FORMPayloadField.VERSION, self.form_version),
|
||||||
'version': '1.0',
|
(FORMPayloadField.TITLE, title),
|
||||||
'title': title,
|
(FORMPayloadField.MESSAGE, body),
|
||||||
'message': body,
|
(FORMPayloadField.MESSAGETYPE, notify_type)):
|
||||||
'type': notify_type,
|
|
||||||
}
|
if not self.payload_map[key]:
|
||||||
|
# Do not store element in payload response
|
||||||
|
continue
|
||||||
|
payload[self.payload_map[key]] = value
|
||||||
|
|
||||||
# Apply any/all payload over-rides defined
|
# Apply any/all payload over-rides defined
|
||||||
payload.update(self.payload_extras)
|
payload.update(self.payload_extras)
|
||||||
|
@ -41,6 +41,17 @@ from ..common import NotifyType
|
|||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class JSONPayloadField:
|
||||||
|
"""
|
||||||
|
Identifies the fields available in the JSON Payload
|
||||||
|
"""
|
||||||
|
VERSION = 'version'
|
||||||
|
TITLE = 'title'
|
||||||
|
MESSAGE = 'message'
|
||||||
|
ATTACHMENTS = 'attachments'
|
||||||
|
MESSAGETYPE = 'type'
|
||||||
|
|
||||||
|
|
||||||
# Defines the method to send the notification
|
# Defines the method to send the notification
|
||||||
METHODS = (
|
METHODS = (
|
||||||
'POST',
|
'POST',
|
||||||
@ -76,6 +87,12 @@ class NotifyJSON(NotifyBase):
|
|||||||
# local anyway
|
# local anyway
|
||||||
request_rate_per_sec = 0
|
request_rate_per_sec = 0
|
||||||
|
|
||||||
|
# Define the JSON version to place in all payloads
|
||||||
|
# Version: Major.Minor, Major is only updated if the entire schema is
|
||||||
|
# changed. If just adding new items (or removing old ones, only increment
|
||||||
|
# the Minor!
|
||||||
|
json_version = '1.0'
|
||||||
|
|
||||||
# Define object templates
|
# Define object templates
|
||||||
templates = (
|
templates = (
|
||||||
'{schema}://{host}',
|
'{schema}://{host}',
|
||||||
@ -162,6 +179,19 @@ class NotifyJSON(NotifyBase):
|
|||||||
self.logger.warning(msg)
|
self.logger.warning(msg)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
# A payload map allows users to over-ride the default mapping if
|
||||||
|
# they're detected with the :overide=value. Normally this would
|
||||||
|
# create a new key and assign it the value specified. However
|
||||||
|
# if the key you specify is actually an internally mapped one,
|
||||||
|
# then a re-mapping takes place using the value
|
||||||
|
self.payload_map = {
|
||||||
|
JSONPayloadField.VERSION: JSONPayloadField.VERSION,
|
||||||
|
JSONPayloadField.TITLE: JSONPayloadField.TITLE,
|
||||||
|
JSONPayloadField.MESSAGE: JSONPayloadField.MESSAGE,
|
||||||
|
JSONPayloadField.ATTACHMENTS: JSONPayloadField.ATTACHMENTS,
|
||||||
|
JSONPayloadField.MESSAGETYPE: JSONPayloadField.MESSAGETYPE,
|
||||||
|
}
|
||||||
|
|
||||||
self.params = {}
|
self.params = {}
|
||||||
if params:
|
if params:
|
||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
@ -172,10 +202,21 @@ class NotifyJSON(NotifyBase):
|
|||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
self.headers.update(headers)
|
self.headers.update(headers)
|
||||||
|
|
||||||
|
self.payload_overrides = {}
|
||||||
self.payload_extras = {}
|
self.payload_extras = {}
|
||||||
if payload:
|
if payload:
|
||||||
# Store our extra payload entries
|
# Store our extra payload entries
|
||||||
self.payload_extras.update(payload)
|
self.payload_extras.update(payload)
|
||||||
|
for key in list(self.payload_extras.keys()):
|
||||||
|
# Any values set in the payload to alter a system related one
|
||||||
|
# alters the system key. Hence :message=msg maps the 'message'
|
||||||
|
# variable that otherwise already contains the payload to be
|
||||||
|
# 'msg' instead (containing the payload)
|
||||||
|
if key in self.payload_map:
|
||||||
|
self.payload_map[key] = self.payload_extras[key].strip()
|
||||||
|
self.payload_overrides[key] = \
|
||||||
|
self.payload_extras[key].strip()
|
||||||
|
del self.payload_extras[key]
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -201,6 +242,8 @@ class NotifyJSON(NotifyBase):
|
|||||||
# Append our payload extra's into our parameters
|
# Append our payload extra's into our parameters
|
||||||
params.update(
|
params.update(
|
||||||
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
||||||
|
params.update(
|
||||||
|
{':{}'.format(k): v for k, v in self.payload_overrides.items()})
|
||||||
|
|
||||||
# Determine Authentication
|
# Determine Authentication
|
||||||
auth = ''
|
auth = ''
|
||||||
@ -275,16 +318,18 @@ class NotifyJSON(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# prepare JSON Object
|
# prepare JSON Object
|
||||||
payload = {
|
payload = {}
|
||||||
# Version: Major.Minor, Major is only updated if the entire
|
for key, value in (
|
||||||
# schema is changed. If just adding new items (or removing
|
(JSONPayloadField.VERSION, self.json_version),
|
||||||
# old ones, only increment the Minor!
|
(JSONPayloadField.TITLE, title),
|
||||||
'version': '1.0',
|
(JSONPayloadField.MESSAGE, body),
|
||||||
'title': title,
|
(JSONPayloadField.ATTACHMENTS, attachments),
|
||||||
'message': body,
|
(JSONPayloadField.MESSAGETYPE, notify_type)):
|
||||||
'attachments': attachments,
|
|
||||||
'type': notify_type,
|
if not self.payload_map[key]:
|
||||||
}
|
# Do not store element in payload response
|
||||||
|
continue
|
||||||
|
payload[self.payload_map[key]] = value
|
||||||
|
|
||||||
# Apply any/all payload over-rides defined
|
# Apply any/all payload over-rides defined
|
||||||
payload.update(self.payload_extras)
|
payload.update(self.payload_extras)
|
||||||
|
@ -41,6 +41,16 @@ from ..common import NotifyType
|
|||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class XMLPayloadField:
|
||||||
|
"""
|
||||||
|
Identifies the fields available in the JSON Payload
|
||||||
|
"""
|
||||||
|
VERSION = 'Version'
|
||||||
|
TITLE = 'Subject'
|
||||||
|
MESSAGE = 'Message'
|
||||||
|
MESSAGETYPE = 'MessageType'
|
||||||
|
|
||||||
|
|
||||||
# Defines the method to send the notification
|
# Defines the method to send the notification
|
||||||
METHODS = (
|
METHODS = (
|
||||||
'POST',
|
'POST',
|
||||||
@ -78,7 +88,8 @@ class NotifyXML(NotifyBase):
|
|||||||
|
|
||||||
# XSD Information
|
# XSD Information
|
||||||
xsd_ver = '1.1'
|
xsd_ver = '1.1'
|
||||||
xsd_url = 'https://raw.githubusercontent.com/caronc/apprise/master' \
|
xsd_default_url = \
|
||||||
|
'https://raw.githubusercontent.com/caronc/apprise/master' \
|
||||||
'/apprise/assets/NotifyXML-{version}.xsd'
|
'/apprise/assets/NotifyXML-{version}.xsd'
|
||||||
|
|
||||||
# Define object templates
|
# Define object templates
|
||||||
@ -161,7 +172,7 @@ class NotifyXML(NotifyBase):
|
|||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<soapenv:Body>
|
<soapenv:Body>
|
||||||
<Notification xmlns:xsi="{{XSD_URL}}">
|
<Notification{{XSD_URL}}>
|
||||||
{{CORE}}
|
{{CORE}}
|
||||||
{{ATTACHMENTS}}
|
{{ATTACHMENTS}}
|
||||||
</Notification>
|
</Notification>
|
||||||
@ -180,6 +191,18 @@ class NotifyXML(NotifyBase):
|
|||||||
self.logger.warning(msg)
|
self.logger.warning(msg)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
# A payload map allows users to over-ride the default mapping if
|
||||||
|
# they're detected with the :overide=value. Normally this would
|
||||||
|
# create a new key and assign it the value specified. However
|
||||||
|
# if the key you specify is actually an internally mapped one,
|
||||||
|
# then a re-mapping takes place using the value
|
||||||
|
self.payload_map = {
|
||||||
|
XMLPayloadField.VERSION: XMLPayloadField.VERSION,
|
||||||
|
XMLPayloadField.TITLE: XMLPayloadField.TITLE,
|
||||||
|
XMLPayloadField.MESSAGE: XMLPayloadField.MESSAGE,
|
||||||
|
XMLPayloadField.MESSAGETYPE: XMLPayloadField.MESSAGETYPE,
|
||||||
|
}
|
||||||
|
|
||||||
self.params = {}
|
self.params = {}
|
||||||
if params:
|
if params:
|
||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
@ -190,6 +213,10 @@ class NotifyXML(NotifyBase):
|
|||||||
# Store our extra headers
|
# Store our extra headers
|
||||||
self.headers.update(headers)
|
self.headers.update(headers)
|
||||||
|
|
||||||
|
# Set our xsd url
|
||||||
|
self.xsd_url = self.xsd_default_url.format(version=self.xsd_ver)
|
||||||
|
|
||||||
|
self.payload_overrides = {}
|
||||||
self.payload_extras = {}
|
self.payload_extras = {}
|
||||||
if payload:
|
if payload:
|
||||||
# Store our extra payload entries (but tidy them up since they will
|
# Store our extra payload entries (but tidy them up since they will
|
||||||
@ -201,8 +228,20 @@ class NotifyXML(NotifyBase):
|
|||||||
'Ignoring invalid XML Stanza element name({})'
|
'Ignoring invalid XML Stanza element name({})'
|
||||||
.format(k))
|
.format(k))
|
||||||
continue
|
continue
|
||||||
self.payload_extras[key] = v
|
|
||||||
|
|
||||||
|
# Any values set in the payload to alter a system related one
|
||||||
|
# alters the system key. Hence :message=msg maps the 'message'
|
||||||
|
# variable that otherwise already contains the payload to be
|
||||||
|
# 'msg' instead (containing the payload)
|
||||||
|
if key in self.payload_map:
|
||||||
|
self.payload_map[key] = v
|
||||||
|
self.payload_overrides[key] = v
|
||||||
|
|
||||||
|
# Over-ride XSD URL as data is no longer known
|
||||||
|
self.xsd_url = None
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.payload_extras[key] = v
|
||||||
return
|
return
|
||||||
|
|
||||||
def url(self, privacy=False, *args, **kwargs):
|
def url(self, privacy=False, *args, **kwargs):
|
||||||
@ -227,6 +266,8 @@ class NotifyXML(NotifyBase):
|
|||||||
# Append our payload extra's into our parameters
|
# Append our payload extra's into our parameters
|
||||||
params.update(
|
params.update(
|
||||||
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
{':{}'.format(k): v for k, v in self.payload_extras.items()})
|
||||||
|
params.update(
|
||||||
|
{':{}'.format(k): v for k, v in self.payload_overrides.items()})
|
||||||
|
|
||||||
# Determine Authentication
|
# Determine Authentication
|
||||||
auth = ''
|
auth = ''
|
||||||
@ -273,14 +314,21 @@ class NotifyXML(NotifyBase):
|
|||||||
# Our XML Attachmement subsitution
|
# Our XML Attachmement subsitution
|
||||||
xml_attachments = ''
|
xml_attachments = ''
|
||||||
|
|
||||||
# Our Payload Base
|
payload_base = {}
|
||||||
payload_base = {
|
|
||||||
'Version': self.xsd_ver,
|
for key, value in (
|
||||||
'Subject': NotifyXML.escape_html(title, whitespace=False),
|
(XMLPayloadField.VERSION, self.xsd_ver),
|
||||||
'MessageType': NotifyXML.escape_html(
|
(XMLPayloadField.TITLE, NotifyXML.escape_html(
|
||||||
notify_type, whitespace=False),
|
title, whitespace=False)),
|
||||||
'Message': NotifyXML.escape_html(body, whitespace=False),
|
(XMLPayloadField.MESSAGE, NotifyXML.escape_html(
|
||||||
}
|
body, whitespace=False)),
|
||||||
|
(XMLPayloadField.MESSAGETYPE, NotifyXML.escape_html(
|
||||||
|
notify_type, whitespace=False))):
|
||||||
|
|
||||||
|
if not self.payload_map[key]:
|
||||||
|
# Do not store element in payload response
|
||||||
|
continue
|
||||||
|
payload_base[self.payload_map[key]] = value
|
||||||
|
|
||||||
# Apply our payload extras
|
# Apply our payload extras
|
||||||
payload_base.update(
|
payload_base.update(
|
||||||
@ -328,7 +376,8 @@ class NotifyXML(NotifyBase):
|
|||||||
''.join(attachments) + '</Attachments>'
|
''.join(attachments) + '</Attachments>'
|
||||||
|
|
||||||
re_map = {
|
re_map = {
|
||||||
'{{XSD_URL}}': self.xsd_url.format(version=self.xsd_ver),
|
'{{XSD_URL}}':
|
||||||
|
f' xmlns:xsi="{self.xsd_url}"' if self.xsd_url else '',
|
||||||
'{{ATTACHMENTS}}': xml_attachments,
|
'{{ATTACHMENTS}}': xml_attachments,
|
||||||
'{{CORE}}': xml_base,
|
'{{CORE}}': xml_base,
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
mock_get.return_value = response
|
mock_get.return_value = response
|
||||||
|
|
||||||
results = NotifyForm.parse_url(
|
results = NotifyForm.parse_url(
|
||||||
'form://localhost:8080/command?:abcd=test&method=POST')
|
'form://localhost:8080/command?:message=msg&:abcd=test&method=POST')
|
||||||
|
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert results['user'] is None
|
assert results['user'] is None
|
||||||
@ -332,6 +332,7 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
assert results['url'] == 'form://localhost:8080/command'
|
assert results['url'] == 'form://localhost:8080/command'
|
||||||
assert isinstance(results['qsd:'], dict) is True
|
assert isinstance(results['qsd:'], dict) is True
|
||||||
assert results['qsd:']['abcd'] == 'test'
|
assert results['qsd:']['abcd'] == 'test'
|
||||||
|
assert results['qsd:']['message'] == 'msg'
|
||||||
|
|
||||||
instance = NotifyForm(**results)
|
instance = NotifyForm(**results)
|
||||||
assert isinstance(instance, NotifyForm)
|
assert isinstance(instance, NotifyForm)
|
||||||
@ -347,8 +348,11 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
assert details[1]['data']['abcd'] == 'test'
|
assert details[1]['data']['abcd'] == 'test'
|
||||||
assert 'title' in details[1]['data']
|
assert 'title' in details[1]['data']
|
||||||
assert details[1]['data']['title'] == 'title'
|
assert details[1]['data']['title'] == 'title'
|
||||||
assert 'message' in details[1]['data']
|
assert 'message' not in details[1]['data']
|
||||||
assert details[1]['data']['message'] == 'body'
|
# message over-ride was provided; the body is now in `msg` and not
|
||||||
|
# `message`
|
||||||
|
assert 'msg' in details[1]['data']
|
||||||
|
assert details[1]['data']['msg'] == 'body'
|
||||||
|
|
||||||
assert instance.url(privacy=False).startswith(
|
assert instance.url(privacy=False).startswith(
|
||||||
'form://localhost:8080/command?')
|
'form://localhost:8080/command?')
|
||||||
@ -364,7 +368,7 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
mock_get.reset_mock()
|
mock_get.reset_mock()
|
||||||
|
|
||||||
results = NotifyForm.parse_url(
|
results = NotifyForm.parse_url(
|
||||||
'form://localhost:8080/command?:message=test&method=POST')
|
'form://localhost:8080/command?:type=&:message=msg&method=POST')
|
||||||
|
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert results['user'] is None
|
assert results['user'] is None
|
||||||
@ -377,7 +381,7 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
assert results['schema'] == 'form'
|
assert results['schema'] == 'form'
|
||||||
assert results['url'] == 'form://localhost:8080/command'
|
assert results['url'] == 'form://localhost:8080/command'
|
||||||
assert isinstance(results['qsd:'], dict) is True
|
assert isinstance(results['qsd:'], dict) is True
|
||||||
assert results['qsd:']['message'] == 'test'
|
assert results['qsd:']['message'] == 'msg'
|
||||||
|
|
||||||
instance = NotifyForm(**results)
|
instance = NotifyForm(**results)
|
||||||
assert isinstance(instance, NotifyForm)
|
assert isinstance(instance, NotifyForm)
|
||||||
@ -391,9 +395,18 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
assert details[0][0] == 'http://localhost:8080/command'
|
assert details[0][0] == 'http://localhost:8080/command'
|
||||||
assert 'title' in details[1]['data']
|
assert 'title' in details[1]['data']
|
||||||
assert details[1]['data']['title'] == 'title'
|
assert details[1]['data']['title'] == 'title'
|
||||||
|
|
||||||
|
# type was removed from response object
|
||||||
|
assert 'type' not in details[1]['data']
|
||||||
|
|
||||||
|
# message over-ride was provided; the body is now in `msg` and not
|
||||||
|
# `message`
|
||||||
|
assert details[1]['data']['msg'] == 'body'
|
||||||
|
|
||||||
# 'body' is over-ridden by 'test' passed inline with the URL
|
# 'body' is over-ridden by 'test' passed inline with the URL
|
||||||
assert 'message' in details[1]['data']
|
assert 'message' not in details[1]['data']
|
||||||
assert details[1]['data']['message'] == 'test'
|
assert 'msg' in details[1]['data']
|
||||||
|
assert details[1]['data']['msg'] == 'body'
|
||||||
|
|
||||||
assert instance.url(privacy=False).startswith(
|
assert instance.url(privacy=False).startswith(
|
||||||
'form://localhost:8080/command?')
|
'form://localhost:8080/command?')
|
||||||
@ -438,8 +451,9 @@ def test_plugin_custom_form_edge_cases(mock_get, mock_post):
|
|||||||
assert 'title' in details[1]['params']
|
assert 'title' in details[1]['params']
|
||||||
assert details[1]['params']['title'] == 'title'
|
assert details[1]['params']['title'] == 'title'
|
||||||
# 'body' is over-ridden by 'test' passed inline with the URL
|
# 'body' is over-ridden by 'test' passed inline with the URL
|
||||||
assert 'message' in details[1]['params']
|
assert 'message' not in details[1]['params']
|
||||||
assert details[1]['params']['message'] == 'test'
|
assert 'test' in details[1]['params']
|
||||||
|
assert details[1]['params']['test'] == 'body'
|
||||||
|
|
||||||
assert instance.url(privacy=False).startswith(
|
assert instance.url(privacy=False).startswith(
|
||||||
'form://localhost:8080/command?')
|
'form://localhost:8080/command?')
|
||||||
|
@ -176,8 +176,11 @@ def test_plugin_custom_json_edge_cases(mock_get, mock_post):
|
|||||||
mock_post.return_value = response
|
mock_post.return_value = response
|
||||||
mock_get.return_value = response
|
mock_get.return_value = response
|
||||||
|
|
||||||
|
# This string also tests that type is set to nothing
|
||||||
results = NotifyJSON.parse_url(
|
results = NotifyJSON.parse_url(
|
||||||
'json://localhost:8080/command?:message=test&method=GET')
|
'json://localhost:8080/command?'
|
||||||
|
':message=msg&:test=value&method=GET'
|
||||||
|
'&:type=')
|
||||||
|
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert results['user'] is None
|
assert results['user'] is None
|
||||||
@ -190,7 +193,9 @@ def test_plugin_custom_json_edge_cases(mock_get, mock_post):
|
|||||||
assert results['schema'] == 'json'
|
assert results['schema'] == 'json'
|
||||||
assert results['url'] == 'json://localhost:8080/command'
|
assert results['url'] == 'json://localhost:8080/command'
|
||||||
assert isinstance(results['qsd:'], dict) is True
|
assert isinstance(results['qsd:'], dict) is True
|
||||||
assert results['qsd:']['message'] == 'test'
|
assert results['qsd:']['message'] == 'msg'
|
||||||
|
# empty special mapping
|
||||||
|
assert results['qsd:']['type'] == ''
|
||||||
|
|
||||||
instance = NotifyJSON(**results)
|
instance = NotifyJSON(**results)
|
||||||
assert isinstance(instance, NotifyJSON)
|
assert isinstance(instance, NotifyJSON)
|
||||||
@ -205,9 +210,16 @@ def test_plugin_custom_json_edge_cases(mock_get, mock_post):
|
|||||||
assert 'title' in details[1]['data']
|
assert 'title' in details[1]['data']
|
||||||
dataset = json.loads(details[1]['data'])
|
dataset = json.loads(details[1]['data'])
|
||||||
assert dataset['title'] == 'title'
|
assert dataset['title'] == 'title'
|
||||||
assert 'message' in dataset
|
assert 'message' not in dataset
|
||||||
# message over-ride was provided
|
assert 'msg' in dataset
|
||||||
assert dataset['message'] == 'test'
|
# type was set to nothing which implies it should be removed
|
||||||
|
assert 'type' not in dataset
|
||||||
|
# message over-ride was provided; the body is now in `msg` and not
|
||||||
|
# `message`
|
||||||
|
assert dataset['msg'] == 'body'
|
||||||
|
|
||||||
|
assert 'test' in dataset
|
||||||
|
assert dataset['test'] == 'value'
|
||||||
|
|
||||||
assert instance.url(privacy=False).startswith(
|
assert instance.url(privacy=False).startswith(
|
||||||
'json://localhost:8080/command?')
|
'json://localhost:8080/command?')
|
||||||
|
@ -251,8 +251,8 @@ def test_plugin_custom_xml_edge_cases(mock_get, mock_post):
|
|||||||
mock_get.return_value = response
|
mock_get.return_value = response
|
||||||
|
|
||||||
results = NotifyXML.parse_url(
|
results = NotifyXML.parse_url(
|
||||||
'xml://localhost:8080/command?:Message=test&method=GET'
|
'xml://localhost:8080/command?:Message=Body&method=GET'
|
||||||
'&:Key=value&:,=invalid')
|
'&:Key=value&:,=invalid&:MessageType=')
|
||||||
|
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert results['user'] is None
|
assert results['user'] is None
|
||||||
@ -265,13 +265,16 @@ def test_plugin_custom_xml_edge_cases(mock_get, mock_post):
|
|||||||
assert results['schema'] == 'xml'
|
assert results['schema'] == 'xml'
|
||||||
assert results['url'] == 'xml://localhost:8080/command'
|
assert results['url'] == 'xml://localhost:8080/command'
|
||||||
assert isinstance(results['qsd:'], dict) is True
|
assert isinstance(results['qsd:'], dict) is True
|
||||||
assert results['qsd:']['Message'] == 'test'
|
assert results['qsd:']['Message'] == 'Body'
|
||||||
assert results['qsd:']['Key'] == 'value'
|
assert results['qsd:']['Key'] == 'value'
|
||||||
assert results['qsd:'][','] == 'invalid'
|
assert results['qsd:'][','] == 'invalid'
|
||||||
|
|
||||||
instance = NotifyXML(**results)
|
instance = NotifyXML(**results)
|
||||||
assert isinstance(instance, NotifyXML)
|
assert isinstance(instance, NotifyXML)
|
||||||
|
|
||||||
|
# XSD URL is disabled due to custom formatting
|
||||||
|
assert instance.xsd_url is None
|
||||||
|
|
||||||
response = instance.send(title='title', body='body')
|
response = instance.send(title='title', body='body')
|
||||||
assert response is True
|
assert response is True
|
||||||
assert mock_post.call_count == 0
|
assert mock_post.call_count == 0
|
||||||
@ -290,9 +293,63 @@ def test_plugin_custom_xml_edge_cases(mock_get, mock_post):
|
|||||||
|
|
||||||
# Test our data set for our key/value pair
|
# Test our data set for our key/value pair
|
||||||
assert re.search(r'<Version>[1-9]+\.[0-9]+</Version>', details[1]['data'])
|
assert re.search(r'<Version>[1-9]+\.[0-9]+</Version>', details[1]['data'])
|
||||||
assert re.search('<MessageType>info</MessageType>', details[1]['data'])
|
|
||||||
assert re.search('<Subject>title</Subject>', details[1]['data'])
|
assert re.search('<Subject>title</Subject>', details[1]['data'])
|
||||||
# Custom entry Message acts as Over-ride and kicks in here
|
|
||||||
assert re.search('<Message>test</Message>', details[1]['data'])
|
assert re.search('<Message>test</Message>', details[1]['data']) is None
|
||||||
|
assert re.search('<Message>', details[1]['data']) is None
|
||||||
|
# MessageType was removed from the payload
|
||||||
|
assert re.search('<MessageType>', details[1]['data']) is None
|
||||||
|
# However we can find our mapped Message to the new value Body
|
||||||
|
assert re.search('<Body>body</Body>', details[1]['data'])
|
||||||
# Custom entry
|
# Custom entry
|
||||||
assert re.search('<Key>value</Key>', details[1]['data'])
|
assert re.search('<Key>value</Key>', details[1]['data'])
|
||||||
|
|
||||||
|
mock_post.reset_mock()
|
||||||
|
mock_get.reset_mock()
|
||||||
|
|
||||||
|
results = NotifyXML.parse_url(
|
||||||
|
'xml://localhost:8081/command?method=POST&:New=Value')
|
||||||
|
|
||||||
|
assert isinstance(results, dict)
|
||||||
|
assert results['user'] is None
|
||||||
|
assert results['password'] is None
|
||||||
|
assert results['port'] == 8081
|
||||||
|
assert results['host'] == 'localhost'
|
||||||
|
assert results['fullpath'] == '/command'
|
||||||
|
assert results['path'] == '/'
|
||||||
|
assert results['query'] == 'command'
|
||||||
|
assert results['schema'] == 'xml'
|
||||||
|
assert results['url'] == 'xml://localhost:8081/command'
|
||||||
|
assert isinstance(results['qsd:'], dict) is True
|
||||||
|
assert results['qsd:']['New'] == 'Value'
|
||||||
|
|
||||||
|
instance = NotifyXML(**results)
|
||||||
|
assert isinstance(instance, NotifyXML)
|
||||||
|
|
||||||
|
# XSD URL is disabled due to custom formatting
|
||||||
|
assert instance.xsd_url is not None
|
||||||
|
|
||||||
|
response = instance.send(title='title', body='body')
|
||||||
|
assert response is True
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
assert mock_get.call_count == 0
|
||||||
|
|
||||||
|
details = mock_post.call_args_list[0]
|
||||||
|
assert details[0][0] == 'http://localhost:8081/command'
|
||||||
|
assert instance.url(privacy=False).startswith(
|
||||||
|
'xml://localhost:8081/command?')
|
||||||
|
|
||||||
|
# Generate a new URL based on our last and verify key values are the same
|
||||||
|
new_results = NotifyXML.parse_url(instance.url(safe=False))
|
||||||
|
for k in ('user', 'password', 'port', 'host', 'fullpath', 'path', 'query',
|
||||||
|
'schema', 'url', 'method'):
|
||||||
|
assert new_results[k] == results[k]
|
||||||
|
|
||||||
|
# Test our data set for our key/value pair
|
||||||
|
assert re.search(r'<Version>[1-9]+\.[0-9]+</Version>', details[1]['data'])
|
||||||
|
assert re.search(r'<MessageType>info</MessageType>', details[1]['data'])
|
||||||
|
assert re.search(r'<Subject>title</Subject>', details[1]['data'])
|
||||||
|
# No over-ride
|
||||||
|
assert re.search(r'<Message>body</Message>', details[1]['data'])
|
||||||
|
# since there is no over-ride, an xmlns:xsi is provided
|
||||||
|
assert re.search(r'<Notification xmlns:xsi=', details[1]['data'])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user