fix: add is_enabled()
method for email handler, to check per type
also add some more descriptive errors when email template not found, body empty
This commit is contained in:
parent
089d9d7ec6
commit
902412322e
|
@ -191,6 +191,17 @@ Glossary
|
||||||
|
|
||||||
Default is :class:`~wuttjamaican.email.handler.EmailHandler`.
|
Default is :class:`~wuttjamaican.email.handler.EmailHandler`.
|
||||||
|
|
||||||
|
email key
|
||||||
|
String idenfier for a certain :term:`email type`. Each email key
|
||||||
|
must be unique across the app, so the correct template files and
|
||||||
|
other settings are used when sending etc.
|
||||||
|
|
||||||
|
email type
|
||||||
|
The :term:`app` is capable of sending many types of emails,
|
||||||
|
e.g. daily reports, alerts of various kinds etc. Each "type" of
|
||||||
|
email then will have its own template(s) and sender/recipient
|
||||||
|
settings etc. See also :term:`email key`.
|
||||||
|
|
||||||
entry point
|
entry point
|
||||||
This refers to a "setuptools-style" entry point specifically,
|
This refers to a "setuptools-style" entry point specifically,
|
||||||
which is a mechanism used to register "plugins" and the like.
|
which is a mechanism used to register "plugins" and the like.
|
||||||
|
|
|
@ -277,6 +277,52 @@ class EmailHandler(GenericHandler):
|
||||||
except TopLevelLookupException:
|
except TopLevelLookupException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def is_enabled(self, key):
|
||||||
|
"""
|
||||||
|
Returns flag indicating whether the given email type is
|
||||||
|
"enabled" - i.e. whether it should ever be sent out (enabled)
|
||||||
|
or always suppressed (disabled).
|
||||||
|
|
||||||
|
All email types are enabled by default, unless config says
|
||||||
|
otherwise; e.g. to disable ``foo`` emails:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[wutta.email]
|
||||||
|
|
||||||
|
# nb. this is fallback if specific type is not configured
|
||||||
|
default.enabled = true
|
||||||
|
|
||||||
|
# this disables 'foo' but e.g 'bar' is still enabled per default above
|
||||||
|
foo.enabled = false
|
||||||
|
|
||||||
|
In a development setup you may want a reverse example, where
|
||||||
|
all emails are disabled by default but you can turn on just
|
||||||
|
one type for testing:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[wutta.email]
|
||||||
|
|
||||||
|
# do not send any emails unless explicitly enabled
|
||||||
|
default.enabled = false
|
||||||
|
|
||||||
|
# turn on 'bar' for testing
|
||||||
|
bar.enabled = true
|
||||||
|
|
||||||
|
See also :meth:`sending_is_enabled()` which is more of a
|
||||||
|
master shutoff switch.
|
||||||
|
|
||||||
|
:param key: Unique identifier for the email type.
|
||||||
|
|
||||||
|
:returns: True if this email type is enabled, otherwise false.
|
||||||
|
"""
|
||||||
|
for key in set([key, 'default']):
|
||||||
|
enabled = self.config.get_bool(f'{self.config.appname}.email.{key}.enabled')
|
||||||
|
if enabled is not None:
|
||||||
|
return enabled
|
||||||
|
return True
|
||||||
|
|
||||||
def deliver_message(self, message, sender=None, recips=None):
|
def deliver_message(self, message, sender=None, recips=None):
|
||||||
"""
|
"""
|
||||||
Deliver a message via SMTP smarthost.
|
Deliver a message via SMTP smarthost.
|
||||||
|
@ -368,17 +414,25 @@ class EmailHandler(GenericHandler):
|
||||||
"""
|
"""
|
||||||
Send an email message.
|
Send an email message.
|
||||||
|
|
||||||
This method can send a ``message`` you provide, or it can
|
This method can send a message you provide, or it can
|
||||||
construct one automatically from key / config / templates.
|
construct one automatically from key / config / templates.
|
||||||
|
|
||||||
:param key: Indicates which "type" of automatic email to send.
|
The most common use case is assumed to be the latter, where
|
||||||
|
caller does not provide the message proper, but specifies key
|
||||||
|
and context so the message is auto-created. In that case this
|
||||||
|
method will also check :meth:`is_enabled()` and skip the
|
||||||
|
sending if that returns false.
|
||||||
|
|
||||||
|
:param key: When auto-creating a message, this is the
|
||||||
|
:term:`email key` identifying the type of email to send.
|
||||||
Used to lookup config settings and template files.
|
Used to lookup config settings and template files.
|
||||||
|
|
||||||
:param context: Context dict for rendering automatic email
|
:param context: Context dict for rendering automatic email
|
||||||
template(s).
|
template(s).
|
||||||
|
|
||||||
:param message: Optional pre-built message instance, to send
|
:param message: Optional pre-built message instance, to send
|
||||||
as-is.
|
as-is. If specified, nothing about the message will be
|
||||||
|
auto-assigned from config.
|
||||||
|
|
||||||
:param sender: Optional sender address for the
|
:param sender: Optional sender address for the
|
||||||
message/delivery.
|
message/delivery.
|
||||||
|
@ -415,9 +469,27 @@ class EmailHandler(GenericHandler):
|
||||||
:meth:`make_auto_message()`. So, not used if you provide
|
:meth:`make_auto_message()`. So, not used if you provide
|
||||||
the ``message``.
|
the ``message``.
|
||||||
"""
|
"""
|
||||||
|
if key and not self.is_enabled(key):
|
||||||
|
log.debug("skipping disabled email: %s", key)
|
||||||
|
return
|
||||||
|
|
||||||
if message is None:
|
if message is None:
|
||||||
|
if not key:
|
||||||
|
raise ValueError("must specify email key (and/or message object)")
|
||||||
|
|
||||||
|
# auto-create message from key + context
|
||||||
if sender:
|
if sender:
|
||||||
kwargs['sender'] = sender
|
kwargs['sender'] = sender
|
||||||
message = self.make_auto_message(key, context, **kwargs)
|
message = self.make_auto_message(key, context, **kwargs)
|
||||||
|
if not (message.txt_body or message.html_body):
|
||||||
|
raise RuntimeError(f"message (type: {key}) has no body - "
|
||||||
|
"perhaps template file not found?")
|
||||||
|
|
||||||
|
if not (message.txt_body or message.html_body):
|
||||||
|
if key:
|
||||||
|
msg = f"message (type: {key}) has no body content"
|
||||||
|
else:
|
||||||
|
msg = "message has no body content"
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
self.deliver_message(message, recips=recips)
|
self.deliver_message(message, recips=recips)
|
||||||
|
|
|
@ -280,6 +280,22 @@ class TestEmailHandler(TestCase):
|
||||||
body = handler.get_auto_html_body('test_foo')
|
body = handler.get_auto_html_body('test_foo')
|
||||||
self.assertEqual(body, '<p>hello from foo html template</p>\n')
|
self.assertEqual(body, '<p>hello from foo html template</p>\n')
|
||||||
|
|
||||||
|
def test_is_enabled(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# enabled by default
|
||||||
|
self.assertTrue(handler.is_enabled('default'))
|
||||||
|
self.assertTrue(handler.is_enabled('foo'))
|
||||||
|
|
||||||
|
# specific type disabled
|
||||||
|
self.config.setdefault('wutta.email.foo.enabled', 'false')
|
||||||
|
self.assertFalse(handler.is_enabled('foo'))
|
||||||
|
|
||||||
|
# default is disabled
|
||||||
|
self.assertTrue(handler.is_enabled('bar'))
|
||||||
|
self.config.setdefault('wutta.email.default.enabled', 'false')
|
||||||
|
self.assertFalse(handler.is_enabled('bar'))
|
||||||
|
|
||||||
def test_deliver_message(self):
|
def test_deliver_message(self):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
@ -387,24 +403,56 @@ class TestEmailHandler(TestCase):
|
||||||
self.assertTrue(handler.sending_is_enabled())
|
self.assertTrue(handler.sending_is_enabled())
|
||||||
|
|
||||||
def test_send_email(self):
|
def test_send_email(self):
|
||||||
with patch.object(mod.EmailHandler, 'deliver_message') as deliver_message:
|
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
with patch.object(handler, 'deliver_message') as deliver_message:
|
||||||
|
|
||||||
# deliver_message() is called
|
# specify message w/ no body
|
||||||
handler.send_email('foo', sender='bob@example.com', to='sally@example.com',
|
|
||||||
txt_body='hello world')
|
|
||||||
deliver_message.assert_called_once()
|
|
||||||
|
|
||||||
# make_auto_message() called only if needed
|
|
||||||
with patch.object(handler, 'make_auto_message') as make_auto_message:
|
|
||||||
|
|
||||||
msg = handler.make_message()
|
msg = handler.make_message()
|
||||||
handler.send_email(message=msg)
|
self.assertRaises(ValueError, handler.send_email, message=msg)
|
||||||
make_auto_message.assert_not_called()
|
self.assertFalse(deliver_message.called)
|
||||||
|
|
||||||
handler.send_email('foo', sender='bob@example.com', to='sally@example.com',
|
# again, but also specify key
|
||||||
txt_body='hello world')
|
msg = handler.make_message()
|
||||||
make_auto_message.assert_called_once_with('foo', {},
|
self.assertRaises(ValueError, handler.send_email, 'foo', message=msg)
|
||||||
sender='bob@example.com',
|
self.assertFalse(deliver_message.called)
|
||||||
to='sally@example.com',
|
|
||||||
txt_body='hello world')
|
# specify complete message
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
msg = handler.make_message(txt_body="hello world")
|
||||||
|
handler.send_email(message=msg)
|
||||||
|
deliver_message.assert_called_once_with(msg, recips=None)
|
||||||
|
|
||||||
|
# again, but also specify key
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
msg = handler.make_message(txt_body="hello world")
|
||||||
|
handler.send_email('foo', message=msg)
|
||||||
|
deliver_message.assert_called_once_with(msg, recips=None)
|
||||||
|
|
||||||
|
# no key, no message
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
self.assertRaises(ValueError, handler.send_email)
|
||||||
|
|
||||||
|
# auto-create message w/ no template
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
self.assertRaises(RuntimeError, handler.send_email, 'foo', sender='foo@example.com')
|
||||||
|
self.assertFalse(deliver_message.called)
|
||||||
|
|
||||||
|
# auto create w/ body
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
handler.send_email('foo', sender='foo@example.com', txt_body="hello world")
|
||||||
|
self.assertTrue(deliver_message.called)
|
||||||
|
|
||||||
|
# type is disabled
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
self.config.setdefault('wutta.email.foo.enabled', False)
|
||||||
|
handler.send_email('foo', sender='foo@example.com', txt_body="hello world")
|
||||||
|
self.assertFalse(deliver_message.called)
|
||||||
|
|
||||||
|
# default is disabled
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
handler.send_email('bar', sender='bar@example.com', txt_body="hello world")
|
||||||
|
self.assertTrue(deliver_message.called)
|
||||||
|
deliver_message.reset_mock()
|
||||||
|
self.config.setdefault('wutta.email.default.enabled', False)
|
||||||
|
handler.send_email('bar', sender='bar@example.com', txt_body="hello world")
|
||||||
|
self.assertFalse(deliver_message.called)
|
||||||
|
|
Loading…
Reference in a new issue