From 9c2318ee52b70437cd4e834539d20ed00194280d Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Wed, 4 Nov 2015 17:08:18 +0100 Subject: Add error handling to MailSender - Issue #499 --- service/pixelated/adapter/services/mail_sender.py | 27 +++++++++++++++++++++- .../test/unit/adapter/services/test_mail_sender.py | 17 +++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'service') diff --git a/service/pixelated/adapter/services/mail_sender.py b/service/pixelated/adapter/services/mail_sender.py index faf3d228..4ffb76b0 100644 --- a/service/pixelated/adapter/services/mail_sender.py +++ b/service/pixelated/adapter/services/mail_sender.py @@ -32,14 +32,33 @@ class SMTPDownException(Exception): NOT_NEEDED = None +class MailSenderException(Exception): + + def __init__(self, message, email_error_map): + super(MailSenderException, self).__init__(message, email_error_map) + self.email_error_map = email_error_map + + class MailSender(object): def __init__(self, smtp_config, keymanager): self._smtp_config = smtp_config self._keymanager = keymanager + @defer.inlineCallbacks def sendmail(self, mail): recipients = flatten([mail.to, mail.cc, mail.bcc]) + + results = yield self._send_mail_to_all_recipients(mail, recipients) + all_succeeded = reduce(lambda a, b: a and b, [r[0] for r in results]) + + if not all_succeeded: + error_map = self._build_error_map(recipients, results) + raise MailSenderException('Failed to send mail to all recipients', error_map) + + defer.returnValue(all_succeeded) + + def _send_mail_to_all_recipients(self, mail, recipients): outgoing_mail = self._create_outgoing_mail() deferreds = [] @@ -47,7 +66,13 @@ class MailSender(object): smtp_recipient = self._create_twisted_smtp_recipient(recipient) deferreds.append(outgoing_mail.send_message(mail.to_smtp_format(), smtp_recipient)) - return defer.gatherResults(deferreds) + return defer.DeferredList(deferreds, fireOnOneErrback=False, consumeErrors=True) + + def _build_error_map(self, recipients, results): + error_map = {} + for email, error in [(recipients[idx], r[1]) for idx, r in enumerate(results)]: + error_map[email] = error + return error_map def _create_outgoing_mail(self): return OutgoingMail(str(self._smtp_config.account_email), diff --git a/service/test/unit/adapter/services/test_mail_sender.py b/service/test/unit/adapter/services/test_mail_sender.py index 20ddc2c6..38da726b 100644 --- a/service/test/unit/adapter/services/test_mail_sender.py +++ b/service/test/unit/adapter/services/test_mail_sender.py @@ -18,7 +18,8 @@ from twisted.mail.smtp import User from twisted.trial import unittest from mockito import mock, when, verify, any, unstub -from pixelated.adapter.services.mail_sender import LocalSmtpMailSender, SMTPDownException, MailSender +from pixelated.adapter.services.mail_sender import LocalSmtpMailSender, SMTPDownException, MailSender, \ + MailSenderException from pixelated.adapter.model.mail import InputMail from pixelated.bitmask_libraries.smtp import LeapSMTPConfig from pixelated.support.functional import flatten @@ -63,6 +64,20 @@ class MailSenderTest(unittest.TestCase): for recipient in flatten([input_mail.to, input_mail.cc, input_mail.bcc]): verify(OutgoingMail).send_message(any(), TwistedSmtpUserCapture(recipient)) + @defer.inlineCallbacks + def test_problem_with_email_raises_exception(self): + sender = MailSender(self._smtp_config, self._keymanager_mock) + input_mail = InputMail.from_dict(mail_dict()) + + when(OutgoingMail).send_message(any(), any()).thenAnswer(lambda: defer.fail(Exception('pretend something went wrong'))) + + try: + yield sender.sendmail(input_mail) + self.fail('Exception expected!') + except MailSenderException, e: + for recipient in flatten([input_mail.to, input_mail.cc, input_mail.bcc]): + self.assertTrue(recipient in e.email_error_map) + class LocalSmtpMailSenderTest(unittest.TestCase): def setUp(self): -- cgit v1.2.3