Mandrill: support esp_extra

* Merge esp_extra with Mandrill send payload
* Handle pythonic forms of `recipient_metadata`
  and `template_content` in esp_extra
* DeprecationWarning for Mandrill EmailMessage
  attributes inherited from Djrill
This commit is contained in:
medmunds
2016-05-11 15:08:57 -07:00
parent d1da685d13
commit a0b92bee7a
4 changed files with 390 additions and 218 deletions

View File

@@ -401,6 +401,69 @@ class MandrillBackendAnymailFeatureTests(MandrillBackendMockAPITestCase):
data = self.get_api_call_json()
self.assertNotIn('subject', data['message'])
def test_esp_extra(self):
self.message.esp_extra = {
'ip_pool': 'Bulk Pool', # Mandrill send param that goes at top level of API payload
'message': {
'subaccount': 'Marketing Dept.' # param that goes within message dict
}
}
self.message.tags = ['test-tag'] # make sure non-esp_extra params are merged
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['ip_pool'], 'Bulk Pool')
self.assertEqual(data['message']['subaccount'], 'Marketing Dept.')
self.assertEqual(data['message']['tags'], ['test-tag'])
def test_esp_extra_recipient_metadata(self):
"""Anymail allows pythonic recipient_metadata dict"""
self.message.esp_extra = {'message': {'recipient_metadata': {
# Anymail expands simple python dicts into the more-verbose
# rcpt/values lists the Mandrill API uses
"customer@example.com": {'cust_id': "67890", 'order_id': "54321"},
"guest@example.com": {'cust_id': "94107", 'order_id': "43215"} ,
}}}
self.message.send()
data = self.get_api_call_json()
self.assertCountEqual(data['message']['recipient_metadata'], [
{'rcpt': "customer@example.com", 'values': {'cust_id': "67890", 'order_id': "54321"}},
{'rcpt': "guest@example.com", 'values': {'cust_id': "94107", 'order_id': "43215"}}])
# You can also just supply it in Mandrill's native form
self.message.esp_extra = {'message': {'recipient_metadata': [
{'rcpt': "customer@example.com", 'values': {'cust_id': "80806", 'order_id': "70701"}},
{'rcpt': "guest@example.com", 'values': {'cust_id': "21212", 'order_id': "10305"}}]}}
self.message.send()
data = self.get_api_call_json()
self.assertCountEqual(data['message']['recipient_metadata'], [
{'rcpt': "customer@example.com", 'values': {'cust_id': "80806", 'order_id': "70701"}},
{'rcpt': "guest@example.com", 'values': {'cust_id': "21212", 'order_id': "10305"}}])
def test_esp_extra_template_content(self):
"""Anymail allows pythonic template_content dict"""
self.message.template_id = "welcome_template" # forces send-template API and default template_content
self.message.esp_extra = {'template_content': {
# Anymail expands simple python dicts into the more-verbose name/content
# structures the Mandrill API uses
'HEADLINE': "<h1>Specials Just For *|FNAME|*</h1>",
'OFFER_BLOCK': "<p><em>Half off</em> all fruit</p>",
}}
self.message.send()
data = self.get_api_call_json()
self.assertCountEqual(data['template_content'], [
{'name': "HEADLINE", 'content': "<h1>Specials Just For *|FNAME|*</h1>"},
{'name': "OFFER_BLOCK", 'content': "<p><em>Half off</em> all fruit</p>"}])
# You can also just supply it in Mandrill's native form
self.message.esp_extra = {'template_content': [
{'name': "HEADLINE", 'content': "<h1>Exciting offers for *|FNAME|*</h1>"},
{'name': "OFFER_BLOCK", 'content': "<p><em>25% off</em> all fruit</p>"}]}
self.message.send()
data = self.get_api_call_json()
self.assertCountEqual(data['template_content'], [
{'name': "HEADLINE", 'content': "<h1>Exciting offers for *|FNAME|*</h1>"},
{'name': "OFFER_BLOCK", 'content': "<p><em>25% off</em> all fruit</p>"}])
def test_default_omits_options(self):
"""Make sure by default we don't send any ESP-specific options.
@@ -411,11 +474,15 @@ class MandrillBackendAnymailFeatureTests(MandrillBackendMockAPITestCase):
self.message.send()
self.assert_esp_called("/messages/send.json")
data = self.get_api_call_json()
self.assertNotIn('global_merge_vars', data['message'])
self.assertNotIn('merge_vars', data['message'])
self.assertNotIn('metadata', data['message'])
self.assertNotIn('send_at', data)
self.assertNotIn('tags', data['message'])
self.assertNotIn('track_opens', data['message'])
self.assertNotIn('template_content', data['message'])
self.assertNotIn('template_name', data['message'])
self.assertNotIn('track_clicks', data['message'])
self.assertNotIn('track_opens', data['message'])
# noinspection PyUnresolvedReferences
def test_send_attaches_anymail_status(self):

View File

@@ -2,54 +2,85 @@ from datetime import date
from django.core import mail
from django.test import override_settings
from anymail.exceptions import AnymailAPIError, AnymailSerializationError
from anymail.exceptions import AnymailSerializationError
from .test_mandrill_backend import MandrillBackendMockAPITestCase
class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
"""Test backend support for features leftover from Djrill"""
"""Test backend support for deprecated features leftover from Djrill"""
# Most of these features should be moved to esp_extra.
# The template and merge_ta
# These features should now be accessed through esp_extra
def test_djrill_message_options(self):
self.message.url_strip_qs = True
self.message.important = True
self.message.auto_text = True
self.message.auto_html = True
self.message.inline_css = True
self.message.preserve_recipients = True
self.message.view_content_link = False
self.message.tracking_domain = "click.example.com"
self.message.signing_domain = "example.com"
self.message.return_path_domain = "support.example.com"
self.message.subaccount = "marketing-dept"
def test_async(self):
self.message.async = True
self.message.ip_pool = "Bulk Pool"
self.message.send()
with self.assertWarnsRegex(DeprecationWarning, 'async'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['url_strip_qs'], True)
self.assertEqual(data['message']['important'], True)
self.assertEqual(data['message']['auto_text'], True)
self.assertEqual(data['message']['auto_html'], True)
self.assertEqual(data['message']['inline_css'], True)
self.assertEqual(data['message']['preserve_recipients'], True)
self.assertEqual(data['message']['view_content_link'], False)
self.assertEqual(data['message']['tracking_domain'], "click.example.com")
self.assertEqual(data['message']['signing_domain'], "example.com")
self.assertEqual(data['message']['return_path_domain'], "support.example.com")
self.assertEqual(data['message']['subaccount'], "marketing-dept")
self.assertEqual(data['async'], True)
self.assertEqual(data['ip_pool'], "Bulk Pool")
def test_google_analytics(self):
self.message.google_analytics_domains = ["example.com"]
def test_auto_html(self):
self.message.auto_html = True
with self.assertWarnsRegex(DeprecationWarning, 'auto_html'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['auto_html'], True)
def test_auto_text(self):
self.message.auto_text = True
with self.assertWarnsRegex(DeprecationWarning, 'auto_text'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['auto_text'], True)
def test_google_analytics_campaign(self):
self.message.google_analytics_campaign = "Email Receipts"
self.message.send()
with self.assertWarnsRegex(DeprecationWarning, 'google_analytics_campaign'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['google_analytics_campaign'], "Email Receipts")
def test_google_analytics_domains(self):
self.message.google_analytics_domains = ["example.com"]
with self.assertWarnsRegex(DeprecationWarning, 'google_analytics_domains'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['google_analytics_domains'], ["example.com"])
self.assertEqual(data['message']['google_analytics_campaign'], "Email Receipts")
def test_important(self):
self.message.important = True
with self.assertWarnsRegex(DeprecationWarning, 'important'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['important'], True)
def test_inline_css(self):
self.message.inline_css = True
with self.assertWarnsRegex(DeprecationWarning, 'inline_css'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['inline_css'], True)
def test_ip_pool(self):
self.message.ip_pool = "Bulk Pool"
with self.assertWarnsRegex(DeprecationWarning, 'ip_pool'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['ip_pool'], "Bulk Pool")
def test_merge_language(self):
self.message.merge_language = "mailchimp"
with self.assertWarnsRegex(DeprecationWarning, 'merge_language'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['merge_language'], "mailchimp")
def test_preserve_recipients(self):
self.message.preserve_recipients = True
with self.assertWarnsRegex(DeprecationWarning, 'preserve_recipients'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['preserve_recipients'], True)
def test_recipient_metadata(self):
self.message.recipient_metadata = {
@@ -58,33 +89,87 @@ class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
"customer@example.com": {'cust_id': "67890", 'order_id': "54321"},
"guest@example.com": {'cust_id': "94107", 'order_id': "43215"}
}
self.message.send()
with self.assertWarnsRegex(DeprecationWarning, 'recipient_metadata'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['recipient_metadata'],
[{'rcpt': "customer@example.com",
'values': {'cust_id': "67890", 'order_id': "54321"}},
{'rcpt': "guest@example.com",
'values': {'cust_id': "94107", 'order_id': "43215"}}
])
self.assertCountEqual(data['message']['recipient_metadata'], [
{'rcpt': "customer@example.com",
'values': {'cust_id': "67890", 'order_id': "54321"}},
{'rcpt': "guest@example.com",
'values': {'cust_id': "94107", 'order_id': "43215"}}])
def test_no_subaccount_by_default(self):
mail.send_mail('Subject', 'Body', 'from@example.com', ['to@example.com'])
def test_return_path_domain(self):
self.message.return_path_domain = "support.example.com"
with self.assertWarnsRegex(DeprecationWarning, 'return_path_domain'):
self.message.send()
data = self.get_api_call_json()
self.assertFalse('subaccount' in data['message'])
self.assertEqual(data['message']['return_path_domain'], "support.example.com")
@override_settings(ANYMAIL_MANDRILL_SEND_DEFAULTS={'subaccount': 'test_subaccount'})
def test_subaccount_setting(self):
mail.send_mail('Subject', 'Body', 'from@example.com', ['to@example.com'])
def test_signing_domain(self):
self.message.signing_domain = "example.com"
with self.assertWarnsRegex(DeprecationWarning, 'signing_domain'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['subaccount'], "test_subaccount")
self.assertEqual(data['message']['signing_domain'], "example.com")
@override_settings(ANYMAIL_MANDRILL_SEND_DEFAULTS={'subaccount': 'global_setting_subaccount'})
def test_subaccount_message_overrides_setting(self):
message = mail.EmailMessage('Subject', 'Body', 'from@example.com', ['to@example.com'])
message.subaccount = "individual_message_subaccount" # should override global setting
message.send()
def test_subaccount(self):
self.message.subaccount = "marketing-dept"
with self.assertWarnsRegex(DeprecationWarning, 'subaccount'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['subaccount'], "individual_message_subaccount")
self.assertEqual(data['message']['subaccount'], "marketing-dept")
def test_template_content(self):
self.message.template_content = {
'HEADLINE': "<h1>Specials Just For *|FNAME|*</h1>",
'OFFER_BLOCK': "<p><em>Half off</em> all fruit</p>"
}
with self.assertWarnsRegex(DeprecationWarning, 'template_content'):
self.message.send()
data = self.get_api_call_json()
# Anymail expands simple python dicts into the more-verbose name/content
# structures the Mandrill API uses
self.assertCountEqual(data['template_content'], [
{'name': "HEADLINE", 'content': "<h1>Specials Just For *|FNAME|*</h1>"},
{'name': "OFFER_BLOCK", 'content': "<p><em>Half off</em> all fruit</p>"}])
def test_tracking_domain(self):
self.message.tracking_domain = "click.example.com"
with self.assertWarnsRegex(DeprecationWarning, 'tracking_domain'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['tracking_domain'], "click.example.com")
def test_url_strip_qs(self):
self.message.url_strip_qs = True
with self.assertWarnsRegex(DeprecationWarning, 'url_strip_qs'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['url_strip_qs'], True)
def test_use_template_from(self):
self.message.template_id = "PERSONALIZED_SPECIALS" # forces send-template api
self.message.use_template_from = True
with self.assertWarnsRegex(DeprecationWarning, 'use_template_from'):
self.message.send()
data = self.get_api_call_json()
self.assertNotIn('from_email', data['message'])
self.assertNotIn('from_name', data['message'])
def test_use_template_subject(self):
self.message.template_id = "PERSONALIZED_SPECIALS" # force send-template API
self.message.use_template_subject = True
with self.assertWarnsRegex(DeprecationWarning, 'use_template_subject'):
self.message.send()
data = self.get_api_call_json()
self.assertNotIn('subject', data['message'])
def test_view_content_link(self):
self.message.view_content_link = True
with self.assertWarnsRegex(DeprecationWarning, 'view_content_link'):
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['view_content_link'], True)
def test_default_omits_options(self):
"""Make sure by default we don't send any Mandrill-specific options.
@@ -96,25 +181,25 @@ class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
self.message.send()
self.assert_esp_called("/messages/send.json")
data = self.get_api_call_json()
self.assertFalse('from_name' in data['message'])
self.assertFalse('bcc_address' in data['message'])
self.assertFalse('important' in data['message'])
self.assertFalse('auto_text' in data['message'])
self.assertFalse('auto_html' in data['message'])
self.assertFalse('inline_css' in data['message'])
self.assertFalse('url_strip_qs' in data['message'])
self.assertFalse('preserve_recipients' in data['message'])
self.assertFalse('view_content_link' in data['message'])
self.assertFalse('tracking_domain' in data['message'])
self.assertFalse('signing_domain' in data['message'])
self.assertFalse('return_path_domain' in data['message'])
self.assertFalse('subaccount' in data['message'])
self.assertFalse('google_analytics_domains' in data['message'])
self.assertFalse('google_analytics_campaign' in data['message'])
self.assertFalse('merge_language' in data['message'])
self.assertFalse('auto_text' in data['message'])
self.assertFalse('bcc_address' in data['message'])
self.assertFalse('from_name' in data['message'])
self.assertFalse('global_merge_vars' in data['message'])
self.assertFalse('google_analytics_campaign' in data['message'])
self.assertFalse('google_analytics_domains' in data['message'])
self.assertFalse('important' in data['message'])
self.assertFalse('inline_css' in data['message'])
self.assertFalse('merge_language' in data['message'])
self.assertFalse('merge_vars' in data['message'])
self.assertFalse('preserve_recipients' in data['message'])
self.assertFalse('recipient_metadata' in data['message'])
self.assertFalse('return_path_domain' in data['message'])
self.assertFalse('signing_domain' in data['message'])
self.assertFalse('subaccount' in data['message'])
self.assertFalse('tracking_domain' in data['message'])
self.assertFalse('url_strip_qs' in data['message'])
self.assertFalse('view_content_link' in data['message'])
# Options at top level of api params (not in message dict):
self.assertFalse('async' in data)
self.assertFalse('ip_pool' in data)
@@ -125,121 +210,20 @@ class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
with self.assertRaises(AnymailSerializationError):
self.message.send()
@override_settings(ANYMAIL_SEND_DEFAULTS={
'from_name': 'Djrill Test',
'important': True,
'auto_text': True,
'auto_html': True,
'inline_css': True,
'url_strip_qs': True,
'preserve_recipients': True,
'view_content_link': True,
'subaccount': 'example-subaccount',
'tracking_domain': 'example.com',
'signing_domain': 'example.com',
'return_path_domain': 'example.com',
'google_analytics_domains': ['example.com/test'],
'google_analytics_campaign': ['UA-00000000-1'],
'merge_language': 'mailchimp',
'async': True,
'ip_pool': 'Pool1',
'invalid': 'invalid',
})
class MandrillBackendDjrillSendDefaultsTests(MandrillBackendMockAPITestCase):
"""Tests backend support for global SEND_DEFAULTS"""
def test_global_options(self):
"""Test that any global settings get passed through
"""
self.message.send()
@override_settings(ANYMAIL_MANDRILL_SEND_DEFAULTS={'subaccount': 'test_subaccount'})
def test_subaccount_setting(self):
"""Global, non-esp_extra version of subaccount default"""
with self.assertWarnsRegex(DeprecationWarning, 'subaccount'):
mail.send_mail('Subject', 'Body', 'from@example.com', ['to@example.com'])
data = self.get_api_call_json()
self.assertEqual(data['message']['from_name'], 'Djrill Test')
self.assertTrue(data['message']['important'])
self.assertTrue(data['message']['auto_text'])
self.assertTrue(data['message']['auto_html'])
self.assertTrue(data['message']['inline_css'])
self.assertTrue(data['message']['url_strip_qs'])
self.assertTrue(data['message']['preserve_recipients'])
self.assertTrue(data['message']['view_content_link'])
self.assertEqual(data['message']['subaccount'], 'example-subaccount')
self.assertEqual(data['message']['tracking_domain'], 'example.com')
self.assertEqual(data['message']['signing_domain'], 'example.com')
self.assertEqual(data['message']['return_path_domain'], 'example.com')
self.assertEqual(data['message']['google_analytics_domains'], ['example.com/test'])
self.assertEqual(data['message']['google_analytics_campaign'], ['UA-00000000-1'])
self.assertEqual(data['message']['merge_language'], 'mailchimp')
self.assertFalse('recipient_metadata' in data['message'])
# Options at top level of api params (not in message dict):
self.assertTrue(data['async'])
self.assertEqual(data['ip_pool'], 'Pool1')
# Option that shouldn't be added
self.assertFalse('invalid' in data['message'])
self.assertEqual(data['message']['subaccount'], "test_subaccount")
def test_global_options_override(self):
"""Test that manually settings options overrides global settings
"""
self.message.from_name = "override"
self.message.important = False
self.message.auto_text = False
self.message.auto_html = False
self.message.inline_css = False
self.message.url_strip_qs = False
self.message.preserve_recipients = False
self.message.view_content_link = False
self.message.subaccount = "override"
self.message.tracking_domain = "override.example.com"
self.message.signing_domain = "override.example.com"
self.message.return_path_domain = "override.example.com"
self.message.google_analytics_domains = ['override.example.com']
self.message.google_analytics_campaign = ['UA-99999999-1']
self.message.merge_language = 'handlebars'
self.message.async = False
self.message.ip_pool = "Bulk Pool"
self.message.send()
@override_settings(ANYMAIL_MANDRILL_SEND_DEFAULTS={'subaccount': 'global_setting_subaccount'})
def test_subaccount_message_overrides_setting(self):
"""Global, non-esp_extra version of subaccount default"""
message = mail.EmailMessage('Subject', 'Body', 'from@example.com', ['to@example.com'])
message.subaccount = "individual_message_subaccount" # should override global setting
with self.assertWarnsRegex(DeprecationWarning, 'subaccount'):
message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['from_name'], 'override')
self.assertFalse(data['message']['important'])
self.assertFalse(data['message']['auto_text'])
self.assertFalse(data['message']['auto_html'])
self.assertFalse(data['message']['inline_css'])
self.assertFalse(data['message']['url_strip_qs'])
self.assertFalse(data['message']['preserve_recipients'])
self.assertFalse(data['message']['view_content_link'])
self.assertEqual(data['message']['subaccount'], 'override')
self.assertEqual(data['message']['tracking_domain'], 'override.example.com')
self.assertEqual(data['message']['signing_domain'], 'override.example.com')
self.assertEqual(data['message']['return_path_domain'], 'override.example.com')
self.assertEqual(data['message']['google_analytics_domains'], ['override.example.com'])
self.assertEqual(data['message']['google_analytics_campaign'], ['UA-99999999-1'])
self.assertEqual(data['message']['merge_language'], 'handlebars')
# Options at top level of api params (not in message dict):
self.assertFalse(data['async'])
self.assertEqual(data['ip_pool'], 'Bulk Pool')
class MandrillBackendDjrillTemplateTests(MandrillBackendMockAPITestCase):
"""Test backend support for ESP templating features"""
# Holdovers from Djrill, until we design Anymail's normalized esp-template support
def test_merge_language(self):
self.message.merge_language = "mailchimp"
self.message.send()
data = self.get_api_call_json()
self.assertEqual(data['message']['merge_language'], "mailchimp")
def test_template_content(self):
self.message.template_content = {
'HEADLINE': "<h1>Specials Just For *|FNAME|*</h1>",
'OFFER_BLOCK': "<p><em>Half off</em> all fruit</p>"
}
self.message.send()
data = self.get_api_call_json()
# Anymail expands simple python dicts into the more-verbose name/content
# structures the Mandrill API uses
self.assertEqual(data['template_content'],
[{'name': "HEADLINE",
'content': "<h1>Specials Just For *|FNAME|*</h1>"},
{'name': "OFFER_BLOCK",
'content': "<p><em>Half off</em> all fruit</p>"}])
self.assertEqual(data['message']['subaccount'], "individual_message_subaccount")