Handle bcc as Mandrill bcc_address, rather than additional to address

This commit is contained in:
medmunds
2013-01-12 10:26:42 -08:00
parent 8f9afdff7e
commit 8de6b218b9
3 changed files with 50 additions and 17 deletions

View File

@@ -138,11 +138,16 @@ Djrill supports most of the functionality of Django's `EmailMessage`_ and
"image/\*", or "application/pdf" (since that is all Mandrill allows). Any "image/\*", or "application/pdf" (since that is all Mandrill allows). Any
other attachment types will raise a ``djrill.NotSupportedByMandrillError`` other attachment types will raise a ``djrill.NotSupportedByMandrillError``
exception when you attempt to send the message. exception when you attempt to send the message.
* Djrill treats all cc and bcc recipients as if they were additional "to" * Djrill treats all "cc" recipients as if they were additional "to" addresses.
addresses. (Mandrill does not distinguish cc, and only allows a single bcc -- (Mandrill does not distinguish "cc" from "to".) Note that you will also need
which Djrill doesn't use. *Caution:* depending on the ``preserve_recipients`` to set ``preserve_recipients`` True if you want each recipient to see the
setting, this could result in exposing bcc addresses to all recipients. It's other recipients listed in the email headers.
probably best to just avoid bcc.) * Mandrill does not permit more than one "bcc" address. Djrill raises
``djrill.NotSupportedByMandrillError`` if you attempt to send a message with
multiple bcc's. (Mandrill's bcc option seems intended primarily for logging.
To send a single message to multiple recipients without exposing their
email addresses to each other, simply include them all in the "to" list and
leave ``preserve_recipients`` set to False.)
* All email addresses (from, to, cc) can be simple ("email@example.com") or * All email addresses (from, to, cc) can be simple ("email@example.com") or
can include a display name ("Real Name <email@example.com>"). can include a display name ("Real Name <email@example.com>").
* The ``from_email`` must be in one of the approved sending domains in your * The ``from_email`` must be in one of the approved sending domains in your

View File

