Sendinblue: fix "invalid headers" error

Work around recent (unannounced) Sendinblue API change
that caused "Invalid headers" API error with non-string
custom header values, by converting basic numeric types
to strings. (Borrowed code from SendGrid backend.)

Fixes #288
This commit is contained in:
medmunds
2022-11-04 15:23:29 -07:00
committed by Mike Edmunds
parent 448e8d9d95
commit 681a4afe6b
3 changed files with 13 additions and 3 deletions

View File

@@ -52,6 +52,10 @@ Fixes
* **Postmark:** Handle Postmark's SubscriptionChange events as Anymail * **Postmark:** Handle Postmark's SubscriptionChange events as Anymail
unsubscribe, subscribe, or bounce tracking events, rather than "unknown". unsubscribe, subscribe, or bounce tracking events, rather than "unknown".
(Thanks to `@puru02`_ for the fix.) (Thanks to `@puru02`_ for the fix.)
* **Sendinblue:** Work around recent (unannounced) Sendinblue API change
that caused "Invalid headers" API error with non-string custom header
values. Anymail now converts int and float header values to strings.
Other Other
~~~~~ ~~~~~

View File

@@ -3,7 +3,7 @@ from requests.structures import CaseInsensitiveDict
from .base_requests import AnymailRequestsBackend, RequestsPayload from .base_requests import AnymailRequestsBackend, RequestsPayload
from ..exceptions import AnymailRequestsAPIError from ..exceptions import AnymailRequestsAPIError
from ..message import AnymailRecipientStatus from ..message import AnymailRecipientStatus
from ..utils import get_anymail_setting from ..utils import get_anymail_setting, BASIC_NUMERIC_TYPES
class EmailBackend(AnymailRequestsBackend): class EmailBackend(AnymailRequestsBackend):
@@ -112,7 +112,12 @@ class SendinBluePayload(RequestsPayload):
self.data['replyTo'] = self.email_object(emails[0]) self.data['replyTo'] = self.email_object(emails[0])
def set_extra_headers(self, headers): def set_extra_headers(self, headers):
self.data['headers'].update(headers) # SendinBlue requires header values to be strings -- not integers -- as of 11/2022.
# We'll stringify ints and floats; anything else is the caller's responsibility.
self.data["headers"].update({
k: str(v) if isinstance(v, BASIC_NUMERIC_TYPES) else v
for k, v in headers.items()
})
def set_tags(self, tags): def set_tags(self, tags):
if len(tags) > 0: if len(tags) > 0:

View File

@@ -118,7 +118,8 @@ class SendinBlueBackendStandardEmailTests(SendinBlueBackendMockAPITestCase):
self.message.send() self.message.send()
data = self.get_api_call_json() data = self.get_api_call_json()
self.assertEqual(data['headers']['X-Custom'], 'string') self.assertEqual(data['headers']['X-Custom'], 'string')
self.assertEqual(data['headers']['X-Num'], 123) # Header values must be strings (changed 11/2022)
self.assertEqual(data['headers']['X-Num'], "123")
# Reply-To must be moved to separate param # Reply-To must be moved to separate param
self.assertNotIn('Reply-To', data['headers']) self.assertNotIn('Reply-To', data['headers'])
self.assertEqual(data['replyTo'], {'name': "Do Not Reply", 'email': "noreply@example.com"}) self.assertEqual(data['replyTo'], {'name': "Do Not Reply", 'email': "noreply@example.com"})