diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification/email.rb b/modules/simple_forms_api/app/services/simple_forms_api/notification/email.rb index 992ac18403b..a2b54dfba3c 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/notification/email.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification/email.rb @@ -1,11 +1,15 @@ # frozen_string_literal: true +require_relative 'notification/parsing_utils' + module SimpleFormsApi module Notification class Email attr_reader :form_number, :confirmation_number, :date_submitted, :expiration_date, :lighthouse_updated_at, :notification_type, :user, :user_account, :form_data + include SimpleFormsApi::Notification::ParsingUtils + TEMPLATE_IDS = { 'vba_21_0845' => { confirmation: Settings.vanotify.services.va_gov.template_id.form21_0845_confirmation_email, @@ -127,12 +131,12 @@ def flipper? end def enqueue_email(at, template_id) - email_from_form_data = get_email_address_from_form_data - first_name_from_form_data = get_first_name_from_form_data + email = contact_info[:email] + first_name = contact_info[:first_name] # async job and form data includes email - if email_from_form_data && first_name_from_form_data - async_job_with_form_data(email_from_form_data, first_name_from_form_data, at, template_id) + if email && first_name + async_job_with_form_data(email, first_name, at, template_id) # async job and we have a UserAccount elsif user_account async_job_with_user_account(user_account, at, template_id) @@ -159,15 +163,15 @@ def async_job_with_form_data(email, first_name, at, template_id) end def async_job_with_user_account(user_account, at, template_id) - first_name_from_user_account = get_first_name_from_user_account - return unless first_name_from_user_account + first_name = get_first_name_from_user_account + return unless first_name if Flipper.enabled?(:simple_forms_notification_callbacks) VANotify::UserAccountJob.perform_at( at, user_account.id, template_id, - get_personalization(first_name_from_user_account), + get_personalization(first_name), *email_args ) else @@ -175,84 +179,23 @@ def async_job_with_user_account(user_account, at, template_id) at, user_account.id, template_id, - get_personalization(first_name_from_user_account) + get_personalization(first_name) ) end end def send_email_now(template_id) - email_from_form_data = get_email_address_from_form_data - first_name_from_form_data = get_first_name_from_form_data - - # sync job and form data includes email - if email_from_form_data && first_name_from_form_data - VANotify::EmailJob.perform_async( - email_from_form_data, - template_id, - get_personalization(first_name_from_form_data) - ) - # sync job and we have a User - elsif user - first_name = get_first_name_from_form_data || get_first_name_from_user - return unless first_name - - VANotify::EmailJob.perform_async( - user.va_profile_email, - template_id, - get_personalization(first_name) - ) - end - end + email = contact_info[:email] + first_name = contact_info[:first_name] || get_first_name_from_user - def get_email_address_from_form_data - case @form_number - when 'vba_21_0845' - form21_0845_contact_info[0] - when 'vba_21p_0847', 'vba_21_0972' - form_data['preparer_email'] - when 'vba_21_0966', 'vba_21_0966_intent_api' - form21_0966_email_address - when 'vba_21_4142', 'vba_26_4555' - form_data.dig('veteran', 'email') - when 'vba_21_10210' - form21_10210_contact_info[0] - when 'vba_20_10206' - form20_10206_contact_info[0] - when 'vba_20_10207' - form20_10207_contact_info[0] - when 'vba_40_0247' - form_data['applicant_email'] - when 'vba_40_10007' - form_data.dig('application', 'claimant', 'email') - end - end + return unless first_name && email - # rubocop:disable Metrics/MethodLength - def get_first_name_from_form_data - case @form_number - when 'vba_21_0845' - form21_0845_contact_info[1] - when 'vba_21p_0847' - form_data.dig('preparer_name', 'first') - when 'vba_21_0966', 'vba_21_0966_intent_api' - form21_0966_first_name - when 'vba_21_0972' - form_data.dig('preparer_full_name', 'first') - when 'vba_21_4142', 'vba_26_4555' - form_data.dig('veteran', 'full_name', 'first') - when 'vba_21_10210' - form21_10210_contact_info[1] - when 'vba_20_10206' - form20_10206_contact_info[1] - when 'vba_20_10207' - form20_10207_contact_info[1] - when 'vba_40_0247' - form_data.dig('applicant_full_name', 'first') - when 'vba_40_10007' - form40_10007_first_name - end + VANotify::EmailJob.perform_async( + email, + template_id, + get_personalization(first_name) + ) end - # rubocop:enable Metrics/MethodLength def get_first_name_from_user_account mpi_response = MPI::Service.new.find_profile_by_identifier(identifier_type: 'ICN', identifier: user_account.icn) @@ -268,10 +211,8 @@ def get_first_name_from_user_account end def get_first_name_from_user - first_name = user.first_name - Rails.logger.error('First name not found in user profile') unless first_name - - first_name + Rails.logger.error('First name not found in user profile') unless user&.first_name + user&.first_name end def get_personalization(first_name) @@ -295,99 +236,6 @@ def default_personalization(first_name) } end - # email and first name for form 20-10206 - def form20_10206_contact_info - # email address not required and omitted - if @form_data['email_address'].blank? && @user - [@user.va_profile_email, @form_data.dig('full_name', 'first')] - - # email address not required and optionally entered - else - [@form_data['email_address'], @form_data.dig('full_name', 'first')] - end - end - - # email and first name for form 20-10207 - def form20_10207_contact_info - preparer_types = %w[veteran third-party-veteran non-veteran third-party-non-veteran] - - return unless preparer_types.include?(@form_data['preparer_type']) - - email_and_first_name = [@user&.va_profile_email] - # veteran - email_and_first_name << if @form_data['preparer_type'] == 'veteran' - @form_data['veteran_full_name']['first'] - - # non-veteran - elsif @form_data['preparer_type'] == 'non-veteran' - @form_data['non_veteran_full_name']['first'] - - # third-party - else - @form_data['third_party_full_name']['first'] - end - - email_and_first_name - end - - # email and first name for form 21-0845 - def form21_0845_contact_info - # (vet && signed in) - if @form_data['authorizer_type'] == 'veteran' - [@form_data['veteran_email'] || @user&.va_profile_email, @form_data.dig('veteran_full_name', 'first')] - - # (non-vet && signed in) || (non-vet && anon) - elsif @form_data['authorizer_type'] == 'nonVeteran' - [@form_data['authorizer_email'], @form_data.dig('authorizer_full_name', 'first')] - - # (vet && anon) - else - [nil, nil] - end - end - - # email and first name for form 21-10210 - def form21_10210_contact_info - # user's own claim - # user is a veteran - if @form_data['claim_ownership'] == 'self' && @form_data['claimant_type'] == 'veteran' - email = @form_data['veteran_email'] || user&.va_profile_email - [email, @form_data.dig('veteran_full_name', 'first')] - - # user's own claim - # user is not a veteran - elsif @form_data['claim_ownership'] == 'self' && @form_data['claimant_type'] == 'non-veteran' - email = @form_data['claimant_email'] || user&.va_profile_email - [email, @form_data.dig('claimant_full_name', 'first')] - - # someone else's claim - # claimant (aka someone else) is a veteran - # or - # claimant (aka someone else) is not a veteran - elsif @form_data['claim_ownership'] == 'third-party' - [@form_data['witness_email'], @form_data.dig('witness_full_name', 'first')] - - else - [nil, nil] - end - end - - def form21_0966_first_name - if form_data['preparer_identification'] == 'SURVIVING_DEPENDENT' - form_data.dig('surviving_dependent_full_name', 'first') - else - form_data.dig('veteran_full_name', 'first') || user&.first_name - end - end - - def form21_0966_email_address - if form_data['preparer_identification'] == 'SURVIVING_DEPENDENT' - form_data['surviving_dependent_email'] - else - form_data['veteran_email'] - end - end - def form21_0966_personalization intent_to_file_benefits, intent_to_file_benefits_links = get_intent_to_file_benefits_variables { diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification/mappings.yml b/modules/simple_forms_api/app/services/simple_forms_api/notification/mappings.yml new file mode 100644 index 00000000000..8e5474fdf0f --- /dev/null +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification/mappings.yml @@ -0,0 +1,51 @@ +vba_20_10206: + first_name: [full_name, first] + email: email_address +vba_20_10207: + preparer_type: + veteran: + first_name: [veteran_full_name, first] + email: veteran_email_address + third-party-veteran: + first_name: [third_party_full_name, first] + email: veteran_email_address + non-veteran: + first_name: [non_veteran_full_name, first] + email: non_veteran_email_address + third-party-non-veteran: + first_name: [third_party_full_name, first] + email: non_veteran_email_address +vba_21_0845: + authorizer_type: + veteran: + first_name: [veteran_full_name, first] + email: veteran_email + non_veteran: + first_name: [authorizer_full_name, first] + email: authorizer_email +vba_21_0966: + preparer_identification: + surviving_dependent: + first_name: [surviving_dependent_full_name, first] + email: surviving_dependent_email +vba_21_10210: + claim_ownership: + self: + claimant_type: + veteran: + first_name: [veteran_full_name, first] + email: veteran_email + non-veteran: + first_name: [claimant_full_name, first] + email: claimant_email + third-party: + claimant_type: + veteran: veteran_email + non-veteran: [veteran_full_name, first] +vba_40_10007: + application: + applicant: + applicant_relationship_to_claimant: + self: + first_name: [application, claimant, name, first] + first_name: [application, applicant, name, first] diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification/parsing_utils.rb b/modules/simple_forms_api/app/services/simple_forms_api/notification/parsing_utils.rb new file mode 100644 index 00000000000..6dfed5ced47 --- /dev/null +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification/parsing_utils.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module SimpleFormsApi + module Notification + module ParsingUtils + def contact_info + form_mapping = YAML.load_file( + 'modules/simple_forms_api/app/services/simple_forms_api/notification/form_mapping.yml' + )&.dig(form_number) + + return unless form_mapping + + transformed_data = deep_transform_keys_and_values(form_data) + mapping_path = find_mapping_path(form_mapping, transformed_data) + + return unless mapping_path + + first_name = dig_value(transformed_data, mapping_path['first_name']) + email = dig_value(transformed_data, mapping_path['email']) || user&.va_profile_email + + { first_name:, email: } + end + + private + + def find_mapping_path(form_mapping, form_data) + return form_mapping if form_mapping.key?('first_name') && form_mapping.key?('email') + + form_mapping.each do |key, value| + next unless form_data.key?(key) + + # If the value under this key is a hash and its keys + # match the form_data's value, go deeper + return find_mapping_path(value[form_data[key]], form_data) if value.is_a?(Hash) && value.key?(form_data[key]) + end + + nil + end + + def deep_transform_keys_and_values(hash) + hash.deep_transform_keys { |key| key.to_s.underscore.downcase } + .transform_values do |value| + case value + when Hash + deep_transform_keys_and_values(value) + when Array + value.map { |v| v.is_a?(Hash) ? deep_transform_keys_and_values(v) : v.to_s.underscore.downcase } + else + value.to_s.underscore.downcase + end + end + end + + def dig_value(data, keys) + return data[keys] unless keys.is_a?(Array) + + keys.reduce(data) { |d, key| d.is_a?(Hash) ? d[key] : nil } + end + end + end +end diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb index b5806d39ab6..1ff82616b07 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb @@ -1,11 +1,15 @@ # frozen_string_literal: true +require_relative 'notification/parsing_utils' + # TODO: Delete this file after SimpleFormsApi::Notification::Email is in place. module SimpleFormsApi class NotificationEmail attr_reader :form_number, :confirmation_number, :date_submitted, :expiration_date, :lighthouse_updated_at, :notification_type, :user, :user_account, :form_data + include SimpleFormsApi::Notification::ParsingUtils + TEMPLATE_IDS = { 'vba_21_0845' => { confirmation: Settings.vanotify.services.va_gov.template_id.form21_0845_confirmation_email, @@ -127,12 +131,12 @@ def flipper? end def enqueue_email(at, template_id) - email_from_form_data = get_email_address_from_form_data - first_name_from_form_data = get_first_name_from_form_data + email = contact_info[:email] + first_name = contact_info[:first_name] # async job and form data includes email - if email_from_form_data && first_name_from_form_data - async_job_with_form_data(email_from_form_data, first_name_from_form_data, at, template_id) + if email && first_name + async_job_with_form_data(email, first_name, at, template_id) # async job and we have a UserAccount elsif user_account async_job_with_user_account(user_account, at, template_id) @@ -159,15 +163,15 @@ def async_job_with_form_data(email, first_name, at, template_id) end def async_job_with_user_account(user_account, at, template_id) - first_name_from_user_account = get_first_name_from_user_account - return unless first_name_from_user_account + first_name = get_first_name_from_user_account + return unless first_name if Flipper.enabled?(:simple_forms_notification_callbacks) VANotify::UserAccountJob.perform_at( at, user_account.id, template_id, - get_personalization(first_name_from_user_account), + get_personalization(first_name), *email_args ) else @@ -175,84 +179,23 @@ def async_job_with_user_account(user_account, at, template_id) at, user_account.id, template_id, - get_personalization(first_name_from_user_account) + get_personalization(first_name) ) end end def send_email_now(template_id) - email_from_form_data = get_email_address_from_form_data - first_name_from_form_data = get_first_name_from_form_data - - # sync job and form data includes email - if email_from_form_data && first_name_from_form_data - VANotify::EmailJob.perform_async( - email_from_form_data, - template_id, - get_personalization(first_name_from_form_data) - ) - # sync job and we have a User - elsif user - first_name = get_first_name_from_form_data || get_first_name_from_user - return unless first_name - - VANotify::EmailJob.perform_async( - user.va_profile_email, - template_id, - get_personalization(first_name) - ) - end - end + email = contact_info[:email] + first_name = contact_info[:first_name] || get_first_name_from_user - def get_email_address_from_form_data - case @form_number - when 'vba_21_0845' - form21_0845_contact_info[0] - when 'vba_21p_0847', 'vba_21_0972' - form_data['preparer_email'] - when 'vba_21_0966', 'vba_21_0966_intent_api' - form21_0966_email_address - when 'vba_21_4142', 'vba_26_4555' - form_data.dig('veteran', 'email') - when 'vba_21_10210' - form21_10210_contact_info[0] - when 'vba_20_10206' - form20_10206_contact_info[0] - when 'vba_20_10207' - form20_10207_contact_info[0] - when 'vba_40_0247' - form_data['applicant_email'] - when 'vba_40_10007' - form_data.dig('application', 'claimant', 'email') - end - end + return unless first_name && email - # rubocop:disable Metrics/MethodLength - def get_first_name_from_form_data - case @form_number - when 'vba_21_0845' - form21_0845_contact_info[1] - when 'vba_21p_0847' - form_data.dig('preparer_name', 'first') - when 'vba_21_0966', 'vba_21_0966_intent_api' - form21_0966_first_name - when 'vba_21_0972' - form_data.dig('preparer_full_name', 'first') - when 'vba_21_4142', 'vba_26_4555' - form_data.dig('veteran', 'full_name', 'first') - when 'vba_21_10210' - form21_10210_contact_info[1] - when 'vba_20_10206' - form20_10206_contact_info[1] - when 'vba_20_10207' - form20_10207_contact_info[1] - when 'vba_40_0247' - form_data.dig('applicant_full_name', 'first') - when 'vba_40_10007' - form40_10007_first_name - end + VANotify::EmailJob.perform_async( + email, + template_id, + get_personalization(first_name) + ) end - # rubocop:enable Metrics/MethodLength def get_first_name_from_user_account mpi_response = MPI::Service.new.find_profile_by_identifier(identifier_type: 'ICN', identifier: user_account.icn) @@ -268,10 +211,8 @@ def get_first_name_from_user_account end def get_first_name_from_user - first_name = user.first_name - Rails.logger.error('First name not found in user profile') unless first_name - - first_name + Rails.logger.error('First name not found in user profile') unless user&.first_name + user&.first_name end def get_personalization(first_name) @@ -295,99 +236,6 @@ def default_personalization(first_name) } end - # email and first name for form 20-10206 - def form20_10206_contact_info - # email address not required and omitted - if @form_data['email_address'].blank? && @user - [@user.va_profile_email, @form_data.dig('full_name', 'first')] - - # email address not required and optionally entered - else - [@form_data['email_address'], @form_data.dig('full_name', 'first')] - end - end - - # email and first name for form 20-10207 - def form20_10207_contact_info - preparer_types = %w[veteran third-party-veteran non-veteran third-party-non-veteran] - - return unless preparer_types.include?(@form_data['preparer_type']) - - email_and_first_name = [@user&.va_profile_email] - # veteran - email_and_first_name << if @form_data['preparer_type'] == 'veteran' - @form_data['veteran_full_name']['first'] - - # non-veteran - elsif @form_data['preparer_type'] == 'non-veteran' - @form_data['non_veteran_full_name']['first'] - - # third-party - else - @form_data['third_party_full_name']['first'] - end - - email_and_first_name - end - - # email and first name for form 21-0845 - def form21_0845_contact_info - # (vet && signed in) - if @form_data['authorizer_type'] == 'veteran' - [@form_data['veteran_email'] || @user&.va_profile_email, @form_data.dig('veteran_full_name', 'first')] - - # (non-vet && signed in) || (non-vet && anon) - elsif @form_data['authorizer_type'] == 'nonVeteran' - [@form_data['authorizer_email'], @form_data.dig('authorizer_full_name', 'first')] - - # (vet && anon) - else - [nil, nil] - end - end - - # email and first name for form 21-10210 - def form21_10210_contact_info - # user's own claim - # user is a veteran - if @form_data['claim_ownership'] == 'self' && @form_data['claimant_type'] == 'veteran' - email = @form_data['veteran_email'] || user&.va_profile_email - [email, @form_data.dig('veteran_full_name', 'first')] - - # user's own claim - # user is not a veteran - elsif @form_data['claim_ownership'] == 'self' && @form_data['claimant_type'] == 'non-veteran' - email = @form_data['claimant_email'] || user&.va_profile_email - [email, @form_data.dig('claimant_full_name', 'first')] - - # someone else's claim - # claimant (aka someone else) is a veteran - # or - # claimant (aka someone else) is not a veteran - elsif @form_data['claim_ownership'] == 'third-party' - [@form_data['witness_email'], @form_data.dig('witness_full_name', 'first')] - - else - [nil, nil] - end - end - - def form21_0966_first_name - if form_data['preparer_identification'] == 'SURVIVING_DEPENDENT' - form_data.dig('surviving_dependent_full_name', 'first') - else - form_data.dig('veteran_full_name', 'first') || user&.first_name - end - end - - def form21_0966_email_address - if form_data['preparer_identification'] == 'SURVIVING_DEPENDENT' - form_data['surviving_dependent_email'] - else - form_data['veteran_email'] - end - end - def form21_0966_personalization intent_to_file_benefits, intent_to_file_benefits_links = get_intent_to_file_benefits_variables {