Support Postmark delivery event webhook.

Add support for Postmark's recently-released [delivery tracking webhook] to Anymail's normailized status event handling. The existing Anymail tracking webhook URL can be copied to "Delivery webhook" in your Postmark outbound server settings.

Closes #45.
This commit is contained in:
medmunds
2016-12-15 14:23:44 -08:00
parent d0596d100b
commit 52596394cc
3 changed files with 33 additions and 6 deletions

View File

@@ -62,6 +62,8 @@ class PostmarkTrackingWebhookView(PostmarkBaseWebhookView):
except KeyError: except KeyError:
if 'FirstOpen' in esp_event: if 'FirstOpen' in esp_event:
event_type = EventType.OPENED event_type = EventType.OPENED
elif 'DeliveredAt' in esp_event:
event_type = EventType.DELIVERED
elif 'From' in esp_event: elif 'From' in esp_event:
# This is an inbound event # This is an inbound event
raise AnymailConfigurationError( raise AnymailConfigurationError(
@@ -73,7 +75,7 @@ class PostmarkTrackingWebhookView(PostmarkBaseWebhookView):
recipient = getfirst(esp_event, ['Email', 'Recipient'], None) # Email for bounce; Recipient for open recipient = getfirst(esp_event, ['Email', 'Recipient'], None) # Email for bounce; Recipient for open
try: try:
timestr = getfirst(esp_event, ['BouncedAt', 'ReceivedAt']) timestr = getfirst(esp_event, ['DeliveredAt', 'BouncedAt', 'ReceivedAt'])
except KeyError: except KeyError:
timestamp = None timestamp = None
else: else:

View File

@@ -177,8 +177,8 @@ Status tracking webhooks
If you are using Anymail's normalized :ref:`status tracking <event-tracking>`, enter If you are using Anymail's normalized :ref:`status tracking <event-tracking>`, enter
the url in your `Postmark account settings`_, under Servers > *your server name* > the url in your `Postmark account settings`_, under Servers > *your server name* >
Settings > Outbound > Webhooks. You should enter this same Anymail tracking URL Settings > Outbound > Webhooks. You should enter this same Anymail tracking URL
for both the "Bounce webhook" and "Opens webhook" (if you want to receive both for all of the "Delivery webhook," "Bounce webhook," and "Opens webhook" (if you
types of events): want to receive all these types of events):
:samp:`https://{random}:{random}@{yoursite.example.com}/anymail/postmark/tracking/` :samp:`https://{random}:{random}@{yoursite.example.com}/anymail/postmark/tracking/`
@@ -192,11 +192,12 @@ If you use multiple Postmark servers, you'll need to repeat entering the webhook
settings for each of them. settings for each of them.
Postmark will report these Anymail :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s: Postmark will report these Anymail :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s:
rejected, failed, bounced, deferred, autoresponded, opened, complained, unsubscribed, subscribed. rejected, failed, bounced, deferred, delivered, autoresponded, opened, complained,
(Postmark does not support sent, delivered, or clicked events.) unsubscribed, subscribed. (Postmark does not support sent or clicked events.)
The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be
a `dict` of Postmark `bounce <http://developer.postmarkapp.com/developer-bounce-webhook.html>`_ a `dict` of Postmark `delivery <http://developer.postmarkapp.com/developer-delivery-webhook.html>`_,
`bounce <http://developer.postmarkapp.com/developer-bounce-webhook.html>`_,
or `open <http://developer.postmarkapp.com/developer-open-webhook.html>`_ webhook data. or `open <http://developer.postmarkapp.com/developer-open-webhook.html>`_ webhook data.
.. _Postmark account settings: https://account.postmarkapp.com/servers .. _Postmark account settings: https://account.postmarkapp.com/servers

View File

@@ -55,6 +55,30 @@ class PostmarkDeliveryTestCase(WebhookTestCase):
self.assertEqual(event.mta_response, self.assertEqual(event.mta_response,
"smtp;550 5.1.1 The email account that you tried to reach does not exist.") "smtp;550 5.1.1 The email account that you tried to reach does not exist.")
def test_delivered_event(self):
raw_event = {
"ServerId": 23,
"MessageID": "883953f4-6105-42a2-a16a-77a8eac79483",
"Recipient": "recipient@example.com",
"Tag": "welcome-email",
"DeliveredAt": "2014-08-01T13:28:10.2735393-04:00",
"Details": "Test delivery webhook details"
}
response = self.client.post('/anymail/postmark/tracking/',
content_type='application/json', data=json.dumps(raw_event))
self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=PostmarkTrackingWebhookView,
event=ANY, esp_name='Postmark')
event = kwargs['event']
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "delivered")
self.assertEqual(event.esp_event, raw_event)
self.assertEqual(event.timestamp, datetime(2014, 8, 1, 13, 28, 10, microsecond=273539,
tzinfo=get_fixed_timezone(-4*60)))
self.assertEqual(event.message_id, "883953f4-6105-42a2-a16a-77a8eac79483")
self.assertEqual(event.recipient, "recipient@example.com")
self.assertEqual(event.tags, ["welcome-email"])
def test_open_event(self): def test_open_event(self):
raw_event = { raw_event = {
"FirstOpen": True, "FirstOpen": True,