Skip to content

Commit bb25715

Browse files
costelamedmunds
authored andcommitted
Mailgun: treat temporary failure as deferred in tracking webhook
Map Mailgun severity: temporary failure event to Anymail "deferred" event, to distinguish it from severity: permanent failures which will show up as Anymail "bounced". Also remap Mailgun reason: generic failure to Anymail "other" reject reason (rather than "bounced"). Closes #130
1 parent 56c11ce commit bb25715

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

anymail/webhooks/mailgun.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,20 @@ def parse_events(self, request):
8787
# (these appear in webhook doc examples, but aren't actually documented anywhere)
8888
"bounce": RejectReason.BOUNCED,
8989
"suppress-bounce": RejectReason.BOUNCED,
90-
"generic": RejectReason.BOUNCED, # ??? appears to be used for any temporary failure?
90+
"generic": RejectReason.OTHER, # ??? appears to be used for any temporary failure?
91+
}
92+
93+
severities = {
94+
# Remap some event types based on "severity" payload field
95+
(EventType.BOUNCED, 'temporary'): EventType.DEFERRED
9196
}
9297

9398
def esp_to_anymail_event(self, esp_event):
9499
event_data = esp_event.get('event-data', {})
95100

96-
try:
97-
event_type = self.event_types[event_data['event']]
98-
except KeyError:
99-
event_type = EventType.UNKNOWN
101+
event_type = self.event_types.get(event_data['event'], EventType.UNKNOWN)
102+
103+
event_type = self.severities.get((EventType.BOUNCED, event_data.get('severity')), event_type)
100104

101105
# Use signature.token for event_id, rather than event_data.id,
102106
# because the latter is only "guaranteed to be unique within a day".

tests/test_mailgun_webhooks.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,52 @@ def test_failed_temporary_event(self):
251251
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
252252
event=ANY, esp_name='Mailgun')
253253
event = kwargs['event']
254-
self.assertEqual(event.event_type, "bounced")
254+
self.assertEqual(event.event_type, "deferred")
255255
self.assertEqual(event.recipient, "[email protected]")
256-
self.assertEqual(event.reject_reason, "bounced")
256+
self.assertEqual(event.reject_reason, "other")
257257
self.assertEqual(event.description, "No MX for nomx.example.com")
258258
self.assertEqual(event.mta_response, "No MX for nomx.example.com")
259259

260+
def test_failed_greylisted_event(self):
261+
raw_event = mailgun_sign_payload({
262+
"event-data": {
263+
"event": "failed",
264+
"severity": "temporary",
265+
"reason": "greylisted",
266+
"timestamp": 1534111899.659519,
267+
"log-level": "warn",
268+
"message": {
269+
"headers": {
270+
271+
"message-id": "[email protected]",
272+
"from": "Test Sender ",
273+
"subject": "Testing"
274+
},
275+
},
276+
"recipient": "[email protected]",
277+
"delivery-status": {
278+
"mx-host": "mx.example.com",
279+
"attempt-no": 1,
280+
"description": "Recipient address rejected: Greylisted",
281+
"session-seconds": 0.0,
282+
"retry-seconds": 300,
283+
"code": 450,
284+
"message": "Recipient address rejected: Greylisted"
285+
}
286+
},
287+
})
288+
response = self.client.post('/anymail/mailgun/tracking/',
289+
data=json.dumps(raw_event), content_type='application/json')
290+
self.assertEqual(response.status_code, 200)
291+
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
292+
event=ANY, esp_name='Mailgun')
293+
event = kwargs['event']
294+
self.assertEqual(event.event_type, "deferred")
295+
self.assertEqual(event.recipient, "[email protected]")
296+
self.assertEqual(event.reject_reason, "other")
297+
self.assertEqual(event.description, "Recipient address rejected: Greylisted")
298+
self.assertEqual(event.mta_response, "Recipient address rejected: Greylisted")
299+
260300
def test_rejected_event(self):
261301
# (The "rejected" event is documented and appears in Mailgun dashboard logs,
262302
# but it doesn't appear to be delivered through webhooks as of 8/2018.)

0 commit comments

Comments
 (0)