Skip to content

Commit

Permalink
Postmark: Handle responses with missing ErrorCode and "delivery may b…
Browse files Browse the repository at this point in the history
…e delayed".

Fixes #392.
  • Loading branch information
medmunds committed Dec 10, 2024
1 parent 8def0bd commit 77b9701
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,16 @@ Fixes
* **Mailjet:** Avoid a Mailjet API error when sending an inline image without a
filename. (Anymail now substitutes ``"attachment"`` for the missing filename.)
(Thanks to `@chickahoona`_ for reporting the issue.)

* **Mailjet:** Fix a JSON parsing error on Mailjet 429 "too many requests" API
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
-----
Expand Down
12 changes: 9 additions & 3 deletions anymail/backends/postmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def parse_recipient_status(self, response, payload, message):
for one_response in parsed_response:
try:
# 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"]
except (KeyError, TypeError) as err:
raise AnymailRequestsAPIError(
Expand All @@ -88,6 +89,11 @@ def parse_recipient_status(self, response, payload, message):
backend=self,
) 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.
# (Must use "To" from API response to get correct individual MessageIDs
# in batch send.)
Expand All @@ -98,15 +104,15 @@ def parse_recipient_status(self, response, payload, message):
else:
for to in parse_address_list(to_header):
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
# below. (Postmark doesn't report "Cc" or "Bcc" in API response; use
# original payload values.)
for recip in payload.cc_and_bcc_emails:
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
Expand Down
16 changes: 16 additions & 0 deletions tests/test_postmark_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,22 @@ def test_mixed_response(self):
self.assertEqual(status.recipients["[email protected]"].status, "sent")
self.assertEqual(status.recipients["[email protected]"].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": "[email protected]",
}
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["[email protected]"].status, "queued")
self.assertEqual(status.message_id, "38360f97-ff7f-44b2-bcd1-5ea94ff2af00")


@tag("postmark")
class PostmarkBackendSessionSharingTestCase(
Expand Down

0 comments on commit 77b9701

Please sign in to comment.