Skip to content

Commit

Permalink
Merge pull request #28 from interflux-electronics/feature/confirmatio…
Browse files Browse the repository at this point in the history
…n-emails

Event registration confirmation emails
  • Loading branch information
janwerkhoven authored Apr 18, 2024
2 parents 56bc222 + 392d7ff commit 5472d0c
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 3 deletions.
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ gem 'aws-sdk-s3'
# For HTTP requests (native Rails does this terribly...)
gem 'faraday'

# For printing curl requests after Faraday requests
gem 'faraday_curl', groups: %i[development]

# For catching N+1 queries
gem 'bullet', groups: %i[development test]

Expand All @@ -62,4 +65,4 @@ gem 'rubocop-rails', groups: %i[development]
gem 'minitest', groups: %i[test]

# For annotating models and fixtures with schema info.
gem 'annotate', groups: %i[development]
gem 'annotate', groups: %i[development]
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ GEM
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
faraday_curl (0.0.2)
faraday (>= 0.9.0)
ffi (1.15.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
Expand Down Expand Up @@ -226,6 +228,7 @@ DEPENDENCIES
byebug
dotenv-rails
faraday
faraday_curl
jsonapi-serializer
jwt
listen
Expand Down
1 change: 1 addition & 0 deletions app/controllers/v1/public/event_attendees_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def creatable_attributes
role
company
email
locale
]
end

Expand Down
1 change: 0 additions & 1 deletion app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
class ApplicationMailer < ActionMailer::Base
default from: 'jw@floatplane.dev'
layout 'mailer'
end
6 changes: 6 additions & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true

private

def log
Rails.logger
end
end
100 changes: 100 additions & 0 deletions app/models/email_attempt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# == Schema Information
#
# Table name: email_attempts
#
# id :uuid not null, primary key
# bcc :string
# cc :string
# created_by_type :string
# delivered :boolean
# from :string
# postmark_stream :string
# postmark_template_alias :string
# postmark_template_model :jsonb
# provider :integer
# reply_to :string
# response_body :jsonb
# response_status :integer
# to :string
# created_at :datetime not null
# updated_at :datetime not null
# created_by_id :uuid
#
# Indexes
#
# index_email_attempts_on_created_by (created_by_type,created_by_id)
# index_email_attempts_on_response_body (response_body) USING gin
#
class EmailAttempt < ApplicationRecord
# The record which created this EmailAttempt
belongs_to :created_by, polymorphic: true, optional: true

# The provider which will send the email for us.
# Rails cannot send emails. It only delegates them.
# enum :provider, { postmark: 0, sendgrid: 1 }, scopes: true

after_save :send_email

private

def blocker
return 'no to' if to.nil?
return 'no from' if from.nil?
return 'no postmark_stream' if postmark_stream.nil?
return 'no postmark_template_alias' if postmark_template_alias.nil?
return 'no postmark_template_model' if postmark_template_model.nil?
return 'model is not a hash' unless postmark_template_model.is_a? Hash

# Never deliver an email twice
# This also allows admins to edit and retry until delivered
return 'already delivered' if delivered == true

nil
end

def send_email
log.info "✅ saved EmailAttempt #{id}"

if blocker.present?
log.warn "🔥 #{blocker}"
return
end

log.info '✅ sending ...'

ap from
ap to
ap postmark_template_alias
ap postmark_template_model

api = Postmark::Api.new(server_token: ENV['POSTMARK_SERVER_TOKEN'])

response = api.send_email_with_template(
message_stream: postmark_stream,
template_alias: postmark_template_alias,
template_model: postmark_template_model,
from: from,
to: to,
cc: cc,
bcc: bcc,
reply_to: reply_to
)

if response.status == 200
log.info '✅ success'
else
log.info '❌ fail'
end

ap response.status
ap response.body

self.response_status = response.status
self.response_body = JSON.parse(response.body) if response.body.present?
self.delivered = response.status == 200

save!

log.info '✅ done'
end
end
24 changes: 24 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,28 @@ class Event < ApplicationRecord
has_many :event_attendees

alias_attribute :attendees, :event_attendees

def location
"#{city}, #{country.name_english}"
end

def start_to_end_date
return '?' if sd.nil? && ed.nil?

return sd.strftime('%a %-d %b %Y') if sd.present? && ed.nil?

return sd.strftime('%a %-d %b %Y') if ed == sd

