Class: Communication::EventParser

Inherits:
BaseService show all
Defined in:
app/services/communication/event_parser.rb

Constant Summary collapse

SENDGRID_EVENTS_MAP =
{
  dropped: :dropped,
  delivered: :delivered,
  bounce: :bounced,
  blocked: :blocked,
  open: :opened,
  click: :clicked,
  spamreport: :spammed,
  unsubscribe: :unsubscribed
}.freeze
SENDGRID_EVENTS =
SENDGRID_EVENTS_MAP.keys.freeze

Instance Method Summary collapse

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #options, #tagged_logger

Constructor Details

This class inherits a constructor from BaseService

Instance Method Details

#process(options) ⇒ Object



18
19
20
21
22
23
24
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'app/services/communication/event_parser.rb', line 18

def process(options)
  # normalize our keys
  Rails.logger.debug("Processing Sendgrid event", event: options[:event], email: options[:email], unique_id: options[:unique_id])
  options.transform_keys!{|key| key.to_s.underscore.to_sym}
  unique_id = options[:unique_id]
  email = options[:email]&.downcase
  timestamp = options.delete(:timestamp)
  date_time_of_event = Time.at(timestamp) if timestamp.present?
  date_time_of_event ||= Time.current
  event = options[:event]&.strip.downcase.presence.to_sym
  ip_address = options[:ip]
  user_agent = options[:useragent]
  options[:bounce_type] ||= options[:type]
  mapped_status = SENDGRID_EVENTS_MAP[event]

  if email.present? && !email.ends_with?('@warmlyyours.com') && mapped_status
    # Email Preference Check
    EmailPreference.with_advisory_lock(email) do
      ep = EmailPreference.where(email: email).first_or_initialize
      #Only process the event if it came after
      if ep.last_delivery_status_at.nil? || (date_time_of_event > ep.last_delivery_status_at)
        ep.last_delivery_status = mapped_status
        ep.last_delivery_status_notes = options.slice(:reason,:response,:type,:status).map{|k,v| "#{k}: #{v}"}.join(', ')
        ep.last_delivery_status_at = date_time_of_event || Time.current
        ep.save
      end
    end
  end

  unless unique_id.present? && email.present? && event.present? && date_time_of_event
    Rails.logger.warn("Communication Event missing required fields", event: event, email: email, unique_id: unique_id)
    return false
  end

  # Find communication recipient by communication id and detail
  cr = CommunicationRecipient.joins(:communication)
                             .where(detail: email)
                             .find_by(communications: { unique_id: unique_id })

  # If we don't find the event log and skip
  # bounce, click, deferred, delivered, dropped, processed, open, spamreport, unsubscribe
  unless cr
    Rails.logger.error("Communication Event recipient not found", event: event, email: email, unique_id: unique_id)
    return false
  end

  webhook_event = WebhookEvent.new(timestamp: date_time_of_event)
  webhook_event.attributes = options.select { |k, _v| webhook_event.attributes.keys.member?(k.to_s) }
  cr.webhook_events << webhook_event

  if cr.state_updated_at.present? && date_time_of_event < cr.state_updated_at
    Rails.logger.warn("Communication Event out of order",
                      event: event,
                      email: email,
                      recipient_id: cr.id,
                      event_time: date_time_of_event,
                      last_update: cr.state_updated_at)
    return false
  end

  cr.state_updated_at = date_time_of_event
  cr.user_agent = user_agent
  cr.ip_address = ip_address

  case event
  when :processed
    cr.process
  when :dropped
    cr.state_response = options[:reason]
    cr.drop
  when :delivered
    cr.state_response = (options[:response]).to_s
    cr.deliver
  when :deferred
    cr.state_response = "Attempt #{options[:attempt]}: #{options[:response]}"
    cr.defer
  when :bounce
    cr.state_response = "#{options[:type]} #{options[:reason]}, #{options[:status]}"
    cr.bounce
  when :open
    cr.open_communication
  when :click
    # Find this url
    el = EmailLink.where(url: webhook_event.url).first_or_create
    cr.communication_recipient_email_links.create(email_link_id: el.id)
    cr.click
  when :spamreport
    cr.spam
  when :unsubscribe
    cr.unsubscribe
  else
    Rails.logger.error "Communication Event #{options.inspect} : Unknown event #{event}"
  end

end