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
This commit is contained in:
Therry van Neerven
2023-04-22 21:00:05 +02:00
committed by GitHub
parent d9a80e7347
commit 885eb9b98a
3 changed files with 55 additions and 9 deletions

View File

@@ -30,6 +30,12 @@ vNext
*Unreleased changes* *Unreleased changes*
Fixes
~~~~~
* **Postmark:** Workaround for handling inbound test webhooks.
(`More info <https://github.com/anymail/django-anymail/issues/304>`__)
Other Other
~~~~~ ~~~~~

View File

@@ -1,8 +1,9 @@
import json import json
import warnings
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from ..exceptions import AnymailConfigurationError from ..exceptions import AnymailConfigurationError, AnymailWarning
from ..inbound import AnymailInboundMessage from ..inbound import AnymailInboundMessage
from ..signals import ( from ..signals import (
AnymailInboundEvent, AnymailInboundEvent,
@@ -169,7 +170,13 @@ class PostmarkInboundWebhookView(PostmarkBaseWebhookView):
attachments = [ attachments = [
AnymailInboundMessage.construct_attachment( AnymailInboundMessage.construct_attachment(
content_type=attachment["ContentType"], content_type=attachment["ContentType"],
content=attachment["Content"], content=(
attachment.get("Content")
# WORKAROUND:
# The test webhooks are not like their real webhooks
# This allows the test webhooks to be parsed.
or attachment["Data"]
),
base64=True, base64=True,
filename=attachment.get("Name", "") or None, filename=attachment.get("Name", "") or None,
content_id=attachment.get("ContentID", "") or None, content_id=attachment.get("ContentID", "") or None,
@@ -177,6 +184,18 @@ class PostmarkInboundWebhookView(PostmarkBaseWebhookView):
for attachment in esp_event.get("Attachments", []) for attachment in esp_event.get("Attachments", [])
] ]
# Warning to the user regarding the workaround of above.
for attachment in esp_event.get("Attachments", []):
if "Data" in attachment:
warnings.warn(
"Received a test webhook attachment. "
"It is recommended to test with real inbound events. "
"See https://github.com/anymail/django-anymail/issues/304 "
"for more information.",
AnymailWarning,
)
break
message = AnymailInboundMessage.construct( message = AnymailInboundMessage.construct(
from_email=self._address(esp_event.get("FromFull")), from_email=self._address(esp_event.get("FromFull")),
to=", ".join([self._address(to) for to in esp_event.get("ToFull", [])]), to=", ".join([self._address(to) for to in esp_event.get("ToFull", [])]),

View File

@@ -4,7 +4,7 @@ from unittest.mock import ANY
from django.test import tag from django.test import tag
from anymail.exceptions import AnymailConfigurationError from anymail.exceptions import AnymailConfigurationError, AnymailWarning
from anymail.inbound import AnymailInboundMessage from anymail.inbound import AnymailInboundMessage
from anymail.signals import AnymailInboundEvent from anymail.signals import AnymailInboundEvent
from anymail.webhooks.postmark import PostmarkInboundWebhookView from anymail.webhooks.postmark import PostmarkInboundWebhookView
@@ -165,14 +165,27 @@ class PostmarkInboundTestCase(WebhookTestCase):
"ContentType": 'message/rfc822; charset="us-ascii"', "ContentType": 'message/rfc822; charset="us-ascii"',
"ContentLength": len(email_content), "ContentLength": len(email_content),
}, },
# This is an attachement like send by the test webhook
# A workaround is implemented to handle it.
# Once Postmark solves the bug on their side this workaround
# can be reverted.
{
"Name": "test.txt",
"ContentType": "text/plain",
"Data": "VGhpcyBpcyBhdHRhY2htZW50IGNvbnRlbnRzLCBiYXNlLTY0IGVuY29kZWQu",
"ContentLength": 45,
},
] ]
} }
response = self.client.post( with self.assertWarnsRegex(
"/anymail/postmark/inbound/", AnymailWarning, r"Received a test webhook attachment. "
content_type="application/json", ):
data=json.dumps(raw_event), response = self.client.post(
) "/anymail/postmark/inbound/",
content_type="application/json",
data=json.dumps(raw_event),
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with( kwargs = self.assert_handler_called_once_with(
self.inbound_handler, self.inbound_handler,
@@ -183,7 +196,7 @@ class PostmarkInboundTestCase(WebhookTestCase):
event = kwargs["event"] event = kwargs["event"]
message = event.message message = event.message
attachments = message.attachments # AnymailInboundMessage convenience accessor attachments = message.attachments # AnymailInboundMessage convenience accessor
self.assertEqual(len(attachments), 2) self.assertEqual(len(attachments), 3)
self.assertEqual(attachments[0].get_filename(), "test.txt") self.assertEqual(attachments[0].get_filename(), "test.txt")
self.assertEqual(attachments[0].get_content_type(), "text/plain") self.assertEqual(attachments[0].get_content_type(), "text/plain")
self.assertEqual(attachments[0].get_content_text(), "test attachment") self.assertEqual(attachments[0].get_content_text(), "test attachment")
@@ -192,6 +205,14 @@ class PostmarkInboundTestCase(WebhookTestCase):
attachments[1].get_content_bytes(), email_content attachments[1].get_content_bytes(), email_content
) )
# Attachment of test webhook
self.assertEqual(attachments[2].get_filename(), "test.txt")
self.assertEqual(attachments[2].get_content_type(), "text/plain")
self.assertEqual(
attachments[2].get_content_text(),
"This is attachment contents, base-64 encoded.",
)
inlines = message.inline_attachments inlines = message.inline_attachments
self.assertEqual(len(inlines), 1) self.assertEqual(len(inlines), 1)
inline = inlines["abc123"] inline = inlines["abc123"]