Skip to content

Commit

Permalink
[CST] Send emails to users when the EVSS::DocumentUpload sidekiq job …
Browse files Browse the repository at this point in the history
…exhausts its retries (#18890)
  • Loading branch information
jerekshoe authored Oct 16, 2024
1 parent 99f3791 commit 7ab5772
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 2 deletions.
5 changes: 3 additions & 2 deletions app/services/evss_claim_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def request_decision(claim)
# upload file to s3 and enqueue job to upload to EVSS, used by Claim Status Tool
# EVSS::DocumentsService is where the uploading of documents actually happens
def upload_document(evss_claim_document)
uploader = EVSSClaimDocumentUploader.new(@user.uuid, evss_claim_document.uploader_ids)
uploader = EVSSClaimDocumentUploader.new(@user.user_account_uuid, evss_claim_document.uploader_ids)
uploader.store!(evss_claim_document.file_obj)

# the uploader sanitizes the filename before storing, so set our doc to match
Expand All @@ -63,7 +63,8 @@ def upload_document(evss_claim_document)
headers = auth_headers.clone
headers_supplemented = supplement_auth_headers(evss_claim_document.evss_claim_id, headers)

job_id = EVSS::DocumentUpload.perform_async(headers, @user.uuid, evss_claim_document.to_serializable_hash)
job_id = EVSS::DocumentUpload.perform_async(headers, @user.user_account_uuid,
evss_claim_document.to_serializable_hash)

record_workaround('document_upload', evss_claim_document.evss_claim_id, job_id) if headers_supplemented

Expand Down
58 changes: 58 additions & 0 deletions app/sidekiq/evss/document_upload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ class EVSS::DocumentUpload
include Sidekiq::Job
extend Logging::ThirdPartyTransaction::MethodWrapper

FILENAME_EXTENSION_MATCHER = /\.\w*$/
OBFUSCATED_CHARACTER_MATCHER = /[a-zA-Z\d]/

NOTIFY_SETTINGS = Settings.vanotify.services.benefits_management_tools
MAILER_TEMPLATE_ID = NOTIFY_SETTINGS.template_id.evidence_submission_failure_email

attr_accessor :auth_headers, :user_uuid, :document_hash

wrap_with_logging(
Expand All @@ -29,6 +35,31 @@ class EVSS::DocumentUpload
rand(3600..3660) if count < 9
end

sidekiq_retries_exhausted do |msg, _ex|
# There should be 3 args:
# 1) Auth headers needed to authenticate with EVSS
# 2) The uuid of the record in the UserAccount table
# 3) Document metadata

next unless Flipper.enabled?('cst_send_evidence_failure_emails')

icn = UserAccount.find(msg['args'][1]).icn
first_name = msg['args'].first['va_eauth_firstName'].titleize
filename = obscured_filename(msg['args'][2]['file_name'])
date_submitted = format_issue_instant_for_mailers(msg['created_at'])

notify_client.send_email(
recipient_identifier: { id_value: icn, id_type: 'ICN' },
template_id: MAILER_TEMPLATE_ID,
personalisation: { first_name:, filename:, date_submitted: }
)

::Rails.logger.info('EVSS::DocumentUpload exhaustion handler email sent')
rescue => e
::Rails.logger.error('EVSS::DocumentUpload exhaustion handler email error',
{ message: e.message })
end

def perform(auth_headers, user_uuid, document_hash)
@auth_headers = auth_headers
@user_uuid = user_uuid
Expand All @@ -40,6 +71,33 @@ def perform(auth_headers, user_uuid, document_hash)
clean_up!
end

def self.obscured_filename(original_filename)
extension = original_filename[FILENAME_EXTENSION_MATCHER]
filename_without_extension = original_filename.gsub(FILENAME_EXTENSION_MATCHER, '')

if filename_without_extension.length > 5
# Obfuscate with the letter 'X'; we cannot obfuscate with special characters such as an asterisk,
# as these filenames appear in VA Notify Mailers and their templating engine uses markdown.
# Therefore, special characters can be interpreted as markdown and introduce formatting issues in the mailer
obfuscated_portion = filename_without_extension[3..-3].gsub(OBFUSCATED_CHARACTER_MATCHER, 'X')
filename_without_extension[0..2] + obfuscated_portion + filename_without_extension[-2..] + extension
else
original_filename
end
end

def self.format_issue_instant_for_mailers(issue_instant)
# We want to return all times in EDT
timestamp = Time.at(issue_instant).in_time_zone('America/New_York')

# We display dates in mailers in the format "May 1, 2024 3:01 p.m. EDT"
timestamp.strftime('%B %-d, %Y %-l:%M %P %Z').sub(/([ap])m/, '\1.m.')
end

def self.notify_client
VaNotify::Service.new(NOTIFY_SETTINGS.api_key)
end

private

def validate_document!
Expand Down
4 changes: 4 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,10 @@ vanotify:
form526_document_upload_failure_notification_template_id: form526_document_upload_failure_notification_template_id
form4142_upload_failure_notification_template_id: form4142_upload_failure_notification_template_id
form0781_upload_failure_notification_template_id: form0781_upload_failure_notification_template_id
benefits_management_tools:
api_key: fake_secret
template_id:
evidence_submission_failure_email: fake_template_id
ivc_champva:
api_key: fake_secret
template_id:
Expand Down
4 changes: 4 additions & 0 deletions config/settings/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ vanotify:
claim_submission_duplicate_text: oh_fake_duplicate_template_id
claim_submission_timeout_text: oh_fake_timeout_template_id
claim_submission_error_text: oh_fake_error_template_id
benefits_management_tools:
api_key: fake_secret
template_id:
evidence_submission_failure_email: fake_template_id

res:
base_url: https://fake_url.com
Expand Down
70 changes: 70 additions & 0 deletions spec/sidekiq/evss/document_upload_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
require 'evss/document_upload'

RSpec.describe EVSS::DocumentUpload, type: :job do
subject { described_class }

let(:client_stub) { instance_double('EVSS::DocumentsService') }
let(:notify_client_stub) { instance_double('VaNotify::Sidekiq') }
let(:uploader_stub) { instance_double('EVSSClaimDocumentUploader') }

let(:user_account) { create(:user_account) }
let(:user_account_uuid) { user_account.id }
let(:user) { FactoryBot.create(:user, :loa3) }
let(:filename) { 'doctors-note.pdf' }
let(:document_data) do
Expand All @@ -19,6 +25,18 @@
end
let(:auth_headers) { EVSS::AuthHeaders.new(user).to_h }

let(:issue_instant) { Time.now.to_i }
let(:args) do
{
'args' => [{ 'va_eauth_firstName' => 'Bob' }, user_account_uuid, { 'file_name' => filename }],
'created_at' => issue_instant
}
end

before do
allow(Rails.logger).to receive(:info)
end

it 'retrieves the file and uploads to EVSS' do
allow(EVSSClaimDocumentUploader).to receive(:new) { uploader_stub }
allow(EVSS::DocumentsService).to receive(:new) { client_stub }
Expand All @@ -29,4 +47,56 @@
expect(client_stub).to receive(:upload).with(file, document_data)
described_class.new.perform(auth_headers, user.uuid, document_data.to_serializable_hash)
end

context 'when cst_send_evidence_failure_emails is enabled' do
before do
Flipper.enable(:cst_send_evidence_failure_emails)
end

let(:formatted_submit_date) do
# We want to return all times in EDT
timestamp = Time.at(issue_instant).in_time_zone('America/New_York')

# We display dates in mailers in the format "May 1, 2024 3:01 p.m. EDT"
timestamp.strftime('%B %-d, %Y %-l:%M %P %Z').sub(/([ap])m/, '\1.m.')
end

it 'enqueues a failure notification mailer to send to the veteran' do
allow(VaNotify::Service).to receive(:new) { notify_client_stub }

subject.within_sidekiq_retries_exhausted_block(args) do
expect(notify_client_stub).to receive(:send_email).with(
{
recipient_identifier: { id_value: user_account.icn, id_type: 'ICN' },
template_id: 'fake_template_id',
personalisation: {
first_name: 'Bob',
filename: 'docXXXX-XXte.pdf',
date_submitted: formatted_submit_date
}
}
)

expect(Rails.logger)
.to receive(:info)
.with('EVSS::DocumentUpload exhaustion handler email sent')
end
end
end

context 'when cst_send_evidence_failure_emails is disabled' do
before do
Flipper.disable(:cst_send_evidence_failure_emails)
end

let(:issue_instant) { Time.now.to_i }

it 'does not enqueue a failure notification mailer to send to the veteran' do
allow(VaNotify::Service).to receive(:new) { notify_client_stub }

subject.within_sidekiq_retries_exhausted_block(args) do
expect(notify_client_stub).not_to receive(:send_email)
end
end
end
end

0 comments on commit 7ab5772

Please sign in to comment.