fix: add auto-prefix for message subject when sending email
This commit is contained in:
parent
5de9c15bbd
commit
8a0830b35d
2 changed files with 203 additions and 26 deletions
|
|
@ -124,6 +124,14 @@ class EmailSetting: # pylint: disable=too-few-public-methods
|
|||
|
||||
default_subject = None
|
||||
|
||||
default_prefix = None
|
||||
"""
|
||||
Default subject prefix for emails of this type.
|
||||
|
||||
Calling code should not access this directly, but instead use
|
||||
:meth:`get_default_prefix()` .
|
||||
"""
|
||||
|
||||
fallback_key = None
|
||||
"""
|
||||
Optional fallback key to use for config/template lookup, if
|
||||
|
|
@ -147,6 +155,20 @@ class EmailSetting: # pylint: disable=too-few-public-methods
|
|||
"""
|
||||
return self.__class__.__doc__.strip()
|
||||
|
||||
def get_default_prefix(self):
|
||||
"""
|
||||
This returns the default subject prefix, for sending emails of
|
||||
this type.
|
||||
|
||||
Default logic here returns :attr:`default_prefix` as-is.
|
||||
|
||||
This method will often return ``None`` in which case the
|
||||
global default prefix is used.
|
||||
|
||||
:returns: Default subject prefix as string, or ``None``
|
||||
"""
|
||||
return self.default_prefix
|
||||
|
||||
def get_default_subject(self):
|
||||
"""
|
||||
This must return the default subject, for sending emails of
|
||||
|
|
@ -434,7 +456,14 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
return Message(**kwargs)
|
||||
|
||||
def make_auto_message(
|
||||
self, key, context=None, default_subject=None, fallback_key=None, **kwargs
|
||||
self,
|
||||
key,
|
||||
context=None,
|
||||
default_subject=None,
|
||||
prefix_subject=True,
|
||||
default_prefix=None,
|
||||
fallback_key=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Make a new email message using config to determine its
|
||||
|
|
@ -455,6 +484,12 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
:param default_subject: Optional :attr:`~Message.subject`
|
||||
template/string to use, if config does not specify one.
|
||||
|
||||
:param prefix_subject: Boolean indicating the message subject
|
||||
should be auto-prefixed.
|
||||
|
||||
:param default_prefix: Default subject prefix to use if none
|
||||
is configured.
|
||||
|
||||
:param fallback_key: Optional fallback :term:`email key` to
|
||||
use for config/template lookup, if nothing is found for
|
||||
``key``.
|
||||
|
|
@ -483,7 +518,12 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
kwargs["sender"] = self.get_auto_sender(key)
|
||||
if "subject" not in kwargs:
|
||||
kwargs["subject"] = self.get_auto_subject(
|
||||
key, context, default=default_subject, fallback_key=fallback_key
|
||||
key,
|
||||
context,
|
||||
default=default_subject,
|
||||
prefix=prefix_subject,
|
||||
default_prefix=default_prefix,
|
||||
fallback_key=fallback_key,
|
||||
)
|
||||
if "to" not in kwargs:
|
||||
kwargs["to"] = self.get_auto_to(key)
|
||||
|
|
@ -567,16 +607,19 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
key,
|
||||
context=None,
|
||||
rendered=True,
|
||||
setting=None,
|
||||
default=None,
|
||||
fallback_key=None,
|
||||
setting=None,
|
||||
prefix=True,
|
||||
default_prefix=None,
|
||||
):
|
||||
"""
|
||||
Returns automatic :attr:`~wuttjamaican.email.Message.subject`
|
||||
line for a message, as determined by config.
|
||||
|
||||
This calls :meth:`get_auto_subject_template()` and then
|
||||
(usually) renders the result using the given context.
|
||||
(usually) renders the result using the given context, and adds
|
||||
the :meth:`get_auto_subject_prefix()`.
|
||||
|
||||
:param key: Key for the :term:`email type`. See also the
|
||||
``fallback_key`` param, below.
|
||||
|
|
@ -588,15 +631,22 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
template will be returned, instead of the final/rendered
|
||||
subject text.
|
||||
|
||||
:param setting: Optional :class:`EmailSetting` class or
|
||||
instance. This is passed along to
|
||||
:meth:`get_auto_subject_template()`.
|
||||
|
||||
:param default: Default subject to use if none is configured.
|
||||
|
||||
:param fallback_key: Optional fallback :term:`email key` to
|
||||
use for config lookup, if nothing is found for ``key``.
|
||||
|
||||
:param setting: Optional :class:`EmailSetting` class or
|
||||
instance. This is passed along to
|
||||
:meth:`get_auto_subject_template()`.
|
||||
|
||||
:param prefix: Boolean indicating the message subject should
|
||||
be auto-prefixed. This is ignored when ``rendered`` param
|
||||
is false.
|
||||
|
||||
:param default_prefix: Default subject prefix to use if none
|
||||
is configured.
|
||||
|
||||
:returns: Final subject text, either "raw" or rendered.
|
||||
"""
|
||||
template = self.get_auto_subject_template(
|
||||
|
|
@ -606,10 +656,18 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
return template
|
||||
|
||||
context = self.get_email_context(key, context)
|
||||
return Template(template).render(**context)
|
||||
subject = Template(template).render(**context)
|
||||
|
||||
if prefix:
|
||||
if prefix := self.get_auto_subject_prefix(
|
||||
key, default=default_prefix, setting=setting, fallback_key=fallback_key
|
||||
):
|
||||
subject = f"{prefix} {subject}"
|
||||
|
||||
return subject
|
||||
|
||||
def get_auto_subject_template(
|
||||
self, key, setting=None, default=None, fallback_key=None
|
||||
self, key, default=None, fallback_key=None, setting=None
|
||||
):
|
||||
"""
|
||||
Returns the template string to use for automatic subject line
|
||||
|
|
@ -623,17 +681,17 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
|
||||
:param key: Key for the :term:`email type`.
|
||||
|
||||
:param default: Default subject to use if none is configured.
|
||||
|
||||
:param fallback_key: Optional fallback :term:`email key` to
|
||||
use for config lookup, if nothing is found for ``key``.
|
||||
|
||||
:param setting: Optional :class:`EmailSetting` class or
|
||||
instance. This may be used to determine the "default"
|
||||
subject if none is configured. You can specify this as an
|
||||
optimization; otherwise it will be fetched if needed via
|
||||
:meth:`get_email_setting()`.
|
||||
|
||||
:param default: Default subject to use if none is configured.
|
||||
|
||||
:param fallback_key: Optional fallback :term:`email key` to
|
||||
use for config lookup, if nothing is found for ``key``.
|
||||
|
||||
:returns: Final subject template, as raw text.
|
||||
"""
|
||||
# prefer configured subject specific to key
|
||||
|
|
@ -664,6 +722,64 @@ class EmailHandler(GenericHandler): # pylint: disable=too-many-public-methods
|
|||
default=self.universal_subject,
|
||||
)
|
||||
|
||||
def get_auto_subject_prefix(
|
||||
self, key, default=None, fallback_key=None, setting=None
|
||||
):
|
||||
"""
|
||||
Returns the string to use for automatic subject prefix, as
|
||||
determined by config. This is called by
|
||||
:meth:`get_auto_subject()`.
|
||||
|
||||
Note that unlike the subject proper, the prefix is just a
|
||||
normal string, not a template.
|
||||
|
||||
Example prefix is ``"[Wutta]"`` - trailing space will be added
|
||||
automatically when applying the prefix to a message subject.
|
||||
|
||||
:param key: The :term:`email key` requested.
|
||||
|
||||
:param default: Default prefix to use if none is configured.
|
||||
|
||||
:param fallback_key: Optional fallback :term:`email key` to
|
||||
use for config lookup, if nothing is found for ``key``.
|
||||
|
||||
:param setting: Optional :class:`EmailSetting` class or
|
||||
instance. This may be used to determine the "default"
|
||||
prefix if none is configured. You can specify this as an
|
||||
optimization; otherwise it will be fetched if needed via
|
||||
:meth:`get_email_setting()`.
|
||||
|
||||
:returns: Final subject prefix string
|
||||
"""
|
||||
|
||||
# prefer configured prefix specific to key
|
||||
if prefix := self.config.get(f"{self.config.appname}.email.{key}.prefix"):
|
||||
return prefix
|
||||
|
||||
# or use caller-specified default, if applicable
|
||||
if default:
|
||||
return default
|
||||
|
||||
# or use fallback key, if provided
|
||||
if fallback_key:
|
||||
if prefix := self.config.get(
|
||||
f"{self.config.appname}.email.{fallback_key}.prefix"
|
||||
):
|
||||
return prefix
|
||||
|
||||
# or prefix from email setting, if defined
|
||||
if not setting:
|
||||
setting = self.get_email_setting(key)
|
||||
if setting:
|
||||
if prefix := setting.get_default_prefix():
|
||||
return prefix
|
||||
|
||||
# fall back to global default
|
||||
return self.config.get(
|
||||
f"{self.config.appname}.email.default.prefix",
|
||||
default=f"[{self.app.get_node_title()}]",
|
||||
)
|
||||
|
||||
def get_auto_to(self, key):
|
||||
"""
|
||||
Returns automatic :attr:`~wuttjamaican.email.Message.to`
|
||||
|
|
|
|||
|
|
@ -31,6 +31,17 @@ class TestEmailSetting(ConfigTestCase):
|
|||
setting = MockSetting(self.config)
|
||||
self.assertEqual(setting.get_description(), "this should be a good test")
|
||||
|
||||
def test_get_default_prefix(self):
|
||||
|
||||
# empty by default
|
||||
setting = mod.EmailSetting(self.config)
|
||||
self.assertIsNone(setting.default_prefix)
|
||||
self.assertIsNone(setting.get_default_prefix())
|
||||
|
||||
# but can override
|
||||
setting.default_prefix = "[foo]"
|
||||
self.assertEqual(setting.get_default_prefix(), "[foo]")
|
||||
|
||||
def test_sample_data(self):
|
||||
setting = mod.EmailSetting(self.config)
|
||||
self.assertEqual(setting.sample_data(), {})
|
||||
|
|
@ -136,6 +147,7 @@ class TestMessage(FileTestCase):
|
|||
|
||||
class mock_foo(mod.EmailSetting):
|
||||
default_subject = "MOCK FOO!"
|
||||
default_prefix = "[mock_foo]"
|
||||
|
||||
def sample_data(self):
|
||||
return {"foo": "mock"}
|
||||
|
|
@ -253,7 +265,7 @@ class TestEmailHandler(ConfigTestCase):
|
|||
self.assertIsInstance(msg, mod.Message)
|
||||
self.assertEqual(msg.key, "foo")
|
||||
self.assertEqual(msg.sender, "root@localhost")
|
||||
self.assertEqual(msg.subject, "Automated message")
|
||||
self.assertEqual(msg.subject, "[WuttJamaican] Automated message")
|
||||
self.assertEqual(msg.to, [])
|
||||
self.assertEqual(msg.cc, [])
|
||||
self.assertEqual(msg.bcc, [])
|
||||
|
|
@ -270,7 +282,7 @@ class TestEmailHandler(ConfigTestCase):
|
|||
self.assertIsInstance(msg, mod.Message)
|
||||
self.assertEqual(msg.key, "foo")
|
||||
self.assertEqual(msg.sender, "bob@example.com")
|
||||
self.assertEqual(msg.subject, "Attention required")
|
||||
self.assertEqual(msg.subject, "[WuttJamaican] Attention required")
|
||||
self.assertEqual(msg.to, [])
|
||||
self.assertEqual(msg.cc, [])
|
||||
self.assertEqual(msg.bcc, [])
|
||||
|
|
@ -287,7 +299,7 @@ class TestEmailHandler(ConfigTestCase):
|
|||
msg = handler.make_auto_message("test_foo")
|
||||
self.assertEqual(msg.key, "test_foo")
|
||||
self.assertEqual(msg.sender, "bob@example.com")
|
||||
self.assertEqual(msg.subject, "hello foo")
|
||||
self.assertEqual(msg.subject, "[WuttJamaican] hello foo")
|
||||
self.assertEqual(msg.to, ["sally@example.com"])
|
||||
self.assertEqual(msg.cc, [])
|
||||
self.assertEqual(msg.bcc, [])
|
||||
|
|
@ -311,7 +323,12 @@ class TestEmailHandler(ConfigTestCase):
|
|||
get_auto_subject.assert_not_called()
|
||||
msg = handler.make_auto_message("foo")
|
||||
get_auto_subject.assert_called_once_with(
|
||||
"foo", {}, default=None, fallback_key=None
|
||||
"foo",
|
||||
{},
|
||||
default=None,
|
||||
prefix=True,
|
||||
default_prefix=None,
|
||||
fallback_key=None,
|
||||
)
|
||||
|
||||
# to
|
||||
|
|
@ -421,36 +438,80 @@ class TestEmailHandler(ConfigTestCase):
|
|||
)
|
||||
self.assertEqual(template, "whatever is clever")
|
||||
|
||||
def test_get_auto_subject_prefix(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# global default
|
||||
prefix = handler.get_auto_subject_prefix("foo")
|
||||
self.assertEqual(prefix, "[WuttJamaican]")
|
||||
|
||||
# can configure alternate global default
|
||||
self.config.setdefault("wutta.email.default.prefix", "[bar]")
|
||||
prefix = handler.get_auto_subject_prefix("foo")
|
||||
self.assertEqual(prefix, "[bar]")
|
||||
|
||||
# can configure just for key
|
||||
self.config.setdefault("wutta.email.foo.prefix", "[foo]")
|
||||
prefix = handler.get_auto_subject_prefix("foo")
|
||||
self.assertEqual(prefix, "[foo]")
|
||||
|
||||
# can configure via fallback_key
|
||||
self.config.setdefault("wutta.email.bar.prefix", "[baz]")
|
||||
prefix = handler.get_auto_subject_prefix("foofoo", fallback_key="bar")
|
||||
self.assertEqual(prefix, "[baz]")
|
||||
|
||||
# EmailSetting can provide default prefix
|
||||
providers = {
|
||||
"wuttatest": MagicMock(email_modules=["tests.test_email"]),
|
||||
}
|
||||
with patch.object(self.app, "providers", new=providers):
|
||||
handler = self.make_handler()
|
||||
prefix = handler.get_auto_subject_prefix("mock_foo")
|
||||
self.assertEqual(prefix, "[mock_foo]")
|
||||
|
||||
# or caller can provide default
|
||||
prefix = handler.get_auto_subject_prefix("mock_foo", default="[zzz]")
|
||||
self.assertEqual(prefix, "[zzz]")
|
||||
|
||||
def test_get_auto_subject(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# global default
|
||||
subject = handler.get_auto_subject("foo")
|
||||
self.assertEqual(subject, "Automated message")
|
||||
self.assertEqual(subject, "[WuttJamaican] Automated message")
|
||||
|
||||
# can configure alternate global default
|
||||
self.config.setdefault("wutta.email.default.subject", "Wutta Message")
|
||||
subject = handler.get_auto_subject("foo")
|
||||
self.assertEqual(subject, "Wutta Message")
|
||||
self.assertEqual(subject, "[WuttJamaican] Wutta Message")
|
||||
|
||||
# caller can provide default subject
|
||||
subject = handler.get_auto_subject("foo", default="whatever is clever")
|
||||
self.assertEqual(subject, "whatever is clever")
|
||||
self.assertEqual(subject, "[WuttJamaican] whatever is clever")
|
||||
|
||||
# can configure just for key
|
||||
self.config.setdefault("wutta.email.foo.subject", "Foo Message")
|
||||
subject = handler.get_auto_subject("foo")
|
||||
self.assertEqual(subject, "Foo Message")
|
||||
self.assertEqual(subject, "[WuttJamaican] Foo Message")
|
||||
|
||||
# proper template is rendered
|
||||
# proper template is rendered..
|
||||
self.config.setdefault("wutta.email.bar.subject", "${foo} Message")
|
||||
subject = handler.get_auto_subject("bar", {"foo": "FOO"})
|
||||
self.assertEqual(subject, "FOO Message")
|
||||
self.assertEqual(subject, "[WuttJamaican] FOO Message")
|
||||
|
||||
# unless we ask it not to
|
||||
# ..unless we ask it not to
|
||||
subject = handler.get_auto_subject("bar", {"foo": "FOO"}, rendered=False)
|
||||
# nb. no prefix for unrendered template
|
||||
self.assertEqual(subject, "${foo} Message")
|
||||
|
||||
# now suppress/override the prefix
|
||||
subject = handler.get_auto_subject("foo")
|
||||
self.assertEqual(subject, "[WuttJamaican] Foo Message")
|
||||
subject = handler.get_auto_subject("foo", prefix=False)
|
||||
self.assertEqual(subject, "Foo Message")
|
||||
subject = handler.get_auto_subject("foo", default_prefix="[foo]")
|
||||
self.assertEqual(subject, "[foo] Foo Message")
|
||||
|
||||
def test_get_auto_recips(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue