mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Get rid of magic JSON serialization for Mailgun metadata.
Treat Mailgun metadata like all other ESPs: simple key-value dict, where values are strings. If you want to store JSON in metadata, you should serialize and deserialize it yourself.
This commit is contained in:
@@ -122,18 +122,8 @@ class MailgunPayload(RequestsPayload):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def set_metadata(self, metadata):
|
def set_metadata(self, metadata):
|
||||||
# The Mailgun docs are a little unclear on whether to send each var as a separate v: field,
|
|
||||||
# or to send a single 'v:my-custom-data' field with a json blob of all vars.
|
|
||||||
# (https://documentation.mailgun.com/user_manual.html#attaching-data-to-messages)
|
|
||||||
# From experimentation, it seems like the first option works:
|
|
||||||
for key, value in metadata.items():
|
for key, value in metadata.items():
|
||||||
# Ensure the value is json-serializable (for Mailgun storage)
|
self.data["v:%s" % key] = value
|
||||||
json = self.serialize_json(value) # will raise AnymailSerializationError
|
|
||||||
# Special case: a single string value should be sent bare (without quotes),
|
|
||||||
# because Mailgun will add quotes when querying the value as json.
|
|
||||||
if json.startswith('"'): # only a single string could be serialized as "...
|
|
||||||
json = value
|
|
||||||
self.data["v:%s" % key] = json
|
|
||||||
|
|
||||||
def set_send_at(self, send_at):
|
def set_send_at(self, send_at):
|
||||||
# Mailgun expects RFC-2822 format dates
|
# Mailgun expects RFC-2822 format dates
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from decimal import Decimal
|
|
||||||
from email.mime.base import MIMEBase
|
from email.mime.base import MIMEBase
|
||||||
from email.mime.image import MIMEImage
|
from email.mime.image import MIMEImage
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ from django.test import SimpleTestCase
|
|||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError, AnymailSerializationError, AnymailUnsupportedFeature
|
from anymail.exceptions import AnymailAPIError, AnymailUnsupportedFeature
|
||||||
from anymail.message import attach_inline_image_file
|
from anymail.message import attach_inline_image_file
|
||||||
|
|
||||||
from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharingTestCasesMixin
|
from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharingTestCasesMixin
|
||||||
@@ -270,12 +269,13 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
|||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
def test_metadata(self):
|
def test_metadata(self):
|
||||||
self.message.metadata = {'user_id': "12345", 'items': ['mail', 'gun']}
|
# Each metadata value is just a string; you can serialize your own JSON if you'd like.
|
||||||
|
# (The Mailgun docs are a little confusing on this point.)
|
||||||
|
self.message.metadata = {'user_id': "12345", 'items': '["mail","gun"]'}
|
||||||
self.message.send()
|
self.message.send()
|
||||||
data = self.get_api_call_data()
|
data = self.get_api_call_data()
|
||||||
# note values get serialized to json:
|
self.assertEqual(data['v:user_id'], '12345')
|
||||||
self.assertEqual(data['v:user_id'], '12345') # simple values are transmitted as-is
|
self.assertEqual(data['v:items'], '["mail","gun"]')
|
||||||
self.assertEqual(data['v:items'], '["mail", "gun"]') # complex values get json-serialized
|
|
||||||
|
|
||||||
def test_send_at(self):
|
def test_send_at(self):
|
||||||
utc_plus_6 = get_fixed_timezone(6 * 60)
|
utc_plus_6 = get_fixed_timezone(6 * 60)
|
||||||
@@ -401,16 +401,8 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
|||||||
self.assertEqual(self.message.anymail_status.recipients, {})
|
self.assertEqual(self.message.anymail_status.recipients, {})
|
||||||
self.assertEqual(self.message.anymail_status.esp_response, mock_response)
|
self.assertEqual(self.message.anymail_status.esp_response, mock_response)
|
||||||
|
|
||||||
def test_json_serialization_errors(self):
|
# test_json_serialization_errors: Mailgun payload isn't JSON, so we don't test this.
|
||||||
"""Try to provide more information about non-json-serializable data"""
|
# (Anything that requests can serialize as a form field will work with Mailgun)
|
||||||
self.message.metadata = {'total': Decimal('19.99')}
|
|
||||||
with self.assertRaises(AnymailSerializationError) as cm:
|
|
||||||
self.message.send()
|
|
||||||
print(self.get_api_call_data())
|
|
||||||
err = cm.exception
|
|
||||||
self.assertIsInstance(err, TypeError) # compatibility with json.dumps
|
|
||||||
self.assertIn("Don't know how to send this data to Mailgun", str(err)) # our added context
|
|
||||||
self.assertIn("Decimal('19.99') is not JSON serializable", str(err)) # original message
|
|
||||||
|
|
||||||
|
|
||||||
class MailgunBackendRecipientsRefusedTests(MailgunBackendMockAPITestCase):
|
class MailgunBackendRecipientsRefusedTests(MailgunBackendMockAPITestCase):
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
|||||||
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
|
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
|
||||||
headers={"X-Anymail-Test": "value"},
|
headers={"X-Anymail-Test": "value"},
|
||||||
|
|
||||||
metadata={"meta1": "simple string", "meta2": 2, "meta3": {"complex": "value"}},
|
metadata={"meta1": "simple string", "meta2": 2},
|
||||||
send_at=send_at,
|
send_at=send_at,
|
||||||
tags=["tag 1", "tag 2"],
|
tags=["tag 1", "tag 2"],
|
||||||
track_clicks=False,
|
track_clicks=False,
|
||||||
@@ -136,9 +136,7 @@ class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
|||||||
event = events.pop()
|
event = events.pop()
|
||||||
self.assertCountEqual(event["tags"], ["tag 1", "tag 2"]) # don't care about order
|
self.assertCountEqual(event["tags"], ["tag 1", "tag 2"]) # don't care about order
|
||||||
self.assertEqual(event["user-variables"],
|
self.assertEqual(event["user-variables"],
|
||||||
{"meta1": "simple string",
|
{"meta1": "simple string", "meta2": "2"}) # all metadata values become strings
|
||||||
"meta2": "2", # numbers become strings
|
|
||||||
"meta3": '{"complex": "value"}'}) # complex values become json
|
|
||||||
|
|
||||||
self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
|
self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
|
||||||
self.assertCountEqual(event["message"]["recipients"],
|
self.assertCountEqual(event["message"]["recipients"],
|
||||||
|
|||||||
Reference in New Issue
Block a user