Mailing Backends

A mailer, member database, and so much more, for digital activism.

Mailing Backends

Identity currently supports 4 backends for sending emails:

Which method you use is configured in your organizations settings.yml file.

Mailing Backend Strategy Configuration

The Identity code now supports different MailBackendStrategy implementations. These may dynamically choose between a number of backends at runtime.

The current Strategies are:

In future there could be other strategies, some ideas are:

Configuration

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 MailBackendStrategys 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'] %>

How it works

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:

Replies to emails

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.

Possible Future Changes

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.