From 1580e21a7bd569cfe5ce7aa92983d915e843be22 Mon Sep 17 00:00:00 2001 From: jrconlin Date: Fri, 19 May 2017 17:08:46 -0700 Subject: [PATCH] bug: Serialize Decimal correctly for ios aps closes #899 --- autopush/router/apns2.py | 13 ++++++++++++- autopush/tests/test_router.py | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/autopush/router/apns2.py b/autopush/router/apns2.py index 45ba64ec..45f8db35 100644 --- a/autopush/router/apns2.py +++ b/autopush/router/apns2.py @@ -1,5 +1,6 @@ import json from collections import deque +from decimal import Decimal import hyper.tls from hyper import HTTP20Connection @@ -21,6 +22,16 @@ APNS_PRIORITY_LOW = '5' +class ComplexEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, Decimal): + return int(obj.to_integral_value()) + # for most data types, this function isn't called. + # the following is added for safety, but should not + # be required. + return json.JSONEncoder.default(self, obj) # pragma nocover + + class APNSException(Exception): pass @@ -104,7 +115,7 @@ def send(self, router_token, payload, apns_id, :type exp: timestamp """ - body = json.dumps(payload) + body = json.dumps(payload, cls=ComplexEncoder) priority = APNS_PRIORITY_IMMEDIATE if priority else APNS_PRIORITY_LOW # NOTE: Hyper requires that all header values be strings. 'Priority' # is a integer string, which may be "simplified" and cause an error. diff --git a/autopush/tests/test_router.py b/autopush/tests/test_router.py index e7b13680..355c08c8 100644 --- a/autopush/tests/test_router.py +++ b/autopush/tests/test_router.py @@ -3,6 +3,7 @@ import uuid import time import json +import decimal from autopush.utils import WebPushNotification from mock import Mock, PropertyMock, patch @@ -195,6 +196,25 @@ def test_route_notification(self): "con": "aesgcm", }) + @inlineCallbacks + def test_route_notification_complex(self): + router_data = dict( + router_data=dict(token="connect_data", + rel_channel="firefox", + aps=dict(string="String", + array=['a', 'b', 'c'], + number=decimal.Decimal(4)))) + result = yield self.router.route_notification(self.notif, + router_data) + yield self._waitfor(lambda: + self.mock_connection.request.called is True) + ok_(isinstance(result, RouterResponse)) + ok_(self.mock_connection.request.called) + body = self.mock_connection.request.call_args[1] + body_json = json.loads(body['body']) + eq_(body_json['aps']['number'], 4) + eq_(body_json['aps']['string'], 'String') + @inlineCallbacks def test_route_low_priority_notification(self): """low priority and empty apns_ids are not yet used, but may feature