Skip to content

Commit

Permalink
Add EmailDeliveryJob
Browse files Browse the repository at this point in the history
This adds a new job, similar to the `SMSDeliveryJob` that allows us to
send emails using GOV.UK Notify directly rather than going via
`ActionMailer`. Using the API directly allows us to record the delivery
message ID, which we can then use to get the status of the receipt later
on and present this information to users.
  • Loading branch information
thomasleese committed Jan 21, 2025
1 parent 668fc08 commit a9cc298
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Rails/UnknownEnv:

RSpec/BeforeAfterAll:
Exclude:
- spec/jobs/email_delivery_job_spec.rb
- spec/jobs/sms_delivery_job_spec.rb

RSpec/ContextWording:
Expand Down
69 changes: 69 additions & 0 deletions app/jobs/email_delivery_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

class EmailDeliveryJob < NotifyDeliveryJob
def perform(
template_name,
consent: nil,
consent_form: nil,
parent: nil,
patient: nil,
patient_session: nil,
programme: nil,
sent_by: nil,
session: nil,
vaccination_record: nil
)
template_id = GOVUK_NOTIFY_EMAIL_TEMPLATES[template_name.to_sym]
raise UnknownTemplate if template_id.nil?

email_address =
consent_form&.parent_email || consent&.parent&.email || parent&.email
return if email_address.nil?

organisation =
session&.organisation || patient_session&.organisation ||
consent_form&.organisation || consent&.organisation ||
vaccination_record&.organisation

personalisation =
GovukNotifyPersonalisation.call(
session:,
consent:,
consent_form:,
patient:,
patient_session:,
programme:,
vaccination_record:
)

args = {
email_address:,
email_reply_to_id: organisation.reply_to_id,
personalisation:,
template_id:
}

delivery_id =
if self.class.send_via_notify?
self.class.client.send_email(**args).id
elsif self.class.send_via_test?
self.class.deliveries << args
SecureRandom.uuid
else
Rails.logger.info "Sending email to #{email_address} with template #{template_id}"
nil
end

patient ||= consent&.patient || patient_session&.patient

NotifyLogEntry.create!(
consent_form:,
delivery_id:,
patient:,
recipient: email_address,
sent_by:,
template_id:,
type: :email
)
end
end
167 changes: 167 additions & 0 deletions spec/jobs/email_delivery_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# frozen_string_literal: true

describe EmailDeliveryJob do
before(:all) do
Rails.configuration.action_mailer.delivery_method = :notify
Rails.configuration.action_mailer.notify_settings = { api_key: "abc" }
end

after(:all) { Rails.configuration.action_mailer.delivery_method = :test }

let(:response) do
instance_double(
Notifications::Client::ResponseNotification,
id: SecureRandom.uuid
)
end
let(:notifications_client) { instance_double(Notifications::Client) }

before do
allow(Notifications::Client).to receive(:new).with("abc").and_return(
notifications_client
)
allow(notifications_client).to receive(:send_email).and_return(response)
end

after { described_class.instance_variable_set("@client", nil) }

describe "#perform_now" do
subject(:perform_now) do
described_class.perform_now(
template_name,
session:,
consent:,
consent_form:,
parent:,
patient:,
patient_session:,
programme:,
sent_by:,
vaccination_record:
)
end

let(:template_name) { GOVUK_NOTIFY_EMAIL_TEMPLATES.keys.first }
let(:programme) { create(:programme) }
let(:organisation) do
create(
:organisation,
reply_to_id: "54bf1d28-8851-43f2-893d-1853f43a50cd",
programmes: [programme]
)
end
let(:session) { create(:session, programme:, organisation:) }
let(:parent) { create(:parent, email: "test@example.com") }
let(:consent) { nil }
let(:consent_form) { nil }
let(:patient) { create(:patient) }
let(:patient_session) { nil }
let(:sent_by) { create(:user) }
let(:vaccination_record) { nil }

it "generates personalisation" do
expect(GovukNotifyPersonalisation).to receive(:call).with(
session:,
consent:,
consent_form:,
patient:,
patient_session:,
programme:,
vaccination_record:
)
perform_now
end

it "sends a text using GOV.UK Notify" do
expect(notifications_client).to receive(:send_email).with(
email_address: "test@example.com",
email_reply_to_id: "54bf1d28-8851-43f2-893d-1853f43a50cd",
personalisation: an_instance_of(Hash),
template_id: GOVUK_NOTIFY_EMAIL_TEMPLATES[template_name]
)
perform_now
end

it "creates a log entry" do
expect { perform_now }.to change(NotifyLogEntry, :count).by(1)

notify_log_entry = NotifyLogEntry.last
expect(notify_log_entry).to be_email
expect(notify_log_entry.delivery_id).to eq(response.id)
expect(notify_log_entry.recipient).to eq("test@example.com")
expect(notify_log_entry.template_id).to eq(
GOVUK_NOTIFY_EMAIL_TEMPLATES[template_name]
)
expect(notify_log_entry.patient).to eq(patient)
expect(notify_log_entry.sent_by).to eq(sent_by)
end

context "when the parent doesn't have an email address" do
let(:parent) { create(:parent, email: nil) }

it "doesn't send a text" do
expect(notifications_client).not_to receive(:send_email)
perform_now
end
end

context "with a consent form" do
let(:consent_form) do
create(
:consent_form,
programme:,
session:,
parent_email: "test@example.com"
)
end
let(:parent) { nil }
let(:patient) { nil }

it "sends a text using GOV.UK Notify" do
expect(notifications_client).to receive(:send_email).with(
email_address: "test@example.com",
email_reply_to_id: "54bf1d28-8851-43f2-893d-1853f43a50cd",
personalisation: an_instance_of(Hash),
template_id: GOVUK_NOTIFY_EMAIL_TEMPLATES[template_name]
)
perform_now
end

it "creates a log entry" do
expect { perform_now }.to change(NotifyLogEntry, :count).by(1)

notify_log_entry = NotifyLogEntry.last
expect(notify_log_entry).to be_email
expect(notify_log_entry.delivery_id).to eq(response.id)
expect(notify_log_entry.recipient).to eq("test@example.com")
expect(notify_log_entry.template_id).to eq(
GOVUK_NOTIFY_EMAIL_TEMPLATES[template_name]
)
expect(notify_log_entry.consent_form).to eq(consent_form)
end

context "when the parent doesn't have a phone number" do
let(:consent_form) do
create(:consent_form, programme:, session:, parent_email: nil)
end

it "doesn't send a text" do
expect(notifications_client).not_to receive(:send_email)
perform_now
end
end
end
end

describe "#perform_later" do
subject(:perform_later) do
described_class.perform_later(GOVUK_NOTIFY_EMAIL_TEMPLATES.keys.first)
end

it "uses the mailer queue" do
expect { perform_later }.to have_enqueued_job(described_class).on_queue(
:mailer
)
end
end
end

0 comments on commit a9cc298

Please sign in to comment.