Skip to content

Commit

Permalink
Send email notifications for 4142 failures (#19183)
Browse files Browse the repository at this point in the history
* Send email notifications for 4142 failures
- tests for sending emails
- tests for feature flag
- tests for errors
- config settings for 4142 email template for local and test
- requires separate PR for 4142 email template for staging and prod
  • Loading branch information
kayline authored Nov 1, 2024
1 parent b921125 commit 518ace1
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/models/secondary_appeal_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ class SecondaryAppealForm < ApplicationRecord

has_kms_key
has_encrypted :form, key: :kms_key, **lockbox_options
scope :needs_failure_notification, lambda {
where(delete_date: nil, failure_notification_sent_at: nil).where('status LIKE ?', '%error%')
}
end
70 changes: 69 additions & 1 deletion app/sidekiq/decision_review/failure_notification_email_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class FailureNotificationEmailJob
'SC' => TEMPLATE_IDS.supplemental_claim_evidence_error_email
}.freeze

SECONDARY_FORM_TEMPLATE_ID = TEMPLATE_IDS.supplemental_claim_secondary_form_error_email

APPEAL_TYPE_TO_SERVICE_MAP = {
'HLR' => 'higher-level-review',
'NOD' => 'board-appeal',
Expand All @@ -38,16 +40,31 @@ class FailureNotificationEmailJob
STATSD_KEY_PREFIX = 'worker.decision_review.failure_notification_email'

def perform
return unless enabled? && (submissions.present? || submission_uploads.present?)
return unless should_perform?

send_form_emails
send_evidence_emails
send_secondary_form_emails if secondary_forms_enabled?

nil
end

private

def should_perform?
perform_form_and_evidence || perform_all
end

def perform_form_and_evidence
enabled? && (submissions.present? || submission_uploads.present?)
end

def perform_all
enabled? &&
(secondary_forms_enabled? &&
(submissions.present? || submission_uploads.present? || errored_secondary_forms.present?))
end

def vanotify_service
@service ||= ::VaNotify::Service.new(Settings.vanotify.services.benefits_decision_review.api_key)
end
Expand Down Expand Up @@ -76,6 +93,10 @@ def submission_uploads
end
end

def errored_secondary_forms
@errored_secondary_forms ||= SecondaryAppealForm.needs_failure_notification.order(id: :asc)
end

def send_email_with_vanotify(submission, filename, created_at, template_id, reference)
email_address = submission.current_email_address
personalisation = {
Expand Down Expand Up @@ -122,6 +143,24 @@ def send_evidence_emails
end
end

def send_secondary_form_emails
StatsD.increment("#{STATSD_KEY_PREFIX}.secondary_forms.processing_records", errored_secondary_forms.size)
errored_secondary_forms.each do |form|
appeal_type = form.appeal_submission.type_of_appeal
reference = "#{appeal_type}-secondary_form-#{form.guid}"
response = send_email_with_vanotify(form.appeal_submission,
nil,
form.created_at,
SECONDARY_FORM_TEMPLATE_ID,
reference)
form.update(failure_notification_sent_at: DateTime.now)

record_secondary_form_email_send_successful(form, response.id)
rescue => e
record_secondary_form_email_send_failure(form, e)
end
end

def record_form_email_send_successful(submission, notification_id)
appeal_type = submission.type_of_appeal
params = { submitted_appeal_uuid: submission.submitted_appeal_uuid, appeal_type:, notification_id: }
Expand All @@ -139,6 +178,31 @@ def record_form_email_send_failure(submission, e)
StatsD.increment("#{STATSD_KEY_PREFIX}.form.error", tags: ["appeal_type:#{appeal_type}"])
end

def record_secondary_form_email_send_successful(secondary_form, notification_id)
submission = secondary_form.appeal_submission
appeal_type = submission.type_of_appeal
params = { submitted_appeal_uuid: submission.submitted_appeal_uuid,
lighthouse_upload_id: secondary_form.guid,
appeal_type:,
notification_id: }
Rails.logger.info('DecisionReview::FailureNotificationEmailJob secondary form email queued', params)
StatsD.increment("#{STATSD_KEY_PREFIX}.secondary_form.email_queued", tags: ["appeal_type:#{appeal_type}"])

tags = ["service:#{APPEAL_TYPE_TO_SERVICE_MAP[appeal_type]}", 'function: secondary form submission to Lighthouse']
StatsD.increment('silent_failure_avoided_no_confirmation', tags:)
end

def record_secondary_form_email_send_failure(secondary_form, e)
submission = secondary_form.appeal_submission
appeal_type = submission.type_of_appeal
params = { submitted_appeal_uuid: submission.submitted_appeal_uuid,
lighthouse_upload_id: secondary_form.guid,
appeal_type:,
message: e.message }
Rails.logger.error('DecisionReview::FailureNotificationEmailJob secondary form error', params)
StatsD.increment("#{STATSD_KEY_PREFIX}.secondary_form.error", tags: ["appeal_type:#{appeal_type}"])
end

def record_evidence_email_send_successful(upload, notification_id)
submission = upload.appeal_submission
appeal_type = submission.type_of_appeal
Expand Down Expand Up @@ -171,5 +235,9 @@ def record_evidence_email_send_failure(upload, e)
def enabled?
Flipper.enabled? :decision_review_failure_notification_email_job_enabled
end

def secondary_forms_enabled?
Flipper.enabled? :decision_review_notify_4142_failures
end
end
end
1 change: 1 addition & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,7 @@ vanotify:
notice_of_disagreement_form_error_email: fake_nod_template_id
supplemental_claim_evidence_error_email: fake_sc_evidence_template_id
supplemental_claim_form_error_email: fake_sc_template_id
supplemental_claim_secondary_form_error_email: fake_sc_secondary_form_template_id
benefits_disability:
api_key: fake_secret
template_id:
Expand Down
1 change: 1 addition & 0 deletions config/settings/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ vanotify:
notice_of_disagreement_form_error_email: fake_nod_template_id
supplemental_claim_evidence_error_email: fake_sc_evidence_template_id
supplemental_claim_form_error_email: fake_sc_template_id
supplemental_claim_secondary_form_error_email: fake_sc_secondary_form_template_id
benefits_management_tools:
api_key: fake_secret
template_id:
Expand Down
158 changes: 158 additions & 0 deletions spec/sidekiq/decision_review/failure_notification_email_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,129 @@
end
end

context 'SecondaryAppealForm records are present with an error status' do
let(:secondary_form_status_error) do
{
status: 'error',
detail: nil,
createDate: 10.days.ago,
updateDate: 5.days.ago
}.to_json
end
let(:secondary_form_status_success) do
{
status: 'vbms',
detail: nil,
createDate: 10.days.ago,
updateDate: 5.days.ago
}.to_json
end
let(:appeal_submission1) { create(:appeal_submission, submitted_appeal_uuid: guid1, type_of_appeal: 'SC') }
let(:appeal_submission2) { create(:appeal_submission, submitted_appeal_uuid: guid2, type_of_appeal: 'SC') }
let!(:secondary_form1) do
create(:secondary_appeal_form4142, appeal_submission: appeal_submission1, status: secondary_form_status_error)
end
let!(:secondary_form2) do
create(:secondary_appeal_form4142, appeal_submission: appeal_submission2,
status: secondary_form_status_success)
end
let(:personalisation) do
{
first_name: mpi_profile.given_names[0],
filename: nil,
date_submitted: secondary_form1.created_at.strftime('%B %d, %Y')
}
end
let(:reference) { "SC-secondary_form-#{secondary_form1.guid}" }

before do
SavedClaim::SupplementalClaim.create(guid: guid1, form: '{}')
SavedClaim::SupplementalClaim.create(guid: guid2, form: '{}')
end

context 'with flag enabled' do
before do
Flipper.enable(:decision_review_notify_4142_failures)
end

it 'sends an email for secondary form and notification date on the secondary form record' do
frozen_time = DateTime.new(2024, 1, 1).utc

Timecop.freeze(frozen_time) do
subject.new.perform
end

expect(secondary_form1.reload.failure_notification_sent_at).to eq frozen_time
expect(secondary_form2.reload.failure_notification_sent_at).to be_nil

expect(vanotify_service).to have_received(:send_email)
.with({ email_address:,
personalisation:,
template_id: 'fake_sc_secondary_form_template_id',
reference: })

expect(vanotify_service).not_to have_received(:send_email)
.with({ email_address: anything,
personalisation: anything,
template_id: 'fake_sc_evidence_template_id' })

expect(vanotify_service).not_to have_received(:send_email).with({ email_address: anything,
personalisation: anything,
template_id: 'fake_sc_template_id' })

expect(vanotify_service).not_to have_received(:send_email).with({ email_address: anything,
personalisation: anything,
template_id: 'fake_nod_template_id' })

expect(vanotify_service).not_to have_received(:send_email).with({ email_address: anything,
personalisation: anything,
template_id: 'fake_hlr_template_id' })

logger_params = [
'DecisionReview::FailureNotificationEmailJob secondary form email queued',
{ submitted_appeal_uuid: guid1,
lighthouse_upload_id: secondary_form1.guid, appeal_type: 'SC', notification_id: }
]
expect(Rails.logger).to have_received(:info).with(*logger_params)

expect(StatsD).to have_received(:increment)
.with('worker.decision_review.failure_notification_email.secondary_form.email_queued',
tags: ['appeal_type:SC'])
.once
end

context 'when already notified' do
before do
secondary_form1.update(failure_notification_sent_at: 1.day.ago)
end

it 'does not send another email' do
subject.new.perform

expect(vanotify_service).not_to have_received(:send_email)
.with({ email_address: anything,
personalisation: anything,
template_id: 'fake_sc_secondary_form_template_id',
reference: anything })

expect(Rails.logger).not_to have_received(:error)
end
end
end

context 'with flag disabled' do
before do
Flipper.disable(:decision_review_notify_4142_failures)
end

it 'does not attempt to notify about secondary form failures' do
expect(SecondaryAppealForm).not_to receive(:where)

subject.new.perform
end
end
end

context 'when an error occurs during form processing' do
let(:email_address) { nil }
let(:message) { 'Failed to retrieve email address' }
Expand Down Expand Up @@ -415,6 +538,41 @@
end
end

context 'when an error occurs during secondary form processing' do
let(:mpi_profile) { nil }

let(:lighthouse_upload_id) { SecureRandom.uuid }
let(:message) { 'Failed to fetch MPI profile' }
let(:secondary_form_status_error) do
{
status: 'error',
detail: nil,
createDate: 10.days.ago,
updateDate: 5.days.ago
}.to_json
end

before do
SavedClaim::SupplementalClaim.create(guid: guid1, form: '{}')
appeal_submission = create(:appeal_submission, type_of_appeal: 'SC', submitted_appeal_uuid: guid1)

create(:secondary_appeal_form4142, guid: lighthouse_upload_id, status: secondary_form_status_error,
appeal_submission:)
end

it 'handles the error and increments the statsd metric' do
expect { subject.new.perform }.not_to raise_exception

logger_params = [
'DecisionReview::FailureNotificationEmailJob secondary form error',
{ submitted_appeal_uuid: guid1, lighthouse_upload_id:, appeal_type: 'SC', message: }
]
expect(Rails.logger).to have_received(:error).with(*logger_params)
expect(StatsD).to have_received(:increment)
.with('worker.decision_review.failure_notification_email.secondary_form.error', tags: ['appeal_type:SC'])
end
end

context 'when there are no errors to email' do
before do
SavedClaim::SupplementalClaim.create(guid: guid1, form: '{}')
Expand Down

0 comments on commit 518ace1

Please sign in to comment.