Improve MandrillAPIError.__str__

* Include formatted response from Mandrill API (if any)
* Clean up recipient address(es)
This commit is contained in:
medmunds
2015-05-13 15:43:54 -07:00
parent 6798b72b8b
commit 00ddd2f4f6
4 changed files with 44 additions and 8 deletions

View File

@@ -1,3 +1,4 @@
import json
from requests import HTTPError from requests import HTTPError
import warnings import warnings
@@ -14,8 +15,15 @@ class MandrillAPIError(HTTPError):
message = "Mandrill API response %d" % self.status_code message = "Mandrill API response %d" % self.status_code
if self.log_message: if self.log_message:
message += "\n" + self.log_message message += "\n" + self.log_message
if self.response: # Include the Mandrill response, nicely formatted, if possible
message += "\nResponse: " + getattr(self.response, 'content', "") try:
json_response = self.response.json()
message += "\nMandrill response:\n" + json.dumps(json_response, indent=2)
except (AttributeError, KeyError, ValueError): # not JSON = ValueError
try:
message += "\nMandrill response: " + self.response.text
except AttributeError:
pass
return message return message

View File

@@ -137,15 +137,16 @@ class DjrillBackend(BaseEmailBackend):
message.mandrill_response = None message.mandrill_response = None
if not self.fail_silently: if not self.fail_silently:
from_email = msg_dict.get('from_email', None) log_message = "Failed to send a message"
from_message = "" if 'to' in msg_dict:
if from_email: log_message += " to " + ','.join(
from_message = ", from %s" % from_email to['email'] for to in msg_dict.get('to', []) if 'email' in to)
if 'from_email' in msg_dict:
log_message += " from %s" % msg_dict['from_email']
raise MandrillAPIError( raise MandrillAPIError(
status_code=response.status_code, status_code=response.status_code,
response=response, response=response,
log_message="Failed to send a message to %s%s" % log_message=log_message)
(msg_dict['to'], from_message))
return False return False
# add the response from mandrill to the EmailMessage so callers can inspect it # add the response from mandrill to the EmailMessage so callers can inspect it

View File

@@ -308,6 +308,31 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
['to@example.com'], fail_silently=True) ['to@example.com'], fail_silently=True)
self.assertEqual(sent, 0) self.assertEqual(sent, 0)
def test_api_error_includes_details(self):
"""MandrillAPIError should include Mandrill's error message"""
msg = mail.EmailMessage('Subject', 'Body', 'from@example.com', ['to@example.com'])
# JSON error response:
error_response = b"""{
"status": "error",
"code": 12,
"name": "Error_Name",
"message": "Helpful explanation from Mandrill"
}"""
self.mock_post.return_value = self.MockResponse(status_code=400, raw=error_response)
with self.assertRaisesMessage(MandrillAPIError, "Helpful explanation from Mandrill"):
msg.send()
# Non-JSON error response:
self.mock_post.return_value = self.MockResponse(status_code=500, raw=b"Invalid API key")
with self.assertRaisesMessage(MandrillAPIError, "Invalid API key"):
msg.send()
# No content in the error response:
self.mock_post.return_value = self.MockResponse(status_code=502, raw=None)
with self.assertRaises(MandrillAPIError):
msg.send()
class DjrillMandrillFeatureTests(DjrillBackendMockAPITestCase): class DjrillMandrillFeatureTests(DjrillBackendMockAPITestCase):
"""Test Djrill backend support for Mandrill-specific features""" """Test Djrill backend support for Mandrill-specific features"""

View File

@@ -76,6 +76,8 @@ Version 1.4 (development):
(Specifying a :ref:`Reply-To header <message-headers>` (Specifying a :ref:`Reply-To header <message-headers>`
still works, with any version of Django, still works, with any version of Django,
and will override the reply_to param if you use both.) and will override the reply_to param if you use both.)
* Include Mandrill error response in str(MandrillAPIError),
to make errors easier to understand.
* More-helpful exception when using a non-JSON-serializable * More-helpful exception when using a non-JSON-serializable
type in merge_vars and other Djrill message attributes type in merge_vars and other Djrill message attributes
* Deprecation warnings for upcoming 2.0 changes (see above) * Deprecation warnings for upcoming 2.0 changes (see above)