Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postmark: Handle responses with missing ErrorCode and delayed delivery #412

Merged
merged 1 commit into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading