From 66a861b659133c9b066795e9ba57d7271995d0e4 Mon Sep 17 00:00:00 2001 From: Michael Marchand <35391349+MarchandMD@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:39:58 -0600 Subject: [PATCH 1/4] Migrate HealthCareApplication to use VANotify::V2::QueueEmailJob for failure emails with feature toggle --- app/models/health_care_application.rb | 32 +++-- config/features.yml | 3 + spec/models/health_care_application_spec.rb | 134 +++++++++++--------- 3 files changed, 99 insertions(+), 70 deletions(-) diff --git a/app/models/health_care_application.rb b/app/models/health_care_application.rb index 64c4ebbf547..681228cb4b9 100644 --- a/app/models/health_care_application.rb +++ b/app/models/health_care_application.rb @@ -19,6 +19,14 @@ class HealthCareApplication < ApplicationRecord 'service:healthcare-application', 'function: 10-10EZ async form submission' ].freeze + API_KEY_PATH = 'Settings.vanotify.services.health_apps_1010.api_key' + CALLBACK_METADATA = { + callback_metadata: { + notification_type: 'error', + form_number: FORM_ID, + statsd_tags: DD_ZSF_TAGS + } + }.freeze LOCKBOX = Lockbox.new(key: Settings.lockbox.master_key, encode: true) attr_accessor :user, :google_analytics_client_id, :form @@ -311,19 +319,21 @@ def log_submission_failure_details def send_failure_email first_name = parsed_form.dig('veteranFullName', 'first') template_id = Settings.vanotify.services.health_apps_1010.template_id.form1010_ez_failure_email - api_key = Settings.vanotify.services.health_apps_1010.api_key - salutation = first_name ? "Dear #{first_name}," : '' - metadata = - { - callback_metadata: { - notification_type: 'error', - form_number: FORM_ID, - statsd_tags: DD_ZSF_TAGS - } - } - VANotify::EmailJob.perform_async(email, template_id, { 'salutation' => salutation }, api_key, metadata) + if Flipper.enabled?(:va_notify_v2_health_care_application_model_send_failure_email) + VANotify::V2::QueueEmailJob.enqueue( + email, + template_id, + { 'salutation' => salutation }, + API_KEY_PATH, + CALLBACK_METADATA + ) + else + api_key = Settings.vanotify.services.health_apps_1010.api_key + VANotify::EmailJob.perform_async(email, template_id, { 'salutation' => salutation }, api_key, CALLBACK_METADATA) + end + StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.submission_failure_email_sent") rescue => e Rails.logger.error('[10-10EZ] - Failure sending Submission Failure Email', { exception: e }) diff --git a/config/features.yml b/config/features.yml index 00e6327cc5c..3513aa69a28 100644 --- a/config/features.yml +++ b/config/features.yml @@ -2970,6 +2970,9 @@ features: va_notify_v2_form1010ezr_submission: actor_type: user description: If enabled, HCA::EzrSubmissionJob will use VANotify::V2::QueueEmailJob instead of VANotify::EmailJob + va_notify_v2_health_care_application_model_send_failure_email: + actor_type: user + description: If enabled, HealthCareApplication will use VANotify::V2::QueueEmailJob instead of VANotify::EmailJob va_notify_v2_in_progress_form_reminder: actor_type: user description: If enabled, InProgressFormReminder uses VANotify::V2::QueueUserAccountJob instead of VANotify::UserAccountJob diff --git a/spec/models/health_care_application_spec.rb b/spec/models/health_care_application_spec.rb index d8f6af98f08..3368ba319f2 100644 --- a/spec/models/health_care_application_spec.rb +++ b/spec/models/health_care_application_spec.rb @@ -727,6 +727,7 @@ def self.expect_job_submission(job) before do allow(VANotify::EmailJob).to receive(:perform_async) + allow(VANotify::V2::QueueEmailJob).to receive(:enqueue) allow_any_instance_of(MPI::Service).to receive( :find_profile_by_attributes ).and_return( @@ -738,7 +739,6 @@ def self.expect_job_submission(job) context 'has form' do context 'with email address' do let(:email_address) { health_care_application.parsed_form['email'] } - let(:api_key) { Settings.vanotify.services.health_apps_1010.api_key } let(:template_id) { Settings.vanotify.services.health_apps_1010.template_id.form1010_ez_failure_email } let(:callback_metadata) do { @@ -749,71 +749,77 @@ def self.expect_job_submission(job) } } end + let(:standard_error) { StandardError.new('Test error') } - let(:template_params) do - [ - email_address, - template_id, - { - 'salutation' => "Dear #{health_care_application.parsed_form['veteranFullName']['first']}," - }, - api_key, - callback_metadata - ] - end + context 'when va_notify_v2_health_care_application_model_send_failure_email is enabled' do + before do + allow(Flipper).to receive(:enabled?).with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(true) + end - let(:standard_error) { StandardError.new('Test error') } + it 'sends a failure email using V2 QueueEmailJob' do + subject + expect(VANotify::V2::QueueEmailJob).to have_received(:enqueue).with( + email_address, + template_id, + { 'salutation' => "Dear #{health_care_application.parsed_form['veteranFullName']['first']}," }, + 'Settings.vanotify.services.health_apps_1010.api_key', + callback_metadata + ) + end - it 'sends a failure email to the email address provided on the form' do - subject - expect(VANotify::EmailJob).to have_received(:perform_async).with(*template_params) - end + it 'increments statsd' do + expect { subject }.to trigger_statsd_increment("#{statsd_key_prefix}.submission_failure_email_sent") + end - it 'logs error if email job throws error' do - allow(VANotify::EmailJob).to receive(:perform_async).and_raise(standard_error) - allow(Rails.logger).to receive(:error) - expect(Rails.logger).to receive(:error).with( - '[10-10EZ] - Failure sending Submission Failure Email', - { exception: standard_error } - ) - expect(Rails.logger).to receive(:info).with( - '[10-10EZ] - HCA total failure', - { - first_initial: 'F', - middle_initial: 'M', - last_initial: 'Z' - } - ) - expect { subject }.not_to raise_error - end + it 'logs error if email job throws error' do + allow(VANotify::V2::QueueEmailJob).to receive(:enqueue).and_raise(standard_error) + allow(Rails.logger).to receive(:error) + expect(Rails.logger).to receive(:error).with( + '[10-10EZ] - Failure sending Submission Failure Email', + { exception: standard_error } + ) + expect { subject }.not_to raise_error + end - it 'increments statsd' do - expect { subject }.to trigger_statsd_increment("#{statsd_key_prefix}.submission_failure_email_sent") + context 'without first name' do + subject do + health_care_application.parsed_form['veteranFullName'] = nil + super() + end + + it 'sends a failure email with empty salutation' do + subject + expect(VANotify::V2::QueueEmailJob).to have_received(:enqueue).with( + email_address, + template_id, + { 'salutation' => '' }, + 'Settings.vanotify.services.health_apps_1010.api_key', + callback_metadata + ) + end + end end - context 'without first name' do - subject do - health_care_application.parsed_form['veteranFullName'] = nil - super() + context 'when va_notify_v2_health_care_application_model_send_failure_email is disabled' do + before do + allow(Flipper).to receive(:enabled?).with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(false) end - let(:template_params_no_name) do - [ + let(:api_key) { Settings.vanotify.services.health_apps_1010.api_key } + + it 'sends a failure email using V1 EmailJob' do + subject + expect(VANotify::EmailJob).to have_received(:perform_async).with( email_address, template_id, - { - 'salutation' => '' - }, + { 'salutation' => "Dear #{health_care_application.parsed_form['veteranFullName']['first']}," }, api_key, callback_metadata - ] + ) end - let(:standard_error) { StandardError.new('Test error') } - - it 'sends a failure email without personalisations to the email address provided on the form' do - subject - expect(VANotify::EmailJob).to have_received(:perform_async).with(*template_params_no_name) + it 'increments statsd' do + expect { subject }.to trigger_statsd_increment("#{statsd_key_prefix}.submission_failure_email_sent") end it 'logs error if email job throws error' do @@ -823,16 +829,26 @@ def self.expect_job_submission(job) '[10-10EZ] - Failure sending Submission Failure Email', { exception: standard_error } ) - expect(Rails.logger).to receive(:info).with( - '[10-10EZ] - HCA total failure', - { - first_initial: 'no initial provided', - middle_initial: 'no initial provided', - last_initial: 'no initial provided' - } - ) expect { subject }.not_to raise_error end + + context 'without first name' do + subject do + health_care_application.parsed_form['veteranFullName'] = nil + super() + end + + it 'sends a failure email with empty salutation' do + subject + expect(VANotify::EmailJob).to have_received(:perform_async).with( + email_address, + template_id, + { 'salutation' => '' }, + api_key, + callback_metadata + ) + end + end end end From 6b1a429fe82d318848fab5f8f9cd1d293b24f53d Mon Sep 17 00:00:00 2001 From: Michael Marchand <35391349+MarchandMD@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:43:44 -0600 Subject: [PATCH 2/4] lint --- spec/models/health_care_application_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/models/health_care_application_spec.rb b/spec/models/health_care_application_spec.rb index 3368ba319f2..640f66c4f64 100644 --- a/spec/models/health_care_application_spec.rb +++ b/spec/models/health_care_application_spec.rb @@ -753,7 +753,8 @@ def self.expect_job_submission(job) context 'when va_notify_v2_health_care_application_model_send_failure_email is enabled' do before do - allow(Flipper).to receive(:enabled?).with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(true) + allow(Flipper).to receive(:enabled?) + .with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(true) end it 'sends a failure email using V2 QueueEmailJob' do @@ -802,7 +803,8 @@ def self.expect_job_submission(job) context 'when va_notify_v2_health_care_application_model_send_failure_email is disabled' do before do - allow(Flipper).to receive(:enabled?).with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(false) + allow(Flipper).to receive(:enabled?) + .with(:va_notify_v2_health_care_application_model_send_failure_email).and_return(false) end let(:api_key) { Settings.vanotify.services.health_apps_1010.api_key } From 09146a4e16a58c837ccde513fadceada120c2c2e Mon Sep 17 00:00:00 2001 From: Michael Marchand <35391349+MarchandMD@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:14:32 -0600 Subject: [PATCH 3/4] Refactor failure email handling in HealthCareApplication to use callback metadata directly in enqueue and perform_async methods --- app/models/health_care_application.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/models/health_care_application.rb b/app/models/health_care_application.rb index 681228cb4b9..594afcf0619 100644 --- a/app/models/health_care_application.rb +++ b/app/models/health_care_application.rb @@ -19,14 +19,6 @@ class HealthCareApplication < ApplicationRecord 'service:healthcare-application', 'function: 10-10EZ async form submission' ].freeze - API_KEY_PATH = 'Settings.vanotify.services.health_apps_1010.api_key' - CALLBACK_METADATA = { - callback_metadata: { - notification_type: 'error', - form_number: FORM_ID, - statsd_tags: DD_ZSF_TAGS - } - }.freeze LOCKBOX = Lockbox.new(key: Settings.lockbox.master_key, encode: true) attr_accessor :user, :google_analytics_client_id, :form @@ -321,17 +313,25 @@ def send_failure_email template_id = Settings.vanotify.services.health_apps_1010.template_id.form1010_ez_failure_email salutation = first_name ? "Dear #{first_name}," : '' + callback_metadata = { + callback_metadata: { + notification_type: 'error', + form_number: FORM_ID, + statsd_tags: DD_ZSF_TAGS + } + } + if Flipper.enabled?(:va_notify_v2_health_care_application_model_send_failure_email) VANotify::V2::QueueEmailJob.enqueue( email, template_id, { 'salutation' => salutation }, - API_KEY_PATH, - CALLBACK_METADATA + 'Settings.vanotify.services.health_apps_1010.api_key', + callback_metadata ) else api_key = Settings.vanotify.services.health_apps_1010.api_key - VANotify::EmailJob.perform_async(email, template_id, { 'salutation' => salutation }, api_key, CALLBACK_METADATA) + VANotify::EmailJob.perform_async(email, template_id, { 'salutation' => salutation }, api_key, callback_metadata) end StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.submission_failure_email_sent") From 0554256da8deb80277f19a87b0e83e8998a0b9d5 Mon Sep 17 00:00:00 2001 From: Michael Marchand <35391349+MarchandMD@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:24:47 -0600 Subject: [PATCH 4/4] lint --- app/models/health_care_application.rb | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/app/models/health_care_application.rb b/app/models/health_care_application.rb index 594afcf0619..dc64848c0b4 100644 --- a/app/models/health_care_application.rb +++ b/app/models/health_care_application.rb @@ -311,27 +311,17 @@ def log_submission_failure_details def send_failure_email first_name = parsed_form.dig('veteranFullName', 'first') template_id = Settings.vanotify.services.health_apps_1010.template_id.form1010_ez_failure_email - salutation = first_name ? "Dear #{first_name}," : '' - - callback_metadata = { - callback_metadata: { - notification_type: 'error', - form_number: FORM_ID, - statsd_tags: DD_ZSF_TAGS - } - } + personalisation = { 'salutation' => first_name ? "Dear #{first_name}," : '' } + metadata = { callback_metadata: { notification_type: 'error', form_number: FORM_ID, statsd_tags: DD_ZSF_TAGS } } if Flipper.enabled?(:va_notify_v2_health_care_application_model_send_failure_email) VANotify::V2::QueueEmailJob.enqueue( - email, - template_id, - { 'salutation' => salutation }, - 'Settings.vanotify.services.health_apps_1010.api_key', - callback_metadata + email, template_id, personalisation, + 'Settings.vanotify.services.health_apps_1010.api_key', metadata ) else api_key = Settings.vanotify.services.health_apps_1010.api_key - VANotify::EmailJob.perform_async(email, template_id, { 'salutation' => salutation }, api_key, callback_metadata) + VANotify::EmailJob.perform_async(email, template_id, personalisation, api_key, metadata) end StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.submission_failure_email_sent")