Skip to content

Commit faf98c2

Browse files
committed
MailerSend: support extra headers
MailerSend added a `"headers"` API field (which is available to "Enterprise accounts only").
1 parent 0776b12 commit faf98c2

File tree

5 files changed

+34
-25
lines changed

5 files changed

+34
-25
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ Features
5555
and ``tags`` when sending with a ``template_id``.
5656
(Requires boto3 v1.34.98 or later.)
5757

58+
* **MailerSend:** Allow all extra headers. (Note that MailerSend limits use
59+
of this feature to "Enterprise accounts only.")
60+
5861
Fixes
5962
~~~~~
6063

anymail/backends/mailersend.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ def set_reply_to(self, emails):
242242
self.data["reply_to"] = self.make_mailersend_email(emails[0])
243243

244244
def set_extra_headers(self, headers):
245-
# MailerSend doesn't support arbitrary email headers, but has
246-
# individual API params for In-Reply-To and Precedence: bulk.
245+
# MailerSend has individual API params for In-Reply-To and Precedence: bulk.
246+
# The general "headers" option "is available to Enterprise accounts only".
247247
# (headers is a CaseInsensitiveDict, and is a copy so safe to modify.)
248248
in_reply_to = headers.pop("In-Reply-To", None)
249249
if in_reply_to is not None:
@@ -256,7 +256,9 @@ def set_extra_headers(self, headers):
256256
self.data["precedence_bulk"] = is_bulk
257257

258258
if headers:
259-
self.unsupported_feature("most extra_headers (see docs)")
259+
self.data["headers"] = [
260+
{"name": field, "value": value} for field, value in headers.items()
261+
]
260262

261263
def set_text_body(self, body):
262264
self.data["text"] = body

docs/esps/mailersend.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,12 @@ see :ref:`unsupported-features`.
204204
Anymail will use only the first one.
205205

206206
**Limited extra headers**
207-
MailerSend does not allow most extra headers. There are two exceptions:
207+
MailerSend allows extra email headers for "Enterprise accounts only."
208+
If you try to send :ref:`extra headers <message-headers>` with a non-enterprise
209+
account, you may receive an API error.
210+
211+
However, MailerSend has special handling for two headers,
212+
and *any* MailerSend account can send messages with them:
208213

209214
* You can include :mailheader:`In-Reply-To` in extra headers, set to
210215
a message-id (without the angle brackets).
@@ -216,8 +221,11 @@ see :ref:`unsupported-features`.
216221
if your extra headers have :mailheader:`Precedence` set to ``"bulk"`` or
217222
``"list"`` or ``"junk"``, or ``false`` for any other value.
218223

219-
Any other extra headers will raise an
220-
:exc:`~anymail.exceptions.AnymailUnsupportedFeature` error.
224+
.. versionchanged:: 11.0
225+
226+
In earlier releases, attempting to send other headers
227+
(even with an enterprise account) would raise an
228+
:exc:`~anymail.exceptions.AnymailUnsupportedFeature` error.
221229

222230
**No merge headers support**
223231
MailerSend's API does not provide a way to support Anymail's

tests/test_mailersend_backend.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,23 +145,6 @@ def test_name_addr(self):
145145
)
146146

147147
def test_custom_headers(self):
148-
email = mail.EmailMessage(
149-
"Subject",
150-
"Body goes here",
151-
"from@example.com",
152-
["to1@example.com"],
153-
headers={
154-
"Reply-To": "another@example.com",
155-
"In-Reply-To": "12345@example.com",
156-
"X-MyHeader": "my value",
157-
"Message-ID": "mycustommsgid@example.com",
158-
"Precedence": "Bulk",
159-
},
160-
)
161-
with self.assertRaisesMessage(AnymailUnsupportedFeature, "extra_headers"):
162-
email.send()
163-
164-
def test_supported_custom_headers(self):
165148
email = mail.EmailMessage(
166149
"Subject",
167150
"Body goes here",
@@ -171,13 +154,20 @@ def test_supported_custom_headers(self):
171154
"Reply-To": "another@example.com",
172155
"In-Reply-To": "12345@example.com",
173156
"Precedence": "Bulk",
157+
# Other custom headers only available to enterprise accounts:
158+
"X-Custom": "custom header",
174159
},
175160
)
176161
email.send()
177162
data = self.get_api_call_json()
163+
# Special handling headers:
178164
self.assertEqual(data["reply_to"], {"email": "another@example.com"})
179165
self.assertEqual(data["in_reply_to"], "12345@example.com")
180166
self.assertIs(data["precedence_bulk"], True)
167+
# Other headers:
168+
self.assertEqual(
169+
data["headers"], [{"name": "X-Custom", "value": "custom header"}]
170+
)
181171

182172
def test_html_message(self):
183173
text_content = "This is an important message."
@@ -607,6 +597,7 @@ def test_default_omits_options(self):
607597
self.assertNotIn("reply_to", data)
608598
self.assertNotIn("html", data)
609599
self.assertNotIn("attachments", data)
600+
self.assertNotIn("headers", data)
610601
self.assertNotIn("template_id", data)
611602
self.assertNotIn("tags", data)
612603
self.assertNotIn("variables", data)

tests/test_mailersend_integration.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,13 @@ def test_all_options(self):
8181
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
8282
# MailerSend only supports single reply_to:
8383
reply_to=["Reply <reply@example.com>"],
84-
# MailerSend supports very limited extra headers:
85-
headers={"Precedence": "bulk", "In-Reply-To": "earlier-id@anymail.dev"},
84+
headers={
85+
# Special handling for these headers (available to all accounts):
86+
"Precedence": "bulk",
87+
"In-Reply-To": "earlier-id@anymail.dev",
88+
# Only "enterprise accounts" can send other custom headers:
89+
"X-Custom": "anymail test",
90+
},
8691
send_at=send_at,
8792
tags=["tag 1", "tag 2"],
8893
track_clicks=False,

0 commit comments

Comments
 (0)