From a1380b82f39ef5d86f08bf3de939938f72b2ecb9 Mon Sep 17 00:00:00 2001 From: medmunds Date: Thu, 13 Oct 2016 15:15:37 -0700 Subject: [PATCH] SendGrid: force empty text and html to " " with template_id Work around an unexpected limitation in SendGrid template rendering, where template text or html bodies are omitted if the supplied message text or html is "". Changing empty string to " " works around the issue. https://sendgrid.com/docs/API_Reference/Web_API_v3/Transactional_Templates/smtpapi.html#-Text-or-HTML-Templates Closes #32 --- anymail/backends/sendgrid.py | 6 ++++++ docs/esps/sendgrid.rst | 7 +++++++ tests/test_sendgrid_backend.py | 14 ++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/anymail/backends/sendgrid.py b/anymail/backends/sendgrid.py index b7e6f44..8434735 100644 --- a/anymail/backends/sendgrid.py +++ b/anymail/backends/sendgrid.py @@ -290,6 +290,12 @@ class SendGridPayload(RequestsPayload): def set_template_id(self, template_id): self.add_filter('templates', 'enable', 1) self.add_filter('templates', 'template_id', template_id) + # Must ensure text and html are non-empty, or template parts won't render. + # https://sendgrid.com/docs/API_Reference/Web_API_v3/Transactional_Templates/smtpapi.html#-Text-or-HTML-Templates + if not self.data.get("text", ""): + self.data["text"] = " " + if not self.data.get("html", ""): + self.data["html"] = " " def set_merge_data(self, merge_data): # Becomes smtpapi['sub'] in build_merge_data, after we know recipients and merge_field_format. diff --git a/docs/esps/sendgrid.rst b/docs/esps/sendgrid.rst index be169f0..6cc9dbf 100644 --- a/docs/esps/sendgrid.rst +++ b/docs/esps/sendgrid.rst @@ -282,6 +282,11 @@ your SendGrid template definition where you want the message-specific versions to appear). If you don't want to supply any additional subject or body content from your Django app, set those EmailMessage attributes to empty strings. +(Anymail will convert empty text and HTML bodies to single spaces whenever +:attr:`~anymail.message.AnymailMessage.template_id` is set, to ensure the +plaintext and HTML from your template are present in your outgoing email. +This works around a `limitation in SendGrid's template rendering`_.) + See the `SendGrid's template overview`_ and `transactional template docs`_ for more information. @@ -289,6 +294,8 @@ for more information. https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html .. _transactional template docs: https://sendgrid.com/docs/API_Reference/Web_API_v3/Transactional_Templates/smtpapi.html +.. _limitation in SendGrid's template rendering: + https://sendgrid.com/docs/API_Reference/Web_API_v3/Transactional_Templates/smtpapi.html#-Text-or-HTML-Templates .. _sendgrid-webhooks: diff --git a/tests/test_sendgrid_backend.py b/tests/test_sendgrid_backend.py index 42ffa89..f0b764d 100644 --- a/tests/test_sendgrid_backend.py +++ b/tests/test_sendgrid_backend.py @@ -400,6 +400,7 @@ class SendGridBackendAnymailFeatureTests(SendGridBackendMockAPITestCase): self.assertEqual(smtpapi['filters']['opentrack'], {'settings': {'enable': 0}}) def test_template_id(self): + self.message.attach_alternative("HTML Body", "text/html") self.message.template_id = "5997fcf6-2b9f-484d-acd5-7e9a99f0dc1f" self.message.send() smtpapi = self.get_smtpapi() @@ -407,6 +408,19 @@ class SendGridBackendAnymailFeatureTests(SendGridBackendMockAPITestCase): 'settings': {'enable': 1, 'template_id': "5997fcf6-2b9f-484d-acd5-7e9a99f0dc1f"} }) + data = self.get_api_call_data() + self.assertEqual(data['text'], "Text Body") + self.assertEqual(data['html'], "HTML Body") + + def test_template_id_with_empty_body(self): + # Text and html must be present (and non-empty-string), or the corresponding + # part will not render from the template. Make sure we fill in strings: + message = mail.EmailMessage(from_email='from@example.com', to=['to@example.com']) + message.template_id = "5997fcf6-2b9f-484d-acd5-7e9a99f0dc1f" + message.send() + data = self.get_api_call_data() + self.assertEqual(data['text'], " ") # single space is sufficient + self.assertEqual(data['html'], " ") def test_merge_data(self): self.message.from_email = 'from@example.com'