Skip to content

Commit 885eb9b

Browse files
authored
Postmark: workaround invalid "test inbound" data
Postmark's "test" button in their inbound settings posts data with attachments that don't match their docs or actual inbound behavior. Accept that and issue a warning. Closes #304
1 parent d9a80e7 commit 885eb9b

File tree

3 files changed

+55
-9
lines changed

3 files changed

+55
-9
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ vNext
3030

3131
*Unreleased changes*
3232

33+
Fixes
34+
~~~~~
35+
36+
* **Postmark:** Workaround for handling inbound test webhooks.
37+
(`More info <https://github.com/anymail/django-anymail/issues/304>`__)
38+
3339
Other
3440
~~~~~
3541

anymail/webhooks/postmark.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
2+
import warnings
23

34
from django.utils.dateparse import parse_datetime
45

5-
from ..exceptions import AnymailConfigurationError
6+
from ..exceptions import AnymailConfigurationError, AnymailWarning
67
from ..inbound import AnymailInboundMessage
78
from ..signals import (
89
AnymailInboundEvent,
@@ -169,14 +170,32 @@ def esp_to_anymail_event(self, esp_event):
169170
attachments = [
170171
AnymailInboundMessage.construct_attachment(
171172
content_type=attachment["ContentType"],
172-
content=attachment["Content"],
173+
content=(
174+
attachment.get("Content")
175+
# WORKAROUND:
176+
# The test webhooks are not like their real webhooks
177+
# This allows the test webhooks to be parsed.
178+
or attachment["Data"]
179+
),
173180
base64=True,
174181
filename=attachment.get("Name", "") or None,
175182
content_id=attachment.get("ContentID", "") or None,
176183
)
177184
for attachment in esp_event.get("Attachments", [])
178185
]
179186

187+
# Warning to the user regarding the workaround of above.
188+
for attachment in esp_event.get("Attachments", []):
189+
if "Data" in attachment:
190+
warnings.warn(
191+
"Received a test webhook attachment. "
192+
"It is recommended to test with real inbound events. "
193+
"See https://github.com/anymail/django-anymail/issues/304 "
194+
"for more information.",
195+
AnymailWarning,
196+
)
197+
break
198+
180199
message = AnymailInboundMessage.construct(
181200
from_email=self._address(esp_event.get("FromFull")),
182201
to=", ".join([self._address(to) for to in esp_event.get("ToFull", [])]),

tests/test_postmark_inbound.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from django.test import tag
66

7-
from anymail.exceptions import AnymailConfigurationError
7+
from anymail.exceptions import AnymailConfigurationError, AnymailWarning
88
from anymail.inbound import AnymailInboundMessage
99
from anymail.signals import AnymailInboundEvent
1010
from anymail.webhooks.postmark import PostmarkInboundWebhookView
@@ -165,14 +165,27 @@ def test_attachments(self):
165165
"ContentType": 'message/rfc822; charset="us-ascii"',
166166
"ContentLength": len(email_content),
167167
},
168+
# This is an attachement like send by the test webhook
169+
# A workaround is implemented to handle it.
170+
# Once Postmark solves the bug on their side this workaround
171+
# can be reverted.
172+
{
173+
"Name": "test.txt",
174+
"ContentType": "text/plain",
175+
"Data": "VGhpcyBpcyBhdHRhY2htZW50IGNvbnRlbnRzLCBiYXNlLTY0IGVuY29kZWQu",
176+
"ContentLength": 45,
177+
},
168178
]
169179
}
170180

171-
response = self.client.post(
172-
"/anymail/postmark/inbound/",
173-
content_type="application/json",
174-
data=json.dumps(raw_event),
175-
)
181+
with self.assertWarnsRegex(
182+
AnymailWarning, r"Received a test webhook attachment. "
183+
):
184+
response = self.client.post(
185+
"/anymail/postmark/inbound/",
186+
content_type="application/json",
187+
data=json.dumps(raw_event),
188+
)
176189
self.assertEqual(response.status_code, 200)
177190
kwargs = self.assert_handler_called_once_with(
178191
self.inbound_handler,
@@ -183,7 +196,7 @@ def test_attachments(self):
183196
event = kwargs["event"]
184197
message = event.message
185198
attachments = message.attachments # AnymailInboundMessage convenience accessor
186-
self.assertEqual(len(attachments), 2)
199+
self.assertEqual(len(attachments), 3)
187200
self.assertEqual(attachments[0].get_filename(), "test.txt")
188201
self.assertEqual(attachments[0].get_content_type(), "text/plain")
189202
self.assertEqual(attachments[0].get_content_text(), "test attachment")
@@ -192,6 +205,14 @@ def test_attachments(self):
192205
attachments[1].get_content_bytes(), email_content
193206
)
194207

208+
# Attachment of test webhook
209+
self.assertEqual(attachments[2].get_filename(), "test.txt")
210+
self.assertEqual(attachments[2].get_content_type(), "text/plain")
211+
self.assertEqual(
212+
attachments[2].get_content_text(),
213+
"This is attachment contents, base-64 encoded.",
214+
)
215+
195216
inlines = message.inline_attachments
196217
self.assertEqual(len(inlines), 1)
197218
inline = inlines["abc123"]

0 commit comments

Comments
 (0)