return "#{sd.strftime('%a %d')} to #{ed.strftime('%a %-d %b %Y')}" if sd.month == ed.month

"#{sd.strftime('%a %-d %b %Y')} to #{ed.strftime('%a %-d %b %Y')}"
end

def sd
start_date&.to_date
end

def ed
end_date&.to_date
end
end
68 changes: 68 additions & 0 deletions app/models/event_attendee.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'postmark/api'
require 'ap'

# == Schema Information
#
# Table name: event_attendees
Expand All @@ -7,6 +10,7 @@
# email :string
# first_name :string
# last_name :string
# locale :string
# role :string
# created_at :datetime not null
# updated_at :datetime not null
Expand All @@ -16,4 +20,68 @@
class EventAttendee < ApplicationRecord
belongs_to :event
belongs_to :person, optional: true

after_save :send_confirmation_emails

private

def send_confirmation_emails
log.info "✅ EventAttendee #{id} saved"

return if email.nil?

# TODO: Prevent this email from being sent upon every save...

template_locale = locale == 'fr' ? 'fr' : 'en'

log.info '✅ creating confirmation email'

EmailAttempt.create!(
to: email,
from: 'Interflux Electronics <robot@interflux.com>',
reply_to: 'Interflux Electronics <ask@interflux.com>',
# provider: :postmark,
postmark_stream: 'outbound',
postmark_template_alias: "interflux-event-registration-1-#{template_locale}",
postmark_template_model: {
first_name: first_name,
last_name: last_name,
event_name: event.name,
event_dates: event.start_to_end_date,
event_location: event.location
},
created_by: self
)

log.info '✅ creating internal email'

EmailAttempt.create!(
to: 'Steven Teliszewski <s.teliszewski@interflux.com>',
cc: 'Jan Werkhoven <jw@interflux.au>',
from: 'Interflux Electronics <robot@interflux.com>',
reply_to: 'Interflux Electronics <ask@interflux.com>',
# provider: :postmark,
postmark_stream: 'outbound',
postmark_template_alias: 'interflux-event-registration-2-en',
postmark_template_model: {
receiver: {
first_name: 'Steven'
},
attendee: {
first_name: first_name,
last_name: last_name,
role: role,
company: company,
email: email,
locale: locale
},
event: {
name: event.name,
dates: event.start_to_end_date,
location: event.location
}
},
created_by: self
)
end
end
29 changes: 29 additions & 0 deletions db/migrate/20240415091050_create_email_attempt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class CreateEmailAttempt < ActiveRecord::Migration[6.1]
def change
create_table :email_attempts, id: :uuid do |t|
t.references :created_by, polymorphic: true, type: :uuid

t.string :from
t.string :to
t.string :cc
t.string :bcc
t.string :reply_to

t.integer :provider

t.string :postmark_stream
t.string :postmark_template_alias
t.jsonb :postmark_template_model, default: {}

# When storing JSONB
# https://dev.to/kputra/rails-postgresql-jsonb-part-1-4ibg
t.jsonb :response_body, default: {}
t.integer :response_status
t.boolean :delivered

t.timestamps
end

add_index :email_attempts, :response_body, using: :gin
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240418113914_add_host_to_event_attendee.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHostToEventAttendee < ActiveRecord::Migration[6.1]
def change
add_column :event_attendees, :locale, :string
end
end
24 changes: 23 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_04_07_010227) do
ActiveRecord::Schema.define(version: 2024_04_18_113914) do

# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
Expand Down Expand Up @@ -214,6 +214,27 @@
t.boolean "public", default: false
end

create_table "email_attempts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "created_by_type"
t.uuid "created_by_id"
t.string "from"
t.string "to"
t.string "cc"
t.string "bcc"
t.string "reply_to"
t.integer "provider"
t.string "postmark_stream"
t.string "postmark_template_alias"
t.jsonb "postmark_template_model", default: {}
t.jsonb "response_body", default: {}
t.integer "response_status"
t.boolean "delivered"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["created_by_type", "created_by_id"], name: "index_email_attempts_on_created_by"
t.index ["response_body"], name: "index_email_attempts_on_response_body", using: :gin
end

create_table "employees", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "company_id"
t.uuid "person_id"
Expand All @@ -233,6 +254,7 @@
t.string "email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "locale"
end

create_table "events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
Expand Down
Loading

0 comments on commit 5472d0c

Please sign in to comment.