Skip to content

98603 Callbacks for ZSF logging - IVC CHAMPVA forms #20015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3 changes: 3 additions & 0 deletions config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ features:
cerner_override_757:
actor_type: user
description: This will show the Cerner facility 757 as `isCerner`.
champva_vanotify_custom_callback:
actor_type: user
description: Enables the custom callback_klass when sending IVC CHAMPVA failure emails with VA Notify
Comment on lines +180 to +182
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Feature flag so we can test in staging.

champva_multiple_stamp_retry:
actor_type: user
description: Enables retry of file creation for some errors in CHAMPVA PDF stamping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ def perform # rubocop:disable Metrics/MethodLength
threshold = Settings.vanotify.services.ivc_champva.failure_email_threshold_days.to_i || 7
if elapsed_days >= threshold && !form.email_sent
template_id = "#{form[:form_number]}-FAILURE"
send_failure_email(form, template_id)
additional_context = { form_id: form[:form_number], form_uuid: form[:form_uuid] }
monitor.log_silent_failure_avoided(additional_context)
monitor.track_missing_status_email_sent(form[:form_number])
unless Flipper.enabled?(:champva_vanotify_custom_callback, @current_user)
monitor.log_silent_failure_avoided(additional_context)
monitor.track_missing_status_email_sent(form[:form_number])
end
send_failure_email(form, template_id, additional_context)
Comment on lines -29 to +34
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These calls to the monitor have been moved inside our callback class so they have access to the email delivery status. send_failure_email has been updated to pass the callback_klass to VA Notify.

When our flipper is NOT enabled, we default to logging here per existing functionality.

send_zsf_notification_to_pega(form, 'PEGA-TEAM-ZSF')
elsif elapsed_days >= (threshold - 2) && !form.email_sent
# Give pega 2-day notice if we intend to email a user.
Expand All @@ -51,7 +53,7 @@ def construct_email_payload(form, template_id)
form_number: form.form_number,
file_count: nil,
pega_status: form.pega_status,
created_at: form.created_at.strftime('%B %d, %Y'),
date_submitted: form.created_at.strftime('%B %d, %Y'),
template_id: template_id,
form_uuid: form.form_uuid }
end
Expand All @@ -60,13 +62,27 @@ def construct_email_payload(form, template_id)
#
# @param form [IvcChampvaForm] form object in question
# @param template_id [string] key for template to use in `IvcChampva::Email::EMAIL_TEMPLATE_MAP`
def send_failure_email(form, template_id)
# @param additional_context [hash] contains properties form_id and form_uuid
# (e.g.: {form_id: '10-10d', form_uuid: '12345678-1234-5678-1234-567812345678'})
def send_failure_email(form, template_id, additional_context)
form_data = construct_email_payload(form, template_id)

if Flipper.enabled?(:champva_vanotify_custom_callback, @current_user)
form_data = form_data.merge(
{
callback_klass: 'IvcChampva::ZsfEmailNotificationCallback',
callback_metadata: {
statsd_tag: 'veteran-ivc-champva-forms',
additional_context:
}
}
)
end
Comment on lines +69 to +80
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Passing this callback class to VA Notify when we send the email allows us to gather the email delivery status, which is then going to the DD silent failures dashboard.


ActiveRecord::Base.transaction do
if IvcChampva::Email.new(form_data).send_email
fetch_forms_by_uuid(form[:form_uuid]).update_all(email_sent: true) # rubocop:disable Rails/SkipsModelValidations
else
additional_context = { form_id: form[:form_number], form_uuid: form[:form_uuid] }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is now being passed in to the function above rather than created here.