@@ -105,10 +105,11 @@ class DjrillBackend(BaseEmailBackend):
sender = sanitize_address(message.from_email, message.encoding) sender = sanitize_address(message.from_email, message.encoding)
from_name, from_email = parseaddr(sender) from_name, from_email = parseaddr(sender)
recipients = [parseaddr(sanitize_address(addr, message.encoding)) recipients = message.to + message.cc # message.recipients() w/o bcc
for addr in message.recipients()] parsed_rcpts = [parseaddr(sanitize_address(addr, message.encoding))
for addr in recipients]
to_list = [{"email": to_email, "name": to_name} to_list = [{"email": to_email, "name": to_name}
for (to_name, to_email) in recipients] for (to_name, to_email) in parsed_rcpts]
msg_dict = { msg_dict = {
"text": message.body, "text": message.body,
@@ -119,6 +120,15 @@ class DjrillBackend(BaseEmailBackend):
if from_name: if from_name:
msg_dict["from_name"] = from_name msg_dict["from_name"] = from_name
if len(message.bcc) == 1:
bcc = message.bcc[0]
_, bcc_addr = parseaddr(sanitize_address(bcc, message.encoding))
msg_dict['bcc_address'] = bcc_addr
elif len(message.bcc) > 1:
raise NotSupportedByMandrillError(
"Too many bcc addresses (%d) - Mandrill only allows one"
% len(message.bcc))
if message.extra_headers: if message.extra_headers:
for k in message.extra_headers.keys(): for k in message.extra_headers.keys():
if k != "Reply-To" and not k.startswith("X-"): if k != "Reply-To" and not k.startswith("X-"):

View File

@@ -35,22 +35,32 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
(Test both sender and recipient addresses) (Test both sender and recipient addresses)
""" """
mail.send_mail('Subject', 'Message', 'From Name <from@example.com>', msg = mail.EmailMessage('Subject', 'Message',
['Recipient #1 <to1@example.com>', 'to2@example.com']) 'From Name <from@example.com>',
['Recipient #1 <to1@example.com>', 'to2@example.com'],
cc=['Carbon Copy <cc1@example.com>', 'cc2@example.com'],
bcc=['Blind Copy <bcc@example.com>'])
msg.send()
data = self.get_api_call_data() data = self.get_api_call_data()
self.assertEqual(data['message']['from_name'], "From Name") self.assertEqual(data['message']['from_name'], "From Name")
self.assertEqual(data['message']['from_email'], "from@example.com") self.assertEqual(data['message']['from_email'], "from@example.com")
self.assertEqual(len(data['message']['to']), 2) self.assertEqual(len(data['message']['to']), 4)
self.assertEqual(data['message']['to'][0]['name'], "Recipient #1") self.assertEqual(data['message']['to'][0]['name'], "Recipient #1")
self.assertEqual(data['message']['to'][0]['email'], "to1@example.com") self.assertEqual(data['message']['to'][0]['email'], "to1@example.com")
self.assertEqual(data['message']['to'][1]['name'], "") self.assertEqual(data['message']['to'][1]['name'], "")
self.assertEqual(data['message']['to'][1]['email'], "to2@example.com") self.assertEqual(data['message']['to'][1]['email'], "to2@example.com")
self.assertEqual(data['message']['to'][2]['name'], "Carbon Copy")
self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com")
self.assertEqual(data['message']['to'][3]['name'], "")
self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com")
# Mandrill only supports email, not name, for bcc:
self.assertEqual(data['message']['bcc_address'], "bcc@example.com")
def test_email_message(self): def test_email_message(self):
email = mail.EmailMessage('Subject', 'Body goes here', email = mail.EmailMessage('Subject', 'Body goes here',
'from@example.com', 'from@example.com',
['to1@example.com', 'Also To <to2@example.com>'], ['to1@example.com', 'Also To <to2@example.com>'],
bcc=['bcc1@example.com', 'Also BCC <bcc2@example.com>'], bcc=['bcc@example.com'],
cc=['cc1@example.com', 'Also CC <cc2@example.com>'], cc=['cc1@example.com', 'Also CC <cc2@example.com>'],
headers={'Reply-To': 'another@example.com', headers={'Reply-To': 'another@example.com',
'X-MyHeader': 'my value'}) 'X-MyHeader': 'my value'})
@@ -62,16 +72,15 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
self.assertEqual(data['message']['from_email'], "from@example.com") self.assertEqual(data['message']['from_email'], "from@example.com")
self.assertEqual(data['message']['headers'], self.assertEqual(data['message']['headers'],
{ 'Reply-To': 'another@example.com', 'X-MyHeader': 'my value' }) { 'Reply-To': 'another@example.com', 'X-MyHeader': 'my value' })
# Mandrill doesn't have a notion of cc, and only allows a single bcc. # Mandrill doesn't have a notion of cc.
# Djrill just treats cc and bcc as though they were "to" addresses, # Djrill just treats cc as additional "to" addresses,
# which may or may not be what you want. # which may or may not be what you want.
self.assertEqual(len(data['message']['to']), 6) self.assertEqual(len(data['message']['to']), 4)
self.assertEqual(data['message']['to'][0]['email'], "to1@example.com") self.assertEqual(data['message']['to'][0]['email'], "to1@example.com")
self.assertEqual(data['message']['to'][1]['email'], "to2@example.com") self.assertEqual(data['message']['to'][1]['email'], "to2@example.com")
self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com") self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com")
self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com") self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com")
self.assertEqual(data['message']['to'][4]['email'], "bcc1@example.com") self.assertEqual(data['message']['bcc_address'], "bcc@example.com")
self.assertEqual(data['message']['to'][5]['email'], "bcc2@example.com")
def test_html_message(self): def test_html_message(self):
text_content = 'This is an important message.' text_content = 'This is an important message.'
@@ -178,6 +187,14 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
mimetype="application/vnd.ms-powerpoint") mimetype="application/vnd.ms-powerpoint")
msg.send() msg.send()
def test_bcc_errors(self):
# Mandrill only allows a single bcc address
with self.assertRaises(NotSupportedByMandrillError):
msg = mail.EmailMessage('Subject', 'Body',
'from@example.com', ['to@example.com'],
bcc=['bcc1@example.com>', 'bcc2@example.com'])
msg.send()
def test_mandrill_api_failure(self): def test_mandrill_api_failure(self):
self.mock_post.return_value = self.MockResponse(status_code=400) self.mock_post.return_value = self.MockResponse(status_code=400)
with self.assertRaises(MandrillAPIError): with self.assertRaises(MandrillAPIError):
@@ -295,6 +312,7 @@ class DjrillMandrillFeatureTests(DjrillBackendMockAPITestCase):
self.assert_mandrill_called("/messages/send.json") self.assert_mandrill_called("/messages/send.json")
data = self.get_api_call_data() data = self.get_api_call_data()
self.assertFalse('from_name' in data['message']) self.assertFalse('from_name' in data['message'])
self.assertFalse('bcc_address' in data['message'])
self.assertFalse('track_opens' in data['message']) self.assertFalse('track_opens' in data['message'])
self.assertFalse('track_clicks' in data['message']) self.assertFalse('track_clicks' in data['message'])
self.assertFalse('auto_text' in data['message']) self.assertFalse('auto_text' in data['message'])