Skip to content

Commit

Permalink
Mailgun: Add MAILGUN_SENDER_DOMAIN setting
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
medmunds committed Aug 3, 2016
1 parent 3f94e69 commit fb21c0d
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 24 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ or SparkPost or any other supported ESP where you see "mailgun":
)
ANYMAIL = {
# (exact settings here depend on your ESP...)
"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...
DEFAULT_FROM_EMAIL = "you@example.com" # if you don't already have this in settings
Expand Down
4 changes: 3 additions & 1 deletion anymail/backends/mailgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def __init__(self, **kwargs):
"""Init options from Django settings"""
esp_name = self.esp_name
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,
default="https://api.mailgun.net/v3")
if not api_url.endswith("/"):
Expand Down Expand Up @@ -54,7 +56,7 @@ class MailgunPayload(RequestsPayload):

def __init__(self, message, defaults, backend, *args, **kwargs):
auth = ("api", backend.api_key)
self.sender_domain = None
self.sender_domain = backend.sender_domain
self.all_recipients = [] # used for backend.parse_recipient_status

# late-binding of recipient-variables:
Expand Down
54 changes: 38 additions & 16 deletions docs/esps/mailgun.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ root of the settings file if neither ``ANYMAIL["MAILGUN_API_KEY"]``
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

.. rubric:: MAILGUN_API_URL
Expand All @@ -58,32 +72,40 @@ The default is ``MAILGUN_API_URL = "https://api.mailgun.net/v3"``
Email sender domain
-------------------

Mailgun's API requires a sender domain `in the API url <base-url>`_.
By default, Anymail will use the domain of each email's from address
as the domain for the Mailgun API.
Mailgun's API requires identifying the sender domain.
By default, Anymail uses the domain of each messages's `from_email`
(e.g., "example.com" for "from\@example.com").

You will need to override this default if you are using
a dedicated `Mailgun sender domain`_ that is different from
a message's `from_email` domain.

If you need to override this default, you can use Anymail's
:attr:`esp_extra` dict, either on an individual message:
For example, if you are sending from "orders\@example.com", but your
Mailgun account is configured for "*mail1*.example.com", you should provide
:setting:`MAILGUN_SENDER_DOMAIN <ANYMAIL_MAILGUN_SENDER_DOMAIN>` in your settings.py:

.. code-block:: python
:emphasize-lines: 4
message = EmailMessage(from_email="sales@europe.example.com", ...)
message.esp_extra = {"sender_domain": "example.com"}
ANYMAIL = {
...
"MAILGUN_API_KEY": "<your API key>",
"MAILGUN_SENDER_DOMAIN": "mail1.example.com"
}
... or as a global :ref:`send default <send-defaults>` setting that applies
to all messages:
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
ANYMAIL = {
...
"MAILGUN_SEND_DEFAULTS": {
"esp_extra": {"sender_domain": "example.com"}
}
}
message = EmailMessage(from_email="marketing@example.com", ...)
message.esp_extra = {"sender_domain": "mail2.example.com"}
.. _base-url: https://documentation.mailgun.com/api-intro.html#base-url
.. _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:
Expand Down
9 changes: 7 additions & 2 deletions tests/test_mailgun_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ def test_only_merge_global_data(self):

def test_sender_domain(self):
"""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'}}
# (The mailgun_integration_tests do that.)
# You could also use MAILGUN_SENDER_DOMAIN in your ANYMAIL settings, as in the next test.
# (The mailgun_integration_tests also do that.)
self.message.from_email = "Test From <from@from-email.example.com>"
self.message.send()
self.assert_esp_called('/from-email.example.com/messages') # API url includes the sender-domain
Expand All @@ -393,6 +393,11 @@ def test_sender_domain(self):
self.message.send()
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):
"""Make sure by default we don't send any ESP-specific options.
Expand Down
11 changes: 6 additions & 5 deletions tests/test_mailgun_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@
@unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN,
"Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables "
"to run Mailgun integration tests")
@override_settings(ANYMAIL_MAILGUN_API_KEY=MAILGUN_TEST_API_KEY,
ANYMAIL_MAILGUN_SEND_DEFAULTS={
'esp_extra': {'o:testmode': 'yes',
'sender_domain': MAILGUN_TEST_DOMAIN}},
@override_settings(ANYMAIL={'MAILGUN_API_KEY': MAILGUN_TEST_API_KEY,
'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}},
EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend")
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""Mailgun API integration tests
Expand Down Expand Up @@ -168,7 +167,9 @@ def test_invalid_from(self):
self.assertEqual(err.status_code, 400)
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):
with self.assertRaises(AnymailAPIError) as cm:
self.message.send()
Expand Down

0 comments on commit fb21c0d

Please sign in to comment.