Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f116816
Created mailer method, preview, and placeholder for content.
jmax-gsa Aug 2, 2023
615abf4
Email formatting
jmax-gsa Aug 3, 2023
7784bf7
Moved strings out to i18m Yaml
jmax-gsa Aug 4, 2023
1f86b76
Lint nits
jmax-gsa Aug 7, 2023
0dc14bc
French and Spanish strings
jmax-gsa Aug 7, 2023
72caa22
Lint and i18n nits
jmax-gsa Aug 7, 2023
ab2a75a
Point email links at correct targets
jmax-gsa Aug 8, 2023
41c1f65
Added reminder_sent_at to usps_confirmations table
jmax-gsa Aug 9, 2023
4736e90
Point links at correct targets, take 2.
jmax-gsa Aug 10, 2023
b72eada
Specs for basic cases.
jmax-gsa Aug 10, 2023
db0d993
Add time range to profile select.
jmax-gsa Aug 10, 2023
6878977
Add checks for time and reminder already sent
jmax-gsa Aug 11, 2023
5a52cce
Hande users with multiple emails
jmax-gsa Aug 11, 2023
b403690
Added mailer spec for gpo_reminder
jmax-gsa Aug 11, 2023
922be1e
Actually set up the job.
jmax-gsa Aug 11, 2023
3450849
Added analytics event for reminder email sent.
jmax-gsa Aug 14, 2023
ef01c75
Lint nit and changelog
jmax-gsa Aug 14, 2023
44766b4
Added spec for user canceling GPO verification
jmax-gsa Aug 15, 2023
0de4ca5
Pointed at new 'request another letter' page.
jmax-gsa Aug 15, 2023
7fbdb71
Review comments
jmax-gsa Aug 15, 2023
13341b9
Review comment
jmax-gsa Aug 15, 2023
eb3f24d
Normalize YAML
jmax-gsa Aug 15, 2023
0a0a36d
Review comment
jmax-gsa Aug 15, 2023
d64357a
Review comment
jmax-gsa Aug 15, 2023
affdbf7
Lint
jmax-gsa Aug 15, 2023
f3489a0
Update spec/jobs/gpo_reminder_job_spec.rb
jmax-gsa Aug 16, 2023
74b9dd6
Update spec/jobs/gpo_reminder_job_spec.rb
jmax-gsa Aug 16, 2023
b719f1c
Fixed preview
jmax-gsa Aug 16, 2023
d5a5304
Review comments
jmax-gsa Aug 16, 2023
e36a6ee
Review comments.
jmax-gsa Aug 16, 2023
7dbf07a
Added spec for user who completes GPO proofing before their reminder
jmax-gsa Aug 16, 2023
f6edd1c
Fixed spec.
jmax-gsa Aug 16, 2023
0227318
Spec for updating sent_at time
jmax-gsa Aug 17, 2023
54f160f
Review comments.
jmax-gsa Aug 17, 2023
e563453
Review comment
jmax-gsa Aug 21, 2023
b49d2d6
Lint nits
jmax-gsa Aug 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/jobs/gpo_reminder_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class GpoReminderJob < ApplicationJob
queue_as :low

# Send email reminders to people with USPS proofing letters whose
# letters were sent a while ago, and haven't yet entered their code
def perform(cutoff_time_for_sending_reminders)
GpoReminderSender.new.
send_emails(cutoff_time_for_sending_reminders)
end
end
10 changes: 10 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,16 @@ def suspended_reset_password
end
end

def gpo_reminder
with_user_locale(user) do
@gpo_verification_pending_at = I18n.l(
user.gpo_verification_pending_profile.gpo_verification_pending_at,
format: :event_date,
)
mail(to: email_address.email, subject: t('idv.messages.gpo_reminder.subject'))
end
end

private

def email_should_receive_nonessential_notifications?(email)
Expand Down
6 changes: 6 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,12 @@ def idv_gpo_confirm_start_over_visited
track_event('IdV: gpo confirm start over visited')
end

# A GPO reminder email was sent to the user
# @param [String] user_id UUID of user who we sent a reminder to
def idv_gpo_reminder_email_sent(user_id:, **extra)
track_event('IdV: gpo reminder email sent', user_id: user_id, **extra)
end

# @identity.idp.previous_event_name Account verification submitted
# @param [Boolean] success
# @param [Hash] errors
Expand Down
21 changes: 21 additions & 0 deletions app/services/gpo_reminder_sender.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class GpoReminderSender
def send_emails(for_letters_sent_before)
profiles_due_for_reminder = Profile.joins(:gpo_confirmation_codes).
where(
gpo_verification_pending_at: ..for_letters_sent_before,
gpo_confirmation_codes: { reminder_sent_at: nil },
)

profiles_due_for_reminder.each do |profile|
profile.user.send_email_to_all_addresses(:gpo_reminder)
profile.gpo_confirmation_codes.first.update(reminder_sent_at: Time.zone.now)
analytics.idv_gpo_reminder_email_sent(user_id: profile.user.uuid)
end
end

private

def analytics
Analytics.new(user: AnonymousUser.new, request: nil, session: {}, sp: nil)
end
end
54 changes: 54 additions & 0 deletions app/views/user_mailer/gpo_reminder.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<p>
<%= help_link = link_to(
t('idv.troubleshooting.options.learn_more_verify_by_mail'),
help_center_redirect_url(
category: 'verify-your-identity',
article: 'verify-your-address-by-mail',
flow: :idv,
step: :gpo_send_letter,
),
{ style: "text-decoration: 'underline'" },
)

t(
'idv.messages.gpo_reminder.body_html',
date_letter_was_sent: @gpo_verification_pending_at,
app_name: APP_NAME,
help_link: help_link,
) %>
</p>

<table class="button expanded large radius">
<tbody>
<tr>
<td>
<table style="margin-bottom: 1em; margin-top: 1em">
<tbody>
<tr>
<td style="text-align: center">
<%= link_to t('idv.messages.gpo_reminder.finish'),
idv_gpo_verify_url,
target: '_blank',
class: 'float-center',
align: 'center',
rel: 'noopener' %>
</td>
</tr>
</tbody>
</table>
</td>
<td class="expander"></td>
</tr>
</tbody>
</table>

<p>
<%= t(
'idv.messages.gpo_reminder.did_not_get_a_letter_html',
another_letter_link_html: link_to(
t('idv.messages.gpo_reminder.sign_in_and_request_another_letter'),
idv_gpo_verify_url(did_not_receive_letter: 1),
{ style: "text-decoration: 'underline'" },
),
) %>
</p>
6 changes: 6 additions & 0 deletions config/initializers/job_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@
cron: cron_24h,
args: -> { [Time.zone.today] },
},
# Send reminder letters for old, outstanding GPO verification codes
send_gpo_code_reminders: {
class: 'GpoReminderJob',
cron: cron_24h,
args: -> { [14.days.ago] },
},
}.compact
end
# rubocop:enable Metrics/BlockLength
Expand Down
9 changes: 9 additions & 0 deletions config/locales/idv/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ en:
resend_timeframe: Letters typically take 3 to 7 business days to arrive.
timeframe_html: Letters are sent the next business day via USPS First Class Mail
and <strong>typically take 3 to 7 business days to arrive</strong>.
gpo_reminder:
body_html: You requested a letter to verify your identity on
<strong>%{date_letter_was_sent}</strong>. You’ll need to enter the
code from the letter to finish verifying your identity. Sign back in
to %{app_name} to finish verifying your identity. %{help_link}.
did_not_get_a_letter_html: If you didn’t get this letter, %{another_letter_link_html}.
finish: Finish verifying your identity
sign_in_and_request_another_letter: sign in to request another letter
subject: Finish verifying your identity
otp_delivery_method_description: If you entered a landline above, please select “Phone call” below.
personal_key: This is your new personal key. Write it down and keep it in a safe
place. You will need it if you ever lose your password.
Expand Down
10 changes: 10 additions & 0 deletions config/locales/idv/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ es:
timeframe_html: Las cartas se envían al día siguiente por First Class Mail de
USPS y <strong>suelen tardar entre 3 y 7 días hábiles en
llegar</strong>.
gpo_reminder:
body_html: El <strong>%{date_letter_was_sent}</strong> solicitaste una carta
para verificar tu identidad. Deberás ingresar el código que contiene
esa carta para completar la verificación por correo. Vuelve a iniciar
sesión en %{app_name} para terminar de verificar tu identidad.
%{help_link}.
did_not_get_a_letter_html: Si no recibiste dicha carta, %{another_letter_link_html}.
finish: Termina de verificar tu identidad
sign_in_and_request_another_letter: inicia sesión para solicitar otra
subject: Termina de verificar tu identidad
otp_delivery_method_description: Si ha introducido un teléfono fijo más arriba,
seleccione “Llamada telefónica” más abajo.
personal_key: Esta es su nueva clave personal. Escríbala y guárdela en un lugar
Expand Down
10 changes: 10 additions & 0 deletions config/locales/idv/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,16 @@ fr:
timeframe_html: Les lettres sont envoyées les jours ouvrables par courriel de
première classe de USPS et <strong>prennent généralement entre trois à
sept jours ouvrables pour être reçues</strong>.
gpo_reminder:
body_html: Vous avez demandé une lettre pour vérifier votre identité le
<strong>%{date_letter_was_sent}</strong>. Vous devrez saisir le code
figurant dans la lettre pour terminer la vérification par courrier.
Reconnectez-vous à %{app_name} pour terminer la vérification de votre
identité. %{help_link}.
did_not_get_a_letter_html: Si vous n’avez pas reçu cette lettre, %{another_letter_link_html}.
finish: Terminer la vérification de votre identité
sign_in_and_request_another_letter: connectez-vous pour en demander une autre.
subject: Terminer la vérification de votre identité
otp_delivery_method_description: Si vous avez saisi une ligne fixe ci-dessus,
veuillez sélectionner « Appel téléphonique » ci-dessous.
personal_key: Il s’agit de votre nouvelle clé personnelle. Notez-la et
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class AddReminderSentAtToUspsConfirmationCodes < ActiveRecord::Migration[7.0]
disable_ddl_transaction!

