Mailgun: Add MAILGUN_SENDER_DOMAIN setting

Allow custom MAILGUN_SENDER_DOMAIN in Anymail
settings. (Replaces need to use global esp_extra.)

Improve docs to cover cases where this is needed.

(esp_extra sender_domain is still supported for
overriding individual messages.)

Fixes #26.
This commit is contained in:
medmunds
2016-08-03 14:19:35 -07:00
parent 3f94e6910c
commit fb21c0d25b
5 changed files with 58 additions and 26 deletions

View File

@@ -100,7 +100,9 @@ or SparkPost or any other supported ESP where you see "mailgun":
) )
ANYMAIL = { ANYMAIL = {
# (exact settings here depend on your ESP...)
"MAILGUN_API_KEY": "<your Mailgun key>", "MAILGUN_API_KEY": "<your Mailgun key>",
"MAILGUN_SENDER_DOMAIN": 'mg.example.com', # your Mailgun domain, if needed
} }
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend" # or sendgrid.SendGridBackend, or... EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend" # or sendgrid.SendGridBackend, or...
DEFAULT_FROM_EMAIL = "you@example.com" # if you don't already have this in settings DEFAULT_FROM_EMAIL = "you@example.com" # if you don't already have this in settings

View File

@@ -16,6 +16,8 @@ class MailgunBackend(AnymailRequestsBackend):
"""Init options from Django settings""" """Init options from Django settings"""
esp_name = self.esp_name esp_name = self.esp_name
self.api_key = get_anymail_setting('api_key', esp_name=esp_name, kwargs=kwargs, allow_bare=True) self.api_key = get_anymail_setting('api_key', esp_name=esp_name, kwargs=kwargs, allow_bare=True)
self.sender_domain = get_anymail_setting('sender_domain', esp_name=esp_name, kwargs=kwargs,
allow_bare=True, default=None)
api_url = get_anymail_setting('api_url', esp_name=esp_name, kwargs=kwargs, api_url = get_anymail_setting('api_url', esp_name=esp_name, kwargs=kwargs,
default="https://api.mailgun.net/v3") default="https://api.mailgun.net/v3")
if not api_url.endswith("/"): if not api_url.endswith("/"):
@@ -54,7 +56,7 @@ class MailgunPayload(RequestsPayload):
def __init__(self, message, defaults, backend, *args, **kwargs): def __init__(self, message, defaults, backend, *args, **kwargs):
auth = ("api", backend.api_key) auth = ("api", backend.api_key)
self.sender_domain = None self.sender_domain = backend.sender_domain
self.all_recipients = [] # used for backend.parse_recipient_status self.all_recipients = [] # used for backend.parse_recipient_status
# late-binding of recipient-variables: # late-binding of recipient-variables:

View File

@@ -41,6 +41,20 @@ root of the settings file if neither ``ANYMAIL["MAILGUN_API_KEY"]``
nor ``ANYMAIL_MAILGUN_API_KEY`` is set. nor ``ANYMAIL_MAILGUN_API_KEY`` is set.
.. setting:: ANYMAIL_MAILGUN_SENDER_DOMAIN
.. rubric:: MAILGUN_SENDER_DOMAIN
If you are using a specific `Mailgun sender domain`_
that is *different* from your messages' `from_email` domains,
set this to the domain you've configured in your Mailgun account.
If your messages' `from_email` domains always match a configured
Mailgun sender domain, this setting is not needed.
See :ref:`mailgun-sender-domain` below for examples.
.. setting:: ANYMAIL_MAILGUN_API_URL .. setting:: ANYMAIL_MAILGUN_API_URL
.. rubric:: MAILGUN_API_URL .. rubric:: MAILGUN_API_URL
@@ -58,32 +72,40 @@ The default is ``MAILGUN_API_URL = "https://api.mailgun.net/v3"``
Email sender domain Email sender domain
------------------- -------------------
Mailgun's API requires a sender domain `in the API url <base-url>`_. Mailgun's API requires identifying the sender domain.
By default, Anymail will use the domain of each email's from address By default, Anymail uses the domain of each messages's `from_email`
as the domain for the Mailgun API. (e.g., "example.com" for "from\@example.com").
If you need to override this default, you can use Anymail's You will need to override this default if you are using
:attr:`esp_extra` dict, either on an individual message: a dedicated `Mailgun sender domain`_ that is different from
a message's `from_email` domain.
.. code-block:: python
For example, if you are sending from "orders\@example.com", but your
message = EmailMessage(from_email="sales@europe.example.com", ...) Mailgun account is configured for "*mail1*.example.com", you should provide
message.esp_extra = {"sender_domain": "example.com"} :setting:`MAILGUN_SENDER_DOMAIN <ANYMAIL_MAILGUN_SENDER_DOMAIN>` in your settings.py:
... or as a global :ref:`send default <send-defaults>` setting that applies
to all messages:
.. code-block:: python .. code-block:: python
:emphasize-lines: 4
ANYMAIL = { ANYMAIL = {
... ...
"MAILGUN_SEND_DEFAULTS": { "MAILGUN_API_KEY": "<your API key>",
"esp_extra": {"sender_domain": "example.com"} "MAILGUN_SENDER_DOMAIN": "mail1.example.com"
}
} }
.. _base-url: https://documentation.mailgun.com/api-intro.html#base-url
If you need to override the sender domain for an individual message,
include `sender_domain` in Anymail's :attr:`~anymail.message.AnymailMessage.esp_extra`
for that message:
.. code-block:: python
message = EmailMessage(from_email="marketing@example.com", ...)
message.esp_extra = {"sender_domain": "mail2.example.com"}
.. _Mailgun sender domain:
https://help.mailgun.com/hc/en-us/articles/202256730-How-do-I-pick-a-domain-name-for-my-Mailgun-account-
.. _mailgun-esp-extra: .. _mailgun-esp-extra:

View File

@@ -383,8 +383,8 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
def test_sender_domain(self): def test_sender_domain(self):
"""Mailgun send domain can come from from_email or esp_extra""" """Mailgun send domain can come from from_email or esp_extra"""
# You could also use ANYMAIL_SEND_DEFAULTS={'esp_extra': {'sender_domain': 'your-domain.com'}} # You could also use MAILGUN_SENDER_DOMAIN in your ANYMAIL settings, as in the next test.
# (The mailgun_integration_tests do that.) # (The mailgun_integration_tests also do that.)
self.message.from_email = "Test From <from@from-email.example.com>" self.message.from_email = "Test From <from@from-email.example.com>"
self.message.send() self.message.send()
self.assert_esp_called('/from-email.example.com/messages') # API url includes the sender-domain self.assert_esp_called('/from-email.example.com/messages') # API url includes the sender-domain
@@ -393,6 +393,11 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
self.message.send() self.message.send()
self.assert_esp_called('/esp-extra.example.com/messages') # overrides from_email self.assert_esp_called('/esp-extra.example.com/messages') # overrides from_email
@override_settings(ANYMAIL_MAILGUN_SENDER_DOMAIN='mg.example.com')
def test_sender_domain_setting(self):
self.message.send()
self.assert_esp_called('/mg.example.com/messages') # setting overrides from_email
def test_default_omits_options(self): def test_default_omits_options(self):
"""Make sure by default we don't send any ESP-specific options. """Make sure by default we don't send any ESP-specific options.

View File

@@ -23,10 +23,9 @@ MAILGUN_TEST_DOMAIN = os.getenv('MAILGUN_TEST_DOMAIN')
@unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN, @unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN,
"Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables " "Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables "
"to run Mailgun integration tests") "to run Mailgun integration tests")
@override_settings(ANYMAIL_MAILGUN_API_KEY=MAILGUN_TEST_API_KEY, @override_settings(ANYMAIL={'MAILGUN_API_KEY': MAILGUN_TEST_API_KEY,
ANYMAIL_MAILGUN_SEND_DEFAULTS={ 'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
'esp_extra': {'o:testmode': 'yes', 'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}},
'sender_domain': MAILGUN_TEST_DOMAIN}},
EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend") EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend")
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin): class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""Mailgun API integration tests """Mailgun API integration tests
@@ -168,7 +167,9 @@ class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
self.assertEqual(err.status_code, 400) self.assertEqual(err.status_code, 400)
self.assertIn("'from' parameter is not a valid address", str(err)) self.assertIn("'from' parameter is not a valid address", str(err))
@override_settings(ANYMAIL_MAILGUN_API_KEY="Hey, that's not an API key!") @override_settings(ANYMAIL={'MAILGUN_API_KEY': "Hey, that's not an API key",
'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}})
def test_invalid_api_key(self): def test_invalid_api_key(self):
with self.assertRaises(AnymailAPIError) as cm: with self.assertRaises(AnymailAPIError) as cm:
self.message.send() self.message.send()