mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Resend: support batch send
Add support for `merge_metadata` and new Resend email/batch API.
This commit is contained in:
@@ -14,7 +14,7 @@ from anymail.exceptions import (
|
||||
AnymailSerializationError,
|
||||
AnymailUnsupportedFeature,
|
||||
)
|
||||
from anymail.message import attach_inline_image_file
|
||||
from anymail.message import AnymailMessage, attach_inline_image_file
|
||||
|
||||
from .mock_requests_backend import (
|
||||
RequestsBackendMockAPITestCase,
|
||||
@@ -445,6 +445,94 @@ class ResendBackendAnymailFeatureTests(ResendBackendMockAPITestCase):
|
||||
},
|
||||
)
|
||||
|
||||
_mock_batch_response = {
|
||||
"data": [
|
||||
{"id": "ae2014de-c168-4c61-8267-70d2662a1ce1"},
|
||||
{"id": "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb"},
|
||||
]
|
||||
}
|
||||
|
||||
def test_merge_data(self):
|
||||
self.message.merge_data = {"to@example.com": {"customer_id": 3}}
|
||||
with self.assertRaisesMessage(AnymailUnsupportedFeature, "merge_data"):
|
||||
self.message.send()
|
||||
|
||||
def test_empty_merge_data(self):
|
||||
# `merge_data = {}` triggers batch send
|
||||
self.set_mock_response(json_data=self._mock_batch_response)
|
||||
message = AnymailMessage(
|
||||
from_email="from@example.com",
|
||||
to=["alice@example.com", "Bob <bob@example.com>"],
|
||||
cc=["cc@example.com"],
|
||||
merge_data={
|
||||
"alice@example.com": {},
|
||||
"bob@example.com": {},
|
||||
},
|
||||
)
|
||||
message.send()
|
||||
self.assert_esp_called("/emails/batch")
|
||||
data = self.get_api_call_json()
|
||||
self.assertEqual(len(data), 2)
|
||||
self.assertEqual(data[0]["to"], ["alice@example.com"])
|
||||
self.assertEqual(data[1]["to"], ["Bob <bob@example.com>"])
|
||||
|
||||
recipients = message.anymail_status.recipients
|
||||
self.assertEqual(recipients["alice@example.com"].status, "queued")
|
||||
self.assertEqual(
|
||||
recipients["alice@example.com"].message_id,
|
||||
"ae2014de-c168-4c61-8267-70d2662a1ce1",
|
||||
)
|
||||
self.assertEqual(recipients["bob@example.com"].status, "queued")
|
||||
self.assertEqual(
|
||||
recipients["bob@example.com"].message_id,
|
||||
"faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb",
|
||||
)
|
||||
# No message_id for cc/bcc recipients in a batch send
|
||||
self.assertEqual(recipients["cc@example.com"].status, "queued")
|
||||
self.assertIsNone(recipients["cc@example.com"].message_id)
|
||||
|
||||
def test_merge_metadata(self):
|
||||
self.set_mock_response(json_data=self._mock_batch_response)
|
||||
message = AnymailMessage(
|
||||
from_email="from@example.com",
|
||||
to=["alice@example.com", "Bob <bob@example.com>"],
|
||||
merge_metadata={
|
||||
"alice@example.com": {"order_id": 123, "tier": "premium"},
|
||||
"bob@example.com": {"order_id": 678},
|
||||
},
|
||||
metadata={"notification_batch": "zx912"},
|
||||
)
|
||||
message.send()
|
||||
|
||||
# merge_metadata forces batch send API:
|
||||
self.assert_esp_called("/emails/batch")
|
||||
|
||||
data = self.get_api_call_json()
|
||||
self.assertEqual(len(data), 2)
|
||||
self.assertEqual(data[0]["to"], ["alice@example.com"])
|
||||
# metadata and merge_metadata[recipient] are combined:
|
||||
self.assertEqual(
|
||||
json.loads(data[0]["headers"]["X-Metadata"]),
|
||||
{"order_id": 123, "tier": "premium", "notification_batch": "zx912"},
|
||||
)
|
||||
self.assertEqual(data[1]["to"], ["Bob <bob@example.com>"])
|
||||
self.assertEqual(
|
||||
json.loads(data[1]["headers"]["X-Metadata"]),
|
||||
{"order_id": 678, "notification_batch": "zx912"},
|
||||
)
|
||||
|
||||
recipients = message.anymail_status.recipients
|
||||
self.assertEqual(recipients["alice@example.com"].status, "queued")
|
||||
self.assertEqual(
|
||||
recipients["alice@example.com"].message_id,
|
||||
"ae2014de-c168-4c61-8267-70d2662a1ce1",
|
||||
)
|
||||
self.assertEqual(recipients["bob@example.com"].status, "queued")
|
||||
self.assertEqual(
|
||||
recipients["bob@example.com"].message_id,
|
||||
"faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb",
|
||||
)
|
||||
|
||||
def test_track_opens(self):
|
||||
self.message.track_opens = True
|
||||
with self.assertRaisesMessage(AnymailUnsupportedFeature, "track_opens"):
|
||||
|
||||
@@ -86,6 +86,37 @@ class ResendBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
|
||||
len(message.anymail_status.message_id), 0
|
||||
) # non-empty string
|
||||
|
||||
def test_batch_send(self):
|
||||
# merge_metadata or merge_data will use batch send API
|
||||
message = AnymailMessage(
|
||||
subject="Anymail Resend batch sendintegration test",
|
||||
body="This is the text body",
|
||||
from_email=self.from_email,
|
||||
to=["test+to1@anymail.dev", '"Recipient 2" <test+to2@anymail.dev>'],
|
||||
metadata={"meta1": "simple string", "meta2": 2},
|
||||
merge_metadata={
|
||||
"test+to1@anymail.dev": {"meta3": "recipient 1"},
|
||||
"test+to2@anymail.dev": {"meta3": "recipient 2"},
|
||||
},
|
||||
tags=["tag 1", "tag 2"],
|
||||
)
|
||||
message.attach_alternative("<p>HTML content</p>", "text/html")
|
||||
message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
|
||||
|
||||
message.send()
|
||||
# Resend always queues:
|
||||
self.assertEqual(message.anymail_status.status, {"queued"})
|
||||
recipient_status = message.anymail_status.recipients
|
||||
self.assertEqual(recipient_status["test+to1@anymail.dev"].status, "queued")
|
||||
self.assertEqual(recipient_status["test+to2@anymail.dev"].status, "queued")
|
||||
self.assertRegex(recipient_status["test+to1@anymail.dev"].message_id, r".+")
|
||||
self.assertRegex(recipient_status["test+to2@anymail.dev"].message_id, r".+")
|
||||
# Each recipient gets their own message_id:
|
||||
self.assertNotEqual(
|
||||
recipient_status["test+to1@anymail.dev"].message_id,
|
||||
recipient_status["test+to2@anymail.dev"].message_id,
|
||||
)
|
||||
|
||||
@unittest.skip("Resend has stopped responding to bad/missing API keys (12/2023)")
|
||||
@override_settings(ANYMAIL_RESEND_API_KEY="Hey, that's not an API key!")
|
||||
def test_invalid_api_key(self):
|
||||
|
||||
Reference in New Issue
Block a user