def change
add_column :usps_confirmation_codes, :reminder_sent_at, :datetime, precision: nil
add_index :usps_confirmation_codes, :reminder_sent_at, algorithm: :concurrently
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we need an index here. Are we querying the usps_confirmation_codes table by when reminders were sent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we are querying that, although it's not immediately obvious. The index is for the benefit of the code in GpoReminderSender, where we do a join that this index helps.

end
end
2 changes: 2 additions & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -619,8 +619,10 @@
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.datetime "bounced_at", precision: nil
t.datetime "reminder_sent_at", precision: nil
t.index ["otp_fingerprint"], name: "index_usps_confirmation_codes_on_otp_fingerprint"
t.index ["profile_id"], name: "index_usps_confirmation_codes_on_profile_id"
t.index ["reminder_sent_at"], name: "index_usps_confirmation_codes_on_reminder_sent_at"
end

create_table "usps_confirmations", id: :serial, force: :cascade do |t|
Expand Down
28 changes: 28 additions & 0 deletions spec/jobs/gpo_reminder_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'rails_helper'

RSpec.describe GpoReminderJob do
let(:wait_before_sending_reminder) { 14.days }

describe '#perform' do
subject(:perform) { job.perform(wait_before_sending_reminder.ago) }

let(:job) { GpoReminderJob.new }
let(:user) { create(:user, :with_pending_gpo_profile) }
let(:pending_profile) { user.pending_profile }
let(:job_analytics) { FakeAnalytics.new }

before do
pending_profile.update(
gpo_verification_pending_at: wait_before_sending_reminder.ago,
)
allow(Analytics).to receive(:new).and_return(job_analytics)
end

it 'sends reminder emails' do
expect { perform }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect(job_analytics).to have_logged_event(
'IdV: gpo reminder email sent',
)
end
end
end
20 changes: 20 additions & 0 deletions spec/mailers/previews/user_mailer_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,32 @@ def suspended_reset_password
).suspended_reset_password
end

def gpo_reminder
UserMailer.with(
user: user_with_pending_gpo_letter,
email_address: email_address_record,
).gpo_reminder
end

private

def user
unsaveable(User.new(email_addresses: [email_address_record]))
end

def user_with_pending_gpo_letter
raw_user = user
gpo_pending_profile = unsaveable(
Profile.new(
user: raw_user,
active: false,
gpo_verification_pending_at: Time.zone.now,
),
)
raw_user.send(:instance_variable_set, :@pending_profile, gpo_pending_profile)
raw_user
end

def email_address
'email@example.com'
end
Expand Down
75 changes: 75 additions & 0 deletions spec/mailers/user_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -872,4 +872,79 @@ def expect_email_body_to_have_help_and_contact_links
)
end
end

describe '#gpo_reminder' do
let(:date_letter_was_sent) { Date.new(1969, 7, 20) }

let(:user) do
user = create(:user, :with_pending_gpo_profile)
user.pending_profile.update(gpo_verification_pending_at: date_letter_was_sent)
user
end

let(:mail) do
UserMailer.with(user: user, email_address: email_address).gpo_reminder
end

it_behaves_like 'a system email'
it_behaves_like 'an email that respects user email locale preference'

it 'sends to the specified email' do
expect(mail.to).to eq [email_address.email]
end

it 'renders the subject' do
expect(mail.subject).to eq t('idv.messages.gpo_reminder.subject')
end

it 'renders the body' do
expected_help_link = ActionController::Base.helpers.link_to(
t('idv.troubleshooting.options.learn_more_verify_by_mail'),
help_center_redirect_url(
category: 'verify-your-identity',
article: 'verify-your-address-by-mail',
flow: :idv,
step: :gpo_send_letter,
),
{ style: "text-decoration: 'underline'" },
)

expected_body = strip_tags(
t(
'idv.messages.gpo_reminder.body_html',
date_letter_was_sent: date_letter_was_sent.strftime(t('time.formats.event_date')),
app_name: APP_NAME,
help_link: expected_help_link,
),
)

expect(mail.html_part.body).to have_content(expected_body)
end

it 'renders the finish link' do
expect(mail.html_part.body).to have_link(
t('idv.messages.gpo_reminder.finish'),
href: idv_gpo_verify_url,
)
end

it 'renders the did not get it link' do
expect(mail.html_part.body).to have_link(
t('idv.messages.gpo_reminder.sign_in_and_request_another_letter'),
href: idv_gpo_verify_url(did_not_receive_letter: 1),
)
end

it 'renders the help link' do
expect(mail.html_part.body).to have_link(
t('idv.troubleshooting.options.learn_more_verify_by_mail'),
href: help_center_redirect_url(
category: 'verify-your-identity',
article: 'verify-your-address-by-mail',
flow: :idv,
step: :gpo_send_letter,
),
)
end
end
end
Loading