mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
SparkPost: add SPARKPOST_API_URL setting to allow SparkPost EU, etc.
Closes #100
This commit is contained in:
@@ -24,8 +24,18 @@ class EmailBackend(AnymailBaseBackend):
|
|||||||
# SPARKPOST_API_KEY is optional - library reads from env by default
|
# SPARKPOST_API_KEY is optional - library reads from env by default
|
||||||
self.api_key = get_anymail_setting('api_key', esp_name=self.esp_name,
|
self.api_key = get_anymail_setting('api_key', esp_name=self.esp_name,
|
||||||
kwargs=kwargs, allow_bare=True, default=None)
|
kwargs=kwargs, allow_bare=True, default=None)
|
||||||
|
|
||||||
|
# SPARKPOST_API_URL is optional - default is set by library;
|
||||||
|
# if provided, must be a full SparkPost API endpoint, including "/v1" if appropriate
|
||||||
|
api_url = get_anymail_setting('api_url', esp_name=self.esp_name, kwargs=kwargs, default=None)
|
||||||
|
extra_sparkpost_params = {}
|
||||||
|
if api_url is not None:
|
||||||
|
if api_url.endswith("/"):
|
||||||
|
api_url = api_url[:-1]
|
||||||
|
extra_sparkpost_params['base_uri'] = _FullSparkPostEndpoint(api_url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sp = SparkPost(self.api_key) # SparkPost API instance
|
self.sp = SparkPost(self.api_key, **extra_sparkpost_params) # SparkPost API instance
|
||||||
except SparkPostException as err:
|
except SparkPostException as err:
|
||||||
# This is almost certainly a missing API key
|
# This is almost certainly a missing API key
|
||||||
raise AnymailConfigurationError(
|
raise AnymailConfigurationError(
|
||||||
@@ -209,3 +219,33 @@ class SparkPostPayload(BasePayload):
|
|||||||
# ESP-specific payload construction
|
# ESP-specific payload construction
|
||||||
def set_esp_extra(self, extra):
|
def set_esp_extra(self, extra):
|
||||||
self.params.update(extra)
|
self.params.update(extra)
|
||||||
|
|
||||||
|
|
||||||
|
class _FullSparkPostEndpoint(str):
|
||||||
|
"""A string-like object that allows using a complete SparkPost API endpoint url as base_uri:
|
||||||
|
|
||||||
|
sp = SparkPost(api_key, base_uri=_FullSparkPostEndpoint('https://api.sparkpost.com/api/labs'))
|
||||||
|
|
||||||
|
Works around SparkPost.__init__ code `self.base_uri = base_uri + '/api/v' + version`,
|
||||||
|
which makes it difficult to simply copy and paste full API endpoints from SparkPost's docs
|
||||||
|
(https://developers.sparkpost.com/api/index.html#header-api-endpoints) -- and completely
|
||||||
|
prevents using the labs API endpoint (which has no "v" in it).
|
||||||
|
|
||||||
|
Should work with all python-sparkpost releases through at least v1.3.6.
|
||||||
|
"""
|
||||||
|
_expect = ['/api/v', '1'] # ignore attempts to concatenate these with me (in order)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
expected = self._expect[0]
|
||||||
|
self._expect = self._expect[1:] # (makes a copy for this instance)
|
||||||
|
if other == expected:
|
||||||
|
# ignore this operation
|
||||||
|
if self._expect:
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return str(self) # my work is done; just be a normal str now
|
||||||
|
else:
|
||||||
|
# something changed in python-sparkpost; please open an Anymail issue to fix
|
||||||
|
raise ValueError(
|
||||||
|
"This version of Anymail is not compatible with this version of python-sparkpost.\n"
|
||||||
|
"(_FullSparkPostEndpoint(%r) expected %r but got %r)" % (self, expected, other))
|
||||||
|
|||||||
@@ -61,6 +61,30 @@ nor ``ANYMAIL_SPARKPOST_API_KEY`` is set.
|
|||||||
.. _SparkPost account API keys: https://app.sparkpost.com/account/credentials
|
.. _SparkPost account API keys: https://app.sparkpost.com/account/credentials
|
||||||
|
|
||||||
|
|
||||||
|
.. setting:: ANYMAIL_SPARKPOST_API_URL
|
||||||
|
|
||||||
|
.. rubric:: SPARKPOST_API_URL
|
||||||
|
|
||||||
|
The `SparkPost API Endpoint`_ to use. This setting is optional; if not provided, Anymail will
|
||||||
|
use the :pypi:`python-sparkpost` client default endpoint (``"https://api.sparkpost.com/api/v1"``).
|
||||||
|
|
||||||
|
Set this to use a SparkPost EU account, or to work with any other API endpoint including
|
||||||
|
SparkPost Enterprise API and SparkPost Labs.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
ANYMAIL = {
|
||||||
|
...
|
||||||
|
"SPARKPOST_API_URL": "https://api.eu.sparkpost.com/api/v1", # use SparkPost EU
|
||||||
|
}
|
||||||
|
|
||||||
|
You must specify the full, versioned API endpoint as shown above (not just the base_uri).
|
||||||
|
This setting only affects Anymail's calls to SparkPost, and will not apply to other code
|
||||||
|
using :pypi:`python-sparkpost`.
|
||||||
|
|
||||||
|
.. _SparkPost API Endpoint: https://developers.sparkpost.com/api/index.html#header-api-endpoints
|
||||||
|
|
||||||
|
|
||||||
.. _sparkpost-esp-extra:
|
.. _sparkpost-esp-extra:
|
||||||
|
|
||||||
esp_extra support
|
esp_extra support
|
||||||
|
|||||||
@@ -586,8 +586,8 @@ class SparkPostBackendRecipientsRefusedTests(SparkPostBackendMockAPITestCase):
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
|
||||||
class SparkPostBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class SparkPostBackendConfigurationTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test various SparkPost client options"""
|
||||||
|
|
||||||
def test_missing_api_key(self):
|
def test_missing_api_key(self):
|
||||||
with self.assertRaises(AnymailConfigurationError) as cm:
|
with self.assertRaises(AnymailConfigurationError) as cm:
|
||||||
@@ -606,3 +606,20 @@ class SparkPostBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin
|
|||||||
# Poke into implementation details to verify:
|
# Poke into implementation details to verify:
|
||||||
self.assertIsNone(conn.api_key) # Anymail prop
|
self.assertIsNone(conn.api_key) # Anymail prop
|
||||||
self.assertEqual(conn.sp.api_key, 'key_from_environment') # SparkPost prop
|
self.assertEqual(conn.sp.api_key, 'key_from_environment') # SparkPost prop
|
||||||
|
|
||||||
|
@override_settings(ANYMAIL={
|
||||||
|
"SPARKPOST_API_URL": "https://api.eu.sparkpost.com/api/v1",
|
||||||
|
"SPARKPOST_API_KEY": "example-key",
|
||||||
|
})
|
||||||
|
def test_sparkpost_api_url(self):
|
||||||
|
conn = mail.get_connection() # this init's the backend without sending anything
|
||||||
|
# Poke into implementation details to verify:
|
||||||
|
self.assertEqual(conn.sp.base_uri, "https://api.eu.sparkpost.com/api/v1")
|
||||||
|
|
||||||
|
# can also override on individual connection (and even use non-versioned labs endpoint)
|
||||||
|
conn2 = mail.get_connection(api_url="https://api.sparkpost.com/api/labs")
|
||||||
|
self.assertEqual(conn2.sp.base_uri, "https://api.sparkpost.com/api/labs")
|
||||||
|
|
||||||
|
# double-check _FullSparkPostEndpoint won't interfere with additional str ops
|
||||||
|
self.assertEqual(conn.sp.base_uri + "/transmissions/send",
|
||||||
|
"https://api.eu.sparkpost.com/api/v1/transmissions/send")
|
||||||
|
|||||||
Reference in New Issue
Block a user