monitor.log_silent_failure(additional_context)
raise ActiveRecord::Rollback, 'Pega Status Update/Action Required Email send failure'
end
Expand Down
14 changes: 5 additions & 9 deletions modules/ivc_champva/app/services/ivc_champva/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,11 @@ def send_email
VANotify::EmailJob.perform_async(
data[:email],
(data[:template_id] ? EMAIL_TEMPLATE_MAP[data[:template_id]] : EMAIL_TEMPLATE_MAP[data[:form_number]]),
{
'first_name' => data[:first_name],
'last_name' => data[:last_name],
'file_count' => data[:file_count],
'pega_status' => data[:pega_status],
'date_submitted' => data[:created_at],
'form_uuid' => data[:form_uuid]
},
Settings.vanotify.services.ivc_champva.api_key
data.slice(:first_name, :last_name, :file_count, :pega_status, :date_submitted, :form_uuid),
Settings.vanotify.services.ivc_champva.api_key,
# If no callback_klass is provided, should fail safely per va_notify implementation.
# See: https://github.com/department-of-veterans-affairs/vets-api/tree/master/modules/va_notify#how-teams-can-integrate-with-callbacks
{ callback_klass: data[:callback_klass], callback_metadata: data[:callback_metadata] }
)
true
rescue => e
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module IvcChampva
# Callback class used for when we notify a user that their
# form has been missing a Pega status for > `failure_email_threshold_days`.
#
# Modified from https://github.com/department-of-veterans-affairs/vets-api/tree/master/modules/va_notify#how-teams-can-integrate-with-callbacks
#

class ZsfEmailNotificationCallback
def self.call(notification)
# @param ac [hash] contains properties form_id and form_uuid
# (e.g.: {form_id: '10-10d', form_uuid: '12345678-1234-5678-1234-567812345678'})
ac = notification.callback_metadata['additional_context'] # TODO: document this type
case notification.status
when 'delivered'
# success
StatsD.increment('api.vanotify.notifications.delivered')
monitor.log_silent_failure_avoided(ac, email_confirmed: true) # Log with email_confirmed
monitor.track_missing_status_email_sent(ac['form_id']) # e.g., '10-10d'
when 'permanent-failure'
# delivery failed
# possibly log error or increment metric and use the optional metadata - notification.callback_metadata
StatsD.increment('api.vanotify.notifications.permanent_failure')
Rails.logger.error(notification_id: notification.notification_id, source: notification.source_location,
status: notification.status, status_reason: notification.status_reason)
# Log our silent failure since the email never reached the user
monitor.log_silent_failure(ac)
when 'temporary-failure'
# the api will continue attempting to deliver - success is still possible
StatsD.increment('api.vanotify.notifications.permanent_failure')
Rails.logger.error(notification_id: notification.notification_id, source: notification.source_location,
status: notification.status, status_reason: notification.status_reason)
else
StatsD.increment('api.vanotify.notifications.other')
Rails.logger.error(notification_id: notification.notification_id, source: notification.source_location,
status: notification.status, status_reason: notification.status_reason)
end
end

##
# retreive a monitor for tracking
#
# @return [IvcChampva::Monitor]
#
def monitor
@monitor ||= IvcChampva::Monitor.new
end
end
end
12 changes: 3 additions & 9 deletions modules/ivc_champva/spec/services/email_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,9 @@
expect(VANotify::EmailJob).to receive(:perform_async).with(
data[:email],
Settings.vanotify.services.ivc_champva.template_id.form_10_10d_email,
{
'first_name' => data[:first_name],
'last_name' => data[:last_name],
'file_count' => data[:file_count],
'pega_status' => data[:pega_status],
'date_submitted' => data[:created_at],
'form_uuid' => data[:form_uuid]
},
Settings.vanotify.services.ivc_champva.api_key
data.slice(:first_name, :last_name, :file_count, :pega_status, :date_submitted, :form_uuid),
Settings.vanotify.services.ivc_champva.api_key,
{ callback_klass: nil, callback_metadata: nil }
Comment on lines -35 to +37
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For documentation/traceability: with this refactor I inadvertently introduced this bug.

)
subject.send_email
end
Expand Down
Loading