Class: Posthaste::DeliveryMethod

Inherits:
Object
  • Object
show all
Defined in:
lib/posthaste/delivery_method.rb

Overview

An ActionMailer delivery method.

This is ActionMailer's own contract and nothing more: add_delivery_method registers a class, wrap_delivery_behavior instantiates it with the settings hash, and Mail::Message#do_delivery calls deliver!(mail). That is why perform_deliveries, raise_delivery_errors, deliver_later, the :test delivery method, assert_emails and every interceptor and observer keep working untouched — none of them are our business, and a hand-rolled sender that bypassed deliver! would break all of them.

Constant Summary collapse

SETTING_KEYS =
%i[
  api_key base_url stream tags metadata open_timeout read_timeout
  max_retries max_retry_delay transport logger on_warning
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(settings = {}) ⇒ DeliveryMethod

Returns a new instance of DeliveryMethod.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/posthaste/delivery_method.rb', line 25

def initialize(settings = {})
  settings = normalize(settings)
  unknown = settings.keys - SETTING_KEYS
  unless unknown.empty?
    raise ConfigurationError.new(
      "unknown posthaste_settings: #{unknown.map(&:inspect).join(', ')}. " \
      "Known settings are: #{SETTING_KEYS.map(&:inspect).join(', ')}.",
      type: 'configuration_error'
    )
  end

  # Held in one place, and never in a hash that some other object's
  # `#inspect` might print.
  @api_key = (settings[:api_key] || ENV['POSTHASTE_API_KEY']).to_s
  if @api_key.empty?
    raise ConfigurationError.new(
      'no Posthaste API key. Set `config.action_mailer.posthaste_settings = ' \
      '{ api_key: … }` or the POSTHASTE_API_KEY environment variable.',
      type: 'configuration_error'
    )
  end

  @logger = settings[:logger]
  @on_warning = settings[:on_warning]
  @defaults = {
    stream: settings[:stream],
    tags: settings[:tags],
    metadata: settings[:metadata]
  }.compact

  @client = HttpClient.new(
    api_key: @api_key,
    base_url: settings[:base_url] || DEFAULT_BASE_URL,
    transport: settings[:transport],
    max_retries: settings.fetch(:max_retries, HttpClient::DEFAULT_MAX_RETRIES),
    max_retry_delay: settings.fetch(:max_retry_delay, HttpClient::DEFAULT_MAX_RETRY_DELAY),
    open_timeout: settings.fetch(:open_timeout, 10),
    read_timeout: settings.fetch(:read_timeout, 30)
  )
end

Instance Method Details

#deliver!(mail) ⇒ Object

ActionMailer's delivery-method contract. Returns the Result, and attaches it to the message, because deliver_now hands the caller back the Mail::Message and not this return value.



69
70
71
72
73
74
75
76
77
# File 'lib/posthaste/delivery_method.rb', line 69

def deliver!(mail)
  payload, warnings = MessageMapper.new(@defaults).call(mail)
  warnings.each { |warning| report(warning) }

  body = @client.send_email(payload, idempotent: payload.key?(:idempotencyKey))
  result = Result.new(body, mapping_warnings: warnings)
  mail.instance_variable_set(:@posthaste_result, result)
  result
end

#inspectObject Also known as: to_s

settings is exposed by ActionMailer as posthaste_settings, which means the key is reachable from Rails.application.config whatever this gem does. What this gem can control is that IT never prints it — and the default #inspect prints every instance variable, which is how a key reaches an error reporter without anybody having logged it.



84
85
86
87
# File 'lib/posthaste/delivery_method.rb', line 84

def inspect
  "#<Posthaste::DeliveryMethod api_key=#{Redaction.describe_key(@api_key).inspect} " \
    "client=#{@client.inspect}>"
end