diff --git a/modules/appeals_api/app/models/appeals_api/higher_level_review.rb b/modules/appeals_api/app/models/appeals_api/higher_level_review.rb index 4275e6cc55d..0a6251cda7a 100644 --- a/modules/appeals_api/app/models/appeals_api/higher_level_review.rb +++ b/modules/appeals_api/app/models/appeals_api/higher_level_review.rb @@ -256,7 +256,7 @@ def update_status(status:, code: nil, detail: nil, raise_on_error: false) return if auth_headers.blank? # Go no further if we've removed PII if status == 'submitted' && email_present? - AppealsApi::AppealSubmittedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) + AppealsApi::AppealReceivedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) end end end diff --git a/modules/appeals_api/app/models/appeals_api/notice_of_disagreement.rb b/modules/appeals_api/app/models/appeals_api/notice_of_disagreement.rb index 06619a0b940..31a07d91ec8 100644 --- a/modules/appeals_api/app/models/appeals_api/notice_of_disagreement.rb +++ b/modules/appeals_api/app/models/appeals_api/notice_of_disagreement.rb @@ -269,7 +269,7 @@ def update_status(status:, code: nil, detail: nil, raise_on_error: false) return if auth_headers.blank? # Go no further if we've removed PII if status == 'submitted' && email_present? - AppealsApi::AppealSubmittedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) + AppealsApi::AppealReceivedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) end end end diff --git a/modules/appeals_api/app/models/appeals_api/supplemental_claim.rb b/modules/appeals_api/app/models/appeals_api/supplemental_claim.rb index cc282b272fe..2bfafbf9bae 100644 --- a/modules/appeals_api/app/models/appeals_api/supplemental_claim.rb +++ b/modules/appeals_api/app/models/appeals_api/supplemental_claim.rb @@ -255,7 +255,7 @@ def update_status(status:, code: nil, detail: nil, raise_on_error: false) return if auth_headers.blank? # Go no further if we've removed PII if status == 'submitted' && email_present? - AppealsApi::AppealSubmittedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) + AppealsApi::AppealReceivedJob.perform_async(id, self.class.name, appellant_local_time.iso8601) end end end diff --git a/modules/appeals_api/app/sidekiq/appeals_api/appeal_received_job.rb b/modules/appeals_api/app/sidekiq/appeals_api/appeal_received_job.rb index 28fb96a93e3..d9577e8c866 100644 --- a/modules/appeals_api/app/sidekiq/appeals_api/appeal_received_job.rb +++ b/modules/appeals_api/app/sidekiq/appeals_api/appeal_received_job.rb @@ -9,152 +9,82 @@ class AppealReceivedJob STATSD_KEY_PREFIX = 'api.appeals.received' STATSD_CLAIMANT_EMAIL_SENT = "#{STATSD_KEY_PREFIX}.claimant.email.sent".freeze - # @param [Hash] opts - # @option opts [String] :receipt_event The callback indicating which appeal was received. Required. - # @option opts [Hash] :email_identifier The values identifying the receiving email address. Required - # @option email_identifier [String] :id_value Either the email or - # ICN (Integration Control Number - generated by the Master Patient Index)associated with the appellant. Required. - # @option email_identifier [String] :id_type The type of id value provided: 'email' or 'ICN'. Required. - # @option opts [String] :first_name First name of Veteran associated with the appeal. Required. - # @option opts [Datetime] :date_submitted The date of the appeal's submission. ISO8601 format. Required. - # @option opts [String] :guid The related appeal's ID. Required. - # @option opts [String] :claimant_email The non-Veteran claimant's email address. - # @option opts [String] :claimant_first_name The non-Veteran claimant's first name. - - def perform(opts) - @opts = opts - + # rubocop:disable Metrics/MethodLength + # Sends an email to a veteran or claimant stating that their appeal has been submitted + # @param [String] appeal_id The id of the appeal record + # @param [String] appeal_class_str The classname of the appeal as a string + # @param [String] date_submitted_str The date the appeal was submitted in ISO8601 string format + def perform(appeal_id, appeal_class_str, date_submitted_str) return unless FeatureFlipper.send_email? - return Rails.logger.error 'AppealReceived: Missing required keys' unless required_keys? - - send(opts['receipt_event'].to_sym) - end - - def hlr_received - return unless Flipper.enabled?(:decision_review_hlr_email) - - return log_error(guid, 'HLR') unless valid_email_identifier? - - template_type = 'higher_level_review_received' - template_name, template_id = template_id(template_type) - - return Rails.logger.error "AppealReceived: could not find template id for #{template_name}" if template_id.blank? - - vanotify_service.send_email(params({ template_id: })) - StatsD.increment(STATSD_CLAIMANT_EMAIL_SENT, tags: { appeal_type: 'hlr', claimant_type: }) - end - - def nod_received - return unless Flipper.enabled?(:decision_review_nod_email) - - return log_error(guid, 'NOD') unless valid_email_identifier? - - template_type = 'notice_of_disagreement_received' - template_name, template_id = template_id(template_type) - return Rails.logger.error "AppealReceived: could not find template id for #{template_name}" if template_id.blank? - - vanotify_service.send_email(params({ template_id: })) - StatsD.increment(STATSD_CLAIMANT_EMAIL_SENT, tags: { appeal_type: 'nod', claimant_type: }) - end - - def sc_received - return unless Flipper.enabled?(:decision_review_sc_email) - - return log_error(guid, 'SC') unless valid_email_identifier? - - template_type = 'supplemental_claim_received' - template_name, template_id = template_id(template_type) - - return Rails.logger.error "AppealReceived: could not find template id for #{template_name}" if template_id.blank? - - vanotify_service.send_email(params({ template_id: })) - StatsD.increment(STATSD_CLAIMANT_EMAIL_SENT, tags: { appeal_type: 'sc', claimant_type: }) - end - - private - - attr_accessor :opts - - def vanotify_service - @vanotify_service ||= VaNotify::Service.new(Settings.vanotify.services.lighthouse.api_key) - end - - def params(template_opts) - [ - lookup, - template_opts, - personalisation - ].reduce(&:merge) - end - - def lookup - return { email_address: opts['claimant_email'] } if opts['claimant_email'].present? - - if opts['email_identifier']['id_type'] == 'email' - { email_address: opts['email_identifier']['id_value'] } - else - { recipient_identifier: { id_value: opts['email_identifier']['id_value'], - id_type: opts['email_identifier']['id_type'] } } + if appeal_id.blank? || appeal_class_str.blank? || date_submitted_str.blank? + argument_list = [appeal_id, appeal_class_str, date_submitted_str] + Rails.logger.error("#{self.class.name}: Missing arguments: Received #{argument_list.join(', ')}") + return end - end - def template_id(template) - t = claimant? ? "#{template}_claimant" : template - template_id = Settings.vanotify.services.lighthouse.template_id.public_send(t) + appeal = appeal_class_str.constantize.find(appeal_id) - [t, template_id] - end - - def personalisation - p = { 'date_submitted' => date_submitted } - if claimant? - p['first_name'] = opts['claimant_first_name'] - p['veterans_name'] = opts['first_name'] - else - p['first_name'] = opts['first_name'] + unless appeal.form_data.present? && appeal.auth_headers.present? + Rails.logger.error("#{self.class.name}: Missing PII for #{appeal_class_str} #{appeal_id}") + return end - { personalisation: p } - end - def log_error(guid, type) - Rails.logger.error "No lookup value present for AppealsApi::AppealReceived notification #{type} - GUID: #{guid}" - end + appeal_type_name = appeal.class.name.demodulize.snakecase + template_name = "#{appeal_type_name}_received#{appeal.non_veteran_claimant? ? '_claimant' : ''}" + template_id = Settings.vanotify.services.lighthouse.template_id[template_name] - def guid - opts['guid'] - end - - def date_submitted - @date_submitted ||= DateTime.iso8601(opts['date_submitted']).strftime('%B %d, %Y') - end + if template_id.blank? + Rails.logger.error("#{self.class.name}: could not find VANotify template id for '#{template_name}'") + return + end - def valid_email_identifier? - if claimant? - opts['claimant_email'].present? + date_submitted = DateTime.iso8601(date_submitted_str).strftime('%B %d, %Y') + + if appeal.non_veteran_claimant? + vanotify_service.send_email( + { + email_address: appeal.claimant.email, + personalisation: { + date_submitted:, + first_name: appeal.claimant.first_name, + veterans_name: appeal.veteran.first_name + }, + template_id: + } + ) else - required_email_identifier_keys.all? { |k| opts.dig('email_identifier', k).present? } + identifier = if appeal.email_identifier[:id_type] == 'email' + { email_address: appeal.email_identifier[:id_value] } + else + { recipient_identifier: appeal.email_identifier } + end + + vanotify_service.send_email( + { + **identifier, + personalisation: { + date_submitted:, + first_name: appeal.veteran.first_name + }, + template_id: + } + ) end - end - - def claimant? - opts['claimant_first_name'].present? || opts['claimant_email'].present? - end - def claimant_type - claimant? ? 'non-veteran' : 'veteran' + StatsD.increment(STATSD_CLAIMANT_EMAIL_SENT, tags: { + appeal_type: appeal.class.name.demodulize.scan(/\p{Upper}/).map(&:downcase).join, + claimant_type: appeal.non_veteran_claimant? ? 'non-veteran' : 'veteran' + }) + rescue ActiveRecord::RecordNotFound + Rails.logger.error("#{self.class.name}: Unable to find #{appeal_class_str} with id '#{appeal_id}'") + rescue Date::Error + Rails.logger.error("#{self.class.name}: Invalid date format: '#{date_submitted_str}' must be in iso8601 format") end + # rubocop:enable Metrics/MethodLength - def required_email_identifier_keys - %w[id_type id_value] - end - - def required_keys? - required_keys.all? { |k| opts.key?(k) } - end - - def required_keys - %w[receipt_event guid email_identifier date_submitted first_name] + def vanotify_service + @vanotify_service ||= VaNotify::Service.new(Settings.vanotify.services.lighthouse.api_key) end end end diff --git a/modules/appeals_api/app/sidekiq/appeals_api/appeal_submitted_job.rb b/modules/appeals_api/app/sidekiq/appeals_api/appeal_submitted_job.rb deleted file mode 100644 index d1a109e0c9d..00000000000 --- a/modules/appeals_api/app/sidekiq/appeals_api/appeal_submitted_job.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -require 'sidekiq' -require 'feature_flipper' - -module AppealsApi - class AppealSubmittedJob - include Sidekiq::Job - # These constants are set to match the previous version of this job, which was AppealsApi::AppealReceivedJob - STATSD_KEY_PREFIX = 'api.appeals.received' - STATSD_CLAIMANT_EMAIL_SENT = "#{STATSD_KEY_PREFIX}.claimant.email.sent".freeze - - # rubocop:disable Metrics/MethodLength - # Sends an email to a veteran or claimant stating that their appeal has been submitted - # @param [String] appeal_id The id of the appeal record - # @param [String] appeal_class_str The classname of the appeal as a string - # @param [String] date_submitted_str The date the appeal was submitted in ISO8601 string format - def perform(appeal_id, appeal_class_str, date_submitted_str) - return unless FeatureFlipper.send_email? - - if appeal_id.blank? || appeal_class_str.blank? || date_submitted_str.blank? - argument_list = [appeal_id, appeal_class_str, date_submitted_str] - Rails.logger.error("#{self.class.name}: Missing arguments: Received #{argument_list.join(', ')}") - return - end - - appeal = appeal_class_str.constantize.find(appeal_id) - - unless appeal.form_data.present? && appeal.auth_headers.present? - Rails.logger.error("#{self.class.name}: Missing PII for #{appeal_class_str} #{appeal_id}") - return - end - - appeal_type_name = appeal.class.name.demodulize.snakecase - template_name = "#{appeal_type_name}_received#{appeal.non_veteran_claimant? ? '_claimant' : ''}" - template_id = Settings.vanotify.services.lighthouse.template_id[template_name] - - if template_id.blank? - Rails.logger.error("#{self.class.name}: could not find VANotify template id for '#{template_name}'") - return - end - - date_submitted = DateTime.iso8601(date_submitted_str).strftime('%B %d, %Y') - - if appeal.non_veteran_claimant? - vanotify_service.send_email( - { - email_address: appeal.claimant.email, - personalisation: { - date_submitted:, - first_name: appeal.claimant.first_name, - veterans_name: appeal.veteran.first_name - }, - template_id: - } - ) - else - identifier = if appeal.email_identifier[:id_type] == 'email' - { email_address: appeal.email_identifier[:id_value] } - else - { recipient_identifier: appeal.email_identifier } - end - - vanotify_service.send_email( - { - **identifier, - personalisation: { - date_submitted:, - first_name: appeal.veteran.first_name - }, - template_id: - } - ) - end - - StatsD.increment(STATSD_CLAIMANT_EMAIL_SENT, tags: { - appeal_type: appeal.class.name.demodulize.scan(/\p{Upper}/).map(&:downcase).join, - claimant_type: appeal.non_veteran_claimant? ? 'non-veteran' : 'veteran' - }) - rescue ActiveRecord::RecordNotFound - Rails.logger.error("#{self.class.name}: Unable to find #{appeal_class_str} with id '#{appeal_id}'") - rescue Date::Error - Rails.logger.error("#{self.class.name}: Invalid date format: '#{date_submitted_str}' must be in iso8601 format") - end - # rubocop:enable Metrics/MethodLength - - def vanotify_service - @vanotify_service ||= VaNotify::Service.new(Settings.vanotify.services.lighthouse.api_key) - end - end -end diff --git a/modules/appeals_api/spec/sidekiq/appeal_submitted_job_spec.rb b/modules/appeals_api/spec/sidekiq/appeal_received_job_spec.rb similarity index 99% rename from modules/appeals_api/spec/sidekiq/appeal_submitted_job_spec.rb rename to modules/appeals_api/spec/sidekiq/appeal_received_job_spec.rb index 8d53dea4d7c..d9443f499f4 100644 --- a/modules/appeals_api/spec/sidekiq/appeal_submitted_job_spec.rb +++ b/modules/appeals_api/spec/sidekiq/appeal_received_job_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AppealsApi::AppealSubmittedJob, type: :job do +describe AppealsApi::AppealReceivedJob, type: :job do let(:job) { described_class.new } let(:appeal) { create(:higher_level_review_v2) } let(:hlr_template_name) { 'higher_level_review_received' } diff --git a/modules/appeals_api/spec/support/shared_examples_for_appeal_status_updates.rb b/modules/appeals_api/spec/support/shared_examples_for_appeal_status_updates.rb index 2fff8eec427..5ee0c1be981 100644 --- a/modules/appeals_api/spec/support/shared_examples_for_appeal_status_updates.rb +++ b/modules/appeals_api/spec/support/shared_examples_for_appeal_status_updates.rb @@ -71,9 +71,9 @@ context "when status has updated to 'submitted' and claimant or veteran email data present" do it 'enqueues the appeal received job' do - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 example_instance.update_status(status: 'submitted') - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 1 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 1 end end @@ -81,25 +81,25 @@ before { example_instance.update(status: 'submitted') } it 'does not enqueue the appeal received job' do - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 example_instance.update_status(status: 'submitted') - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 end end context "when incoming status is not 'submitted' and claimant or veteran email data present" do it 'does not enqueue the appeal received job' do - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 example_instance.update_status(status: 'pending') - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 end end context 'when veteran appellant without email provided' do it 'gets the ICN and enqueues the appeal received job' do - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 instance_without_email.update_status(status: 'submitted') - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 1 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 1 end end @@ -111,9 +111,9 @@ end it 'does not enqueue the appeal received job' do - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 example_instance.update_status(status: 'submitted') - expect(AppealsApi::AppealSubmittedJob.jobs.size).to eq 0 + expect(AppealsApi::AppealReceivedJob.jobs.size).to eq 0 end end end