Add merge_metadata for other ESPs

Support merge_metadata in Mailgun, Mailjet, Mandrill, Postmark, 
SparkPost, and Test backends. (SendGrid covered in earlier PR.)

Also:
* Add `merge_metadata` to AnymailMessage, AnymailMessageMixin
* Add `is_batch()` logic to BasePayload, for consistent handling
* Docs

Note: Mailjet implementation switches *all* batch sending from their 
"Recipients" field to to the "Messages" array bulk sending option.
This allows an independent payload for each batch recipient.
In addition to supporting merge_metadata, this also removes the
prior limitation on mixing Cc/Bcc with merge_data.

Closes #141.
This commit is contained in:
Mike Edmunds
2019-02-23 13:32:28 -08:00
committed by GitHub
parent 85dce5fd6a
commit 75d7671056
22 changed files with 468 additions and 132 deletions

View File

@@ -11,7 +11,7 @@ from django.utils.functional import Promise
from django.utils.timezone import utc
from django.utils.translation import ugettext_lazy
from anymail.backends.test import EmailBackend as TestBackend
from anymail.backends.test import EmailBackend as TestBackend, TestPayload
from anymail.exceptions import AnymailConfigurationError, AnymailInvalidAddress, AnymailUnsupportedFeature
from anymail.message import AnymailMessage
from anymail.utils import get_anymail_setting
@@ -425,3 +425,45 @@ class SpecialHeaderTests(TestBackendTestCase):
self.message.extra_headers = {"To": "Apparent Recipient <but-not-really@example.com>"}
with self.assertRaisesMessage(AnymailUnsupportedFeature, "spoofing `To` header"):
self.message.send()
class BatchSendDetectionTestCase(TestBackendTestCase):
"""Tests shared code to consistently determine whether to use batch send"""
def setUp(self):
super(BatchSendDetectionTestCase, self).setUp()
self.backend = TestBackend()
def test_default_is_not_batch(self):
payload = self.backend.build_message_payload(self.message, {})
self.assertFalse(payload.is_batch())
def test_merge_data_implies_batch(self):
self.message.merge_data = {} # *anything* (even empty dict) implies batch
payload = self.backend.build_message_payload(self.message, {})
self.assertTrue(payload.is_batch())
def test_merge_metadata_implies_batch(self):
self.message.merge_metadata = {} # *anything* (even empty dict) implies batch
payload = self.backend.build_message_payload(self.message, {})
self.assertTrue(payload.is_batch())
def test_merge_global_data_does_not_imply_batch(self):
self.message.merge_global_data = {}
payload = self.backend.build_message_payload(self.message, {})
self.assertFalse(payload.is_batch())
def test_cannot_call_is_batch_during_init(self):
# It's tempting to try to warn about unsupported batch features in setters,
# but because of the way payload attrs are processed, it won't work...
class ImproperlyImplementedPayload(TestPayload):
def set_cc(self, emails):
if self.is_batch(): # this won't work here!
self.unsupported_feature("cc with batch send")
super(ImproperlyImplementedPayload, self).set_cc(emails)
connection = mail.get_connection('anymail.backends.test.EmailBackend',
payload_class=ImproperlyImplementedPayload)
with self.assertRaisesMessage(AssertionError,
"Cannot call is_batch before all attributes processed"):
connection.send_messages([self.message])