Skip to content

[VI-1104] updates Form526 user_account lookup #21164

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

Merged
merged 14 commits into from
Apr 17, 2025
106 changes: 54 additions & 52 deletions app/models/form526_submission.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ class Form526Submission < ApplicationRecord
:submit_flashes,
:poll_form526_pdf,
:cleanup,
additional_class_logs: {
action: 'Begin as anciliary 526 submission'
},
additional_class_logs: { action: 'Begin as anciliary 526 submission' },
additional_instance_logs: {
saved_claim_id: %i[saved_claim id],
user_uuid: %i[user_uuid]
Expand Down Expand Up @@ -54,13 +52,11 @@ class Form526Submission < ApplicationRecord
has_kms_key
has_encrypted :auth_headers_json, :birls_ids_tried, :form_json, key: :kms_key, **lockbox_options

belongs_to :saved_claim,
class_name: 'SavedClaim::DisabilityCompensation',
inverse_of: false
belongs_to :saved_claim, class_name: 'SavedClaim::DisabilityCompensation', inverse_of: false

has_many :form526_job_statuses, dependent: :destroy
has_many :form526_submission_remediations, dependent: :destroy
belongs_to :user_account, dependent: nil, optional: true
belongs_to :user_account, dependent: nil

validates(:auth_headers_json, presence: true)
enum :backup_submitted_claim_status, { accepted: 0, rejected: 1, paranoid_success: 2 }
Expand Down Expand Up @@ -163,9 +159,7 @@ def submit_with_birls_id_that_hasnt_been_tried_yet!(

# Note that the User record is cached in Redis -- `User.redis_namespace_ttl`
def get_first_name
user = User.find(user_uuid)
user&.first_name&.upcase.presence ||
auth_headers&.dig('va_eauth_firstName')&.upcase
user&.first_name&.upcase.presence || auth_headers&.dig('va_eauth_firstName')&.upcase
end

# Checks against the User record first, and then resorts to checking the auth_headers
Expand All @@ -174,15 +168,13 @@ def get_first_name
# @return [Hash] of the user's full name (first, middle, last, suffix)
#
def full_name
name_hash = User.find(user_uuid)&.full_name_normalized
name_hash = user&.full_name_normalized
return name_hash if name_hash&.[](:first).present?

{
first: auth_headers&.dig('va_eauth_firstName')&.capitalize,
{ first: auth_headers&.dig('va_eauth_firstName')&.capitalize,
middle: nil,
last: auth_headers&.dig('va_eauth_lastName')&.capitalize,
suffix: nil
}
suffix: nil }
end

# form_json is memoized here so call invalidate_form_hash after updating form_json
Expand Down Expand Up @@ -468,22 +460,11 @@ def last_remediation
end

def account
# first, check for an ICN on the UserAccount associated to the submission, return it if found
account = user_account
return account if account&.icn.present?

# next, check past submissions for different UserAccounts that might have ICNs
past_submissions = get_past_submissions
account = find_user_account_with_icn(past_submissions, 'past submissions')
return account if account.present? && account.icn.present?

# next, check for any historical UserAccounts for that user which might have an ICN
user_verifications = get_user_verifications
account = find_user_account_with_icn(user_verifications, 'user verifications')
return account if account.present? && account.icn.present?
return user_account if user_account&.icn.present?

# failing all the above, default to an Account lookup
Account.lookup_by_user_uuid(user_uuid)
Rails.logger.info('Form526Submission::account - no UserAccount ICN found', log_payload)
# query MPI by EDIPI first & attributes second for user ICN, return in OpenStruct
get_icn_from_mpi
end

# Send the Submitted Email - when the Veteran has clicked the "submit" button in va.gov
Expand All @@ -493,8 +474,7 @@ def account
# @param invoker: string where the Received Email trigger is being called from
def send_submitted_email(invoker)
if Flipper.enabled?(:disability_526_send_form526_submitted_email)
Rails.logger.info("Form526SubmittedEmailJob called for user #{user_uuid},
submission: #{id} from #{invoker}")
Rails.logger.info("Form526SubmittedEmailJob called for user #{user_uuid}, submission: #{id} from #{invoker}")
first_name = get_first_name
params = personalization_parameters(first_name)
Form526SubmittedEmailJob.perform_async(params)
Expand All @@ -506,8 +486,7 @@ def send_submitted_email(invoker)
# Backup Path: when Form526StatusPollingJob reaches "paranoid_success" status
# @param invoker: string where the Received Email trigger is being called from
def send_received_email(invoker)
Rails.logger.info("Form526ConfirmationEmailJob called for user #{user_uuid},
submission: #{id} from #{invoker}")
Rails.logger.info("Form526ConfirmationEmailJob called for user #{user_uuid}, submission: #{id} from #{invoker}")
first_name = get_first_name
params = personalization_parameters(first_name)
Form526ConfirmationEmailJob.perform_async(params)
Expand Down Expand Up @@ -612,7 +591,6 @@ def submit_form_8940
end

def submit_flashes
user = User.find(user_uuid)
# Note that the User record is cached in Redis -- `User.redis_namespace_ttl`
# If this method runs after the TTL, then the flashes will not be applied -- a possible bug.
BGS::FlashUpdater.perform_async(id) if user && Flipper.enabled?(:disability_compensation_flashes, user)
Expand All @@ -630,27 +608,51 @@ def cleanup
EVSS::DisabilityCompensationForm::SubmitForm526Cleanup.perform_async(id)
end

def find_user_account_with_icn(records, record_type)
records.pluck(:user_account_id).uniq.each do |user_account_id|
user_account = UserAccount.find(user_account_id)
next if user_account&.icn.blank?

Rails.logger.info("ICN not found on submission #{id}, " \
"using ICN for user account #{user_account_id} instead (based on #{record_type})")
return user_account
def get_icn_from_mpi
edipi_response_profile = edipi_mpi_profile_query(auth_headers['va_eauth_dodedipnid'])
if edipi_response_profile&.icn.present?
OpenStruct.new(icn: edipi_response_profile.icn)
else
Rails.logger.info('Form526Submission::account - unable to look up MPI profile with EDIPI', log_payload)
attributes_response_profile = attributes_mpi_profile_query(auth_headers)
if attributes_response_profile&.icn.present?
OpenStruct.new(icn: attributes_response_profile.icn)
else
Rails.logger.info('Form526Submission::account - no ICN present', log_payload)
OpenStruct.new(icn: nil)
end
end
end

def get_past_submissions
Form526Submission.where(user_uuid:).where.not(user_account_id:)
def edipi_mpi_profile_query(edipi)
return unless edipi

edipi_response = mpi_service.find_profile_by_edipi(edipi:)
edipi_response.profile if edipi_response.ok? && edipi_response.profile.icn.present?
end

def attributes_mpi_profile_query(auth_headers)
required_attributes = %w[va_eauth_firstName va_eauth_lastName va_eauth_birthdate va_eauth_pnid]
return unless required_attributes.all? { |attr| auth_headers[attr].present? }

attributes_response = mpi_service.find_profile_by_attributes(
first_name: auth_headers['va_eauth_firstName'],
last_name: auth_headers['va_eauth_lastName'],
birth_date: auth_headers['va_eauth_birthdate']&.to_date.to_s,
ssn: auth_headers['va_eauth_pnid']
)
attributes_response.profile if attributes_response.ok? && attributes_response.profile.icn.present?
end

def log_payload
@log_payload ||= { user_uuid:, submission_id: id }
end

def mpi_service
@mpi_service ||= MPI::Service.new
end

def get_user_verifications
UserVerification.where(idme_uuid: user_uuid)
.or(UserVerification.where(backing_idme_uuid: user_uuid))
.or(UserVerification.where(logingov_uuid: user_uuid))
.or(UserVerification.where(mhv_uuid: user_uuid))
.or(UserVerification.where(dslogon_uuid: user_uuid))
.where.not(user_account_id:)
def user
@user ||= User.find(user_uuid)
end
end
46 changes: 17 additions & 29 deletions rakelib/form526.rake
Original file line number Diff line number Diff line change
Expand Up @@ -518,11 +518,15 @@ namespace :form526 do
end

vname = "#{fs.auth_headers['va_eauth_firstName']} #{fs.auth_headers['va_eauth_lastName']}"
icn = Account.lookup_by_user_uuid(fs.user_uuid).first&.icn
icn = fs.user_account&.icn
if icn.blank?
# TODO: make this work for blank icn's
puts "icn blank #{fs.id}"
next
mpi_response = MPI::Service.new.find_profile_by_edipi(edipi: fs['va_eauth_dodedipnid'])
if mpi_response.ok? && mpi_response.profile.icn.present?
icn = mpi_response.profile.icn
else
puts "icn blank #{fs.id}"
next
end
end
user = OpenStruct.new(participant_id: fs.auth_headers['va_eauth_pid'], icn:, common_name: vname,
ssn:)
Expand Down Expand Up @@ -684,42 +688,26 @@ namespace :form526 do
desc 'pretty print MPI profile for submission'
task mpi: :environment do |_, args|
def puts_mpi_profile(submission)
ids = {}
ids[:edipi] = edipi submission.auth_headers
ids[:icn] = icn ids[:edipi]
edipi = submission.auth_headers['va_eauth_dodedipnid']
raise Error, 'no edipi' unless edipi

pp mpi_profile(user_identity(**ids)).as_json
ids = { edipi:, icn: submission.user_account&.icn }

pp mpi_profile(ids).as_json
end

def mpi_profile(user_identity)
if user_identity.mhv_icn
find_profile_response = MPI::Service.new.find_profile_by_identifier(identifier: user_identity.mhv_icn,
def mpi_profile(ids)
if ids[:icn]
find_profile_response = MPI::Service.new.find_profile_by_identifier(identifier: ids[:icn],
identifier_type: MPI::Constants::ICN)
else
find_profile_response = MPI::Service.new.find_profile_by_edipi(edipi: user_identity.edipi)
find_profile_response = MPI::Service.new.find_profile_by_edipi(edipi: ids[:edipi])
end
raise find_profile_response.error if find_profile_response.error

find_profile_response.profile
end

def user_identity(icn:, edipi:)
OpenStruct.new mhv_icn: icn, edipi:
end

def edipi(auth_headers)
auth_headers['va_eauth_dodedipnid']
end

def icn(edipi)
raise Error, 'no edipi' unless edipi

icns = Account.where(edipi:).pluck :icn
raise Error, 'multiple icns' if icns.uniq.length > 1

icns.first
end

Form526Submission.where(id: args.extras).find_each { |sub| puts_mpi_profile sub }
end

Expand Down
4 changes: 3 additions & 1 deletion spec/factories/form526_submissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
FactoryBot.define do
factory :form526_submission do
transient do
user { create(:disabilities_compensation_user) }
user_verification { create(:idme_user_verification) }
user { create(:disabilities_compensation_user, idme_uuid: user_verification.idme_uuid) }
submissions_path { Rails.root.join(*'/spec/support/disability_compensation_form/submissions'.split('/')).to_s }
end
user_account { user_verification.user_account }
user_uuid { user.uuid }
saved_claim { create(:va526ez) }
submitted_claim_id { nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
end

describe '#choose_provider' do
let(:account) { create(:account) }
let(:submission) { create(:form526_submission, user_uuid: account.idme_uuid, submit_endpoint: 'claims_api') }
let(:user) { create(:user, :loa3, :with_terms_of_use_agreement) }
let(:user_account) { user.user_account }
let(:icn) { user_account.icn }
let(:submission) { create(:form526_submission, user_account:, submit_endpoint: 'claims_api') }

it 'delegates to the ApiProviderFactory with the correct data' do
auth_headers = {}
Expand All @@ -38,7 +40,7 @@
type: ApiProviderFactory::FACTORIES[:generate_pdf],
provider: ApiProviderFactory::API_PROVIDER[:lighthouse],
options: { auth_headers:, breakered: true },
current_user: OpenStruct.new({ flipper_id: submission.user_uuid, icn: account.icn }),
current_user: OpenStruct.new({ flipper_id: submission.user_uuid, icn: }),
feature_toggle: nil
}
)
Expand All @@ -59,7 +61,7 @@
type: ApiProviderFactory::FACTORIES[:generate_pdf],
provider: ApiProviderFactory::API_PROVIDER[:lighthouse],
options: { auth_headers: submission.auth_headers, breakered: true },
current_user: OpenStruct.new({ flipper_id: submission.user_uuid, icn: account.icn }),
current_user: OpenStruct.new({ flipper_id: submission.user_uuid, icn: }),
feature_toggle: nil
}
).and_call_original
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
.and_return('access_token')
end

let(:user) { create(:user, :loa3) }
let(:user_account) { create(:user_account, icn: '123498767V234859') }
let(:user) { create(:user, :loa3, icn: user_account.icn) }
let(:auth_headers) do
EVSS::DisabilityCompensationAuthHeaders.new(user).add_headers(EVSS::AuthHeaders.new(user).to_h)
end
Expand All @@ -27,7 +28,7 @@
allow(Settings.form526_backup).to receive(:enabled).and_return(false)
end

let!(:submission) { create(:form526_submission, :with_everything) }
let!(:submission) { create(:form526_submission, :with_everything, user_account:) }

it 'creates a submission job' do
expect { subject.perform_async(submission.id) }.to change(subject.jobs, :size).by(1)
Expand All @@ -42,7 +43,7 @@
let(:timestamp) { Time.now.utc }

context 'when all retries are exhausted' do
let!(:form526_submission) { create(:form526_submission) }
let!(:form526_submission) { create(:form526_submission, user_account:) }
let!(:form526_job_status) { create(:form526_job_status, :retryable_error, form526_submission:, job_id: 1) }

it 'updates a StatsD counter and updates the status on an exhaustion event' do
Expand Down Expand Up @@ -111,7 +112,7 @@
allow(Settings.form526_backup).to receive_messages(submission_method: payload_method, enabled: true)
end

let!(:submission) { create(:form526_submission, :with_everything) }
let!(:submission) { create(:form526_submission, :with_everything, user_account:) }
let!(:upload_data) { submission.form[Form526Submission::FORM_526_UPLOADS] }

context 'successfully' do
Expand Down Expand Up @@ -222,7 +223,7 @@
allow(Settings.form526_backup).to receive_messages(submission_method: 'single', enabled: true)
end

let!(:submission) { create(:form526_submission, :with_non_pdf_uploads) }
let!(:submission) { create(:form526_submission, :with_non_pdf_uploads, user_account:) }
let!(:upload_data) { submission.form[Form526Submission::FORM_526_UPLOADS] }

context 'converts non-pdf files to pdf' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
end

context 'with an exhausted callback message' do
let!(:form526_submission) { create(:form526_submission) }
let(:user_account) { create(:user_account, icn: '123498767V234859') }
let!(:form526_submission) { create(:form526_submission, user_account:) }
let!(:form526_job_status) do
create(:form526_job_status, job_id: msg['jid'], form526_submission:)
end
Expand Down
Loading
Loading