A mailer, member database, and so much more, for digital activism.
Identity currently supports 4 backends for sending emails:
Which method you use is configured in your organizations settings.yml
file.
The Identity code now supports different MailBackendStrategy
implementations. These may dynamically choose between a number of backends at runtime.
The current Strategies are:
SingleBackendStrategy
- This is a simple strategy which always uses the same backend.DomainBasedBackendStrategy
- This is a more advanced strategy which checks the domain of the ‘from’ email address, and uses this to choose the backend.In future there could be other strategies, some ideas are:
ProviderLimitBackendStrategy
- Which could send a maximum number of emails using each backend per day/week/month, in order to stay within free usage limits of each provider.FailoverBackendStrategy
- Which ‘fails over’ to a different backend in case of errors on the primary backend.Configure any strategies you may want to use under Settings.email.backend_strategies
. Each strategy must have a unique name, a strategy_class
, and any necessary strategy_options
.
The strategy_class
must be set to the class name of the MailBackendStrategy
you want to use.
The strategy_options
should be configured with the parameters required by the MailBackendStrategy
. These will vary for each backend, and the hash containing these parameters is simply passed to the MailBackendStrategy
s initialize
method.
Once all strategies are configured under Settings.email.backend_strategies
, you choose which one to use with Settings.email.active_backend_strategy_name
. This can be hard-coded in your settings file, or can be set to an ENV VAR if you’d like to be able to switch strategies without a code deployment.
The example below configures two strategies - one which always uses Amazon SES as the backend for sending emails, and another which always uses SendGrid. The configuration is hard-coded to always use the Amazon SES strategy.
email:
active_backend_strategy_name: 'always_aws_ses'
backend_strategies:
always_aws_ses:
strategy_class: 'SingleBackendStrategy'
strategy_options:
backend: 'ses'
options:
access_key_id: <%= ENV['AWS_SES_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SES_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'] %>
region: <%= ENV['AWS_SES_REGION'] || ENV['AWS_REGION'] %>
always_sendgrid:
strategy_class: 'SingleBackendStrategy'
strategy_options:
backend: 'sendgrid'
options:
api_key: <%= ENV['SENDGRID_APIKEY'] %>
The TransactionalMail.send_email
method, which acts as the entry point for sending emails within Identity, calls MailBackendStrategyFactory.get_backend_for_mailing
. MailBackendStrategyFactory
checks the Settings
to initialize the configured MailBackendStrategy
once, and then cache it for use later. Once MailBackendStrategyFactory
has the appropriate MailBackendStrategy
object, it calls the get_backend_for_mailing
on that object which will return a Mailing Backend (Amazon SES, Sendgrid, Mailjet). Finally, TransactionalMail
uses the backend to send the mail.
So in summary, the classes involved are:
MailBackendStrategyFactory
- A single entry point for getting the correct MailBackendStrategy
from anywhere in the code, and for caching the MailBackendStrategy
.MailBackendStrategy
- A strategy for deciding which MailBackend to use for each email sent. 2 current implementations: SingleBackendStrategy
, DomainBasedBackendStrategy
(see above for more detail on each implementation).send_email
method, sending via different providers. 4 current implementations: SendgridApi
, SesMailer
, SmtpApi
and MailjetApi
.TransactionalMail
- A single entry point for sending mail in Identity. Calls MailBackendStrategyFactory
to work out which MailBackendStrategy
, and ultimately which MailBackend, to use to send an email.When using more advanced strategies which may send from multiple different backends or addresses such as the DomainBasedBackendStrategy
, organisations may still want all replies to go to their existing mailbox. Therefore an option AppSetting.emails.default_reply_to
is available, which is respected by all the existing MailBackends. It is also suggested that this configuration option should be respected by any future MailBackends.
Note that this configuration option applies to MailBackends themselves, not to strategies - so this option will be respected even when using the legacy configuration.
1 - Store the backend used when sending a mailing
This would be possible, although may require some restructuring of code as currently the mailing object is not available in TransactionalMail where the decision of Mail Backend is made. It would also prevent one specific mailing being sent by multiple backends (eg. breaking up a very large mailing across multiple backends to stay within free usage limits).
2 - Shift Mail Backends to their own folder
Move SendgridApi, SesMailer, SmtpApi and MailjetApi into their own folder, eg. mail_backends so they are more obviously grouped (they all need to implement the same API).
3 - Possibly get rid of TransactionalMail
This class is now very small, just containing a helper method for sending email. Arguably places which call TransactionalMail.send_email could just call MailBackendStrategyFactory.get_backend_for_mailing(options).send_email(options)
themselves…?
4 - Refactor send_email methods to take a mailing
Some of the Mail Backends, particularly MailjetApi and SendgridApi seem to be more complex than necessary because they take a mail hash rather than a mailing. Some form of easily constructed mail hash is still required for sending ad-hoc transactional emails (like password reminders, samples, etc) but refactoring the definition of this hash, and allowing them to take a mailing object would probably make these methods simpler. This would probably also involved refactoring the MailBackendStrategy implementations so they can lookup a backend for a mailing object too rather than just a mail hash.