Skip to content

Commit

Permalink
[Pushsafer] Fix to prevent submitting empty parameters to upstream API
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This has an impact on the ``vibration`` parameter:

Previously, a Pushsafer notification would always have vibrations turned
on, which is considered to be a bug. Now, you will have to explicitly
configure `vibration=0`, in order to use the "device-default" vibration.
  • Loading branch information
amotl committed Mar 12, 2023
1 parent 31100ad commit ed84c69
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 5 deletions.
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ mqttwarn changelog
in progress
===========

- Pushsafer: Fix to prevent submitting empty parameters to upstream API.

BREAKING CHANGE: This has an impact on the ``vibration`` parameter:
Previously, a Pushsafer notification would always have vibrations turned
on, which is considered to be a bug. Now, you will have to explicitly
configure ``vibration=0``, in order to use the "device-default" vibration.


2023-02-13 0.32.0
=================
Expand Down
28 changes: 27 additions & 1 deletion mqttwarn/services/pushsafer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def pushsafer(**kwargs):
if not kwargs['k']:
kwargs['k'] = os.environ['PUSHSAFER_TOKEN']

# Don't submit empty parameters to Pushsafer.
kwargs = filter_empty_parameters(kwargs)

url = urllib.parse.urljoin(PUSHSAFER_API, "api")
data = urllib.parse.urlencode(kwargs).encode('utf-8')
req = urllib.request.Request(url, data)
Expand All @@ -47,6 +50,24 @@ def pushsafer(**kwargs):
raise PushsaferError(output)


def filter_empty_parameters(params: t.Dict[str, str]):
"""
Filter empty parameters.
Also, translate special value `vibration=0` into empty value,
because this currently signals "vibrate with device-default".
"""
effective_parameters = OrderedDict()
for key, value in params.items():
if value != "":
# Special handling for `vibration=0`.
# TODO: Remove after eventual upstream adjustments.
if key == "v" and str(value) == "0":
value = ""
effective_parameters[key] = value
return effective_parameters


def plugin(srv, item):

srv.logging.debug("*** MODULE=%s: service=%s, target=%s", __file__, item.service, item.target)
Expand Down Expand Up @@ -132,7 +153,12 @@ def encode_v1(self):
params['s'] = addrs[3]

if len(addrs) > 4:
params['v'] = addrs[4]
# Special handling for `vibration`.
# With the v1 configuration layout, it should not always trigger a vibration when left empty.
# Therefore, a synthetic value `0` can be used to configure "device-default" vibration.
value = str(addrs[4])
if value != "":
params['v'] = value

if len(addrs) > 5:
params['u'] = addrs[5]
Expand Down
3 changes: 2 additions & 1 deletion tests/etc/better-addresses.ini
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ targets = {
'device': '52|65|78',
'icon': 64,
'sound': 2,
'vibration': 'blank', # ?
# Zero means "vibrate with device-default".
'vibration': '0',
'url': 'http://example.org',
'url_title': 'Example Org',
'time_to_live': 60,
Expand Down
3 changes: 2 additions & 1 deletion tests/services/pushsafer/test_pushsafer_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class IoTestItem:
IoTestItem(id="device-id", in_addrs=[TEST_TOKEN, "52|65|78"], out_data={"d": "52|65|78"}),
IoTestItem(id="icon", in_addrs=[TEST_TOKEN, "", "test.ico"], out_data={"i": "test.ico"}),
IoTestItem(id="sound", in_addrs=[TEST_TOKEN, "", "", "test.mp3"], out_data={"s": "test.mp3"}),
IoTestItem(id="vibration", in_addrs=[TEST_TOKEN, "", "", "", "1"], out_data={"v": "1"}),
IoTestItem(id="vibration-default", in_addrs=[TEST_TOKEN, "", "", "", "0"], out_data={"v": ""}),
IoTestItem(id="vibration-two", in_addrs=[TEST_TOKEN, "", "", "", "2"], out_data={"v": "2"}),
IoTestItem(
id="url", in_addrs=[TEST_TOKEN, "", "", "", "", "http://example.org"], out_data={"u": "http://example.org"}
),
Expand Down
3 changes: 2 additions & 1 deletion tests/services/pushsafer/test_pushsafer_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class IoTestItem:
IoTestItem(id="device-id", in_addrs={"private_key": TEST_TOKEN, "device": "52|65|78"}, out_data={"d": "52|65|78"}),
IoTestItem(id="icon", in_addrs={"private_key": TEST_TOKEN, "icon": "test.ico"}, out_data={"i": "test.ico"}),
IoTestItem(id="sound", in_addrs={"private_key": TEST_TOKEN, "sound": "test.mp3"}, out_data={"s": "test.mp3"}),
IoTestItem(id="vibration", in_addrs={"private_key": TEST_TOKEN, "vibration": 1}, out_data={"v": "1"}),
IoTestItem(id="vibration-default", in_addrs={"private_key": TEST_TOKEN, "vibration": 0}, out_data={"v": ""}),
IoTestItem(id="vibration-two", in_addrs={"private_key": TEST_TOKEN, "vibration": 2}, out_data={"v": "2"}),
IoTestItem(
id="url",
in_addrs={"private_key": TEST_TOKEN, "url": "http://example.org"},
Expand Down
2 changes: 1 addition & 1 deletion tests/services/pushsafer/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ def get_reference_data(**more_data):

def assert_request(request: urllib.request.Request, reference_data: t.Dict[str, str]):
assert request.full_url == "https://www.pushsafer.com/api"
actual_data = dict(parse_qsl(request.data.decode("utf-8")))
actual_data = dict(parse_qsl(request.data.decode("utf-8"), keep_blank_values=True))
msg = f"\nGot: {actual_data}\nExpected: {reference_data}"
assert actual_data == reference_data, msg

0 comments on commit ed84c69

Please sign in to comment.