1module EmailDelivery
 
2  # Send a single email to a recipient informing them about a petition that they have signed
 
3  # Implemented as a custom job rather than using action mailers #deliver_later so we can do
 
4  # extra checking before sending the email
 
5
 
6  extend ActiveSupport::Concern
 
7
 
8  PERMANENT_FAILURES = [
 
 9    Net::SMTPFatalError,
 
10    Net::SMTPSyntaxError
 
11  ]
 
 
13  TEMPORARY_FAILURES = [
 
14    Net::SMTPAuthenticationError,
 
15    Net::OpenTimeout,
 
16    Net::SMTPServerBusy,
 
17    Errno::ECONNRESET,
 
18    Errno::ECONNREFUSED,
 
19    Errno::ETIMEDOUT,
 
20    Timeout::Error,
 
21    EOFError,
 
22    SocketError
 
23  ]
 
 
25  included do
 
26    before_perform :set_appsignal_namespace
 
 
28    attr_reader :signature, :timestamp_name, :petition, :requested_at
 
29    queue_as :low_priority
 
 
31    rescue_from *PERMANENT_FAILURES do |exception|
 
32      log_exception(exception)
 
33    end
 
 
35    rescue_from *TEMPORARY_FAILURES do |exception|
 
36      log_exception(exception)
 
37      retry_job
 
38    end
 
39  end
 
  • TooManyStatements - has approx 6 statements » reek
  • Complexity 2 » saikuro
41  def perform(**args)
 
42    @signature = args[:signature]
 
43    @petition = args[:petition]
 
44    @requested_at = args[:requested_at].in_time_zone
 
45    @timestamp_name = args[:timestamp_name]
 
 
47    if can_send_email?
 
48      send_email
 
49      record_email_sent
 
50    end
 
51  end
 
 
53  private
 
  • Complexity 1 » saikuro
55  def log_exception(exception)
 
56    logger.info(log_message(exception))
 
57  end
 
  • Complexity 1 » saikuro
59  def log_message(exception)
 
60    "#{exception.class.name} while sending email for #{self.class.name} to: #{signature.email} for #{petition.action}"
 
61  end
 
  • Complexity 1 » saikuro
63  def can_send_email?
 
64    petition_has_not_been_updated? && email_not_previously_sent?
 
65  end
 
  • Complexity 1 » saikuro
67  def send_email
 
68    create_email.deliver_now
 
69  end
 
  • Complexity 3 » saikuro
71  def mailer
 
72    case petition
 
73    when Archived::Petition
 
74      Archived::PetitionMailer
 
75    when Petition
 
76      PetitionMailer
 
77    else
 
78      raise ArgumentError, "Unknown petition type: #{petition.class}"
 
79    end
 
80  end
 
  • Complexity 1 » saikuro
82  def create_email
 
83    raise NotImplementedError.new "Including classes must implement #create_email method"
 
84  end
 
  • Complexity 1 » saikuro
86  def record_email_sent
 
87    signature.set_email_sent_at_for timestamp_name, to: petition_timestamp
 
88  end
 
  • Complexity 1 » saikuro
90  def petition_timestamp
 
91    petition.get_email_requested_at_for(timestamp_name)
 
92  end
 
 
94  # We do not want to send the email if the petition has been updated
 
95  # As email sending is enqueued straight after a petition has been updated
  • Complexity 1 » saikuro
96  def petition_has_not_been_updated?
 
97    (petition_timestamp - requested_at.in_time_zone).abs < 1
 
98  end
 
 99
 
100  # Have we already sent an email for this petition version?
 
101  # If we have then the timestamp for the signature will match the timestamp for the petition
  • Complexity 1 » saikuro
102  def email_not_previously_sent?
 
103    # check that the signature is still in the list of signatures
 
104    petition.signatures_to_email_for(timestamp_name).where(id: signature.id).exists?
 
105  end
 
  • UtilityFunction - doesn't depend on instance state (maybe move it to another class?) » reek
  • Complexity 1 » saikuro
107  def set_appsignal_namespace
 
108    Appsignal.set_namespace("email")
 
109  end
 
110end