Postmark: Handle responses with missing ErrorCode and "delivery may be delayed".

Fixes #392.
This commit is contained in:
Mike Edmunds
2024-12-10 13:01:18 -08:00
parent 8def0bdc06
commit 77b9701b5e
3 changed files with 32 additions and 3 deletions

View File

@@ -43,9 +43,16 @@ Fixes
* **Mailjet:** Avoid a Mailjet API error when sending an inline image without a * **Mailjet:** Avoid a Mailjet API error when sending an inline image without a
filename. (Anymail now substitutes ``"attachment"`` for the missing filename.) filename. (Anymail now substitutes ``"attachment"`` for the missing filename.)
(Thanks to `@chickahoona`_ for reporting the issue.) (Thanks to `@chickahoona`_ for reporting the issue.)
* **Mailjet:** Fix a JSON parsing error on Mailjet 429 "too many requests" API * **Mailjet:** Fix a JSON parsing error on Mailjet 429 "too many requests" API
responses. (Thanks to `@rodrigondec`_ for reporting the issue.) responses. (Thanks to `@rodrigondec`_ for reporting the issue.)
* **Postmark:** Fix a parsing error when Postmark indicates a sent message has
been delayed, which can occur if your message stream is paused or throttled or
when Postmark is experiencing service issues. These messages will now report
"queued" in the ``anymail_status`` (rather than throwing an error or reporting
"sent"). (Thanks to `@jmduke`_ for reporting the issue.)
v12.0 v12.0
----- -----

View File

@@ -64,7 +64,8 @@ class EmailBackend(AnymailRequestsBackend):
for one_response in parsed_response: for one_response in parsed_response:
try: try:
# these fields should always be present # these fields should always be present
error_code = one_response["ErrorCode"] # (but ErrorCode may be missing for Postmark service delays)
error_code = one_response.get("ErrorCode", 0)
msg = one_response["Message"] msg = one_response["Message"]
except (KeyError, TypeError) as err: except (KeyError, TypeError) as err:
raise AnymailRequestsAPIError( raise AnymailRequestsAPIError(
@@ -88,6 +89,11 @@ class EmailBackend(AnymailRequestsBackend):
backend=self, backend=self,
) from err ) from err
status = "sent"
# "Message accepted, but delivery may be delayed." (See #392.)
if "delivery may be delayed" in msg:
status = "queued"
# Assume all To recipients are "sent" unless proven otherwise below. # Assume all To recipients are "sent" unless proven otherwise below.
# (Must use "To" from API response to get correct individual MessageIDs # (Must use "To" from API response to get correct individual MessageIDs
# in batch send.) # in batch send.)
@@ -98,7 +104,7 @@ class EmailBackend(AnymailRequestsBackend):
else: else:
for to in parse_address_list(to_header): for to in parse_address_list(to_header):
recipient_status[to.addr_spec] = AnymailRecipientStatus( recipient_status[to.addr_spec] = AnymailRecipientStatus(
message_id=message_id, status="sent" message_id=message_id, status=status
) )
# Assume all Cc and Bcc recipients are "sent" unless proven otherwise # Assume all Cc and Bcc recipients are "sent" unless proven otherwise
@@ -106,7 +112,7 @@ class EmailBackend(AnymailRequestsBackend):
# original payload values.) # original payload values.)
for recip in payload.cc_and_bcc_emails: for recip in payload.cc_and_bcc_emails:
recipient_status[recip.addr_spec] = AnymailRecipientStatus( recipient_status[recip.addr_spec] = AnymailRecipientStatus(
message_id=message_id, status="sent" message_id=message_id, status=status
) )
# Change "sent" to "rejected" if Postmark reported an address as # Change "sent" to "rejected" if Postmark reported an address as

View File

@@ -1079,6 +1079,22 @@ class PostmarkBackendRecipientsRefusedTests(PostmarkBackendMockAPITestCase):
self.assertEqual(status.recipients["valid@example.com"].status, "sent") self.assertEqual(status.recipients["valid@example.com"].status, "sent")
self.assertEqual(status.recipients["spam@example.com"].status, "rejected") self.assertEqual(status.recipients["spam@example.com"].status, "rejected")
def test_delivery_delayed_response(self):
# Postmark's "delivery may be delayed" response doesn't have an ErrorCode
# field set to 0 (despite their API docs).
response_data = {
"Message": "Message accepted, but delivery may be delayed.",
"MessageID": "38360f97-ff7f-44b2-bcd1-5ea94ff2af00",
"SubmittedAt": "2024-08-05T02:03:37.0951168Z",
"To": "to@example.com",
}
self.set_mock_response(json_data=response_data)
sent = self.message.send()
self.assertEqual(sent, 1)
status = self.message.anymail_status
self.assertEqual(status.recipients["to@example.com"].status, "queued")
self.assertEqual(status.message_id, "38360f97-ff7f-44b2-bcd1-5ea94ff2af00")
@tag("postmark") @tag("postmark")
class PostmarkBackendSessionSharingTestCase( class PostmarkBackendSessionSharingTestCase(