From 355a4358226771709260d8711295871ca4843a7e Mon Sep 17 00:00:00 2001 From: Trevor Bosaw Date: Mon, 22 Apr 2024 14:34:37 -0700 Subject: [PATCH 01/15] [81196] Fixing Terms of Use erorr with sign in service resulting from removing User creation in SiS auth flow (#16431) --- .../v0/terms_of_use_agreements_controller.rb | 59 ++++++++++++------- app/models/sign_in/terms_code_container.rb | 4 +- app/services/sign_in/user_code_map_creator.rb | 10 +++- ...terms_of_use_agreements_controller_spec.rb | 52 ++++++++-------- .../sign_in/terms_code_containers.rb | 2 +- .../sign_in/terms_code_container_spec.rb | 12 ++-- .../sign_in/user_code_map_creator_spec.rb | 6 +- 7 files changed, 84 insertions(+), 61 deletions(-) diff --git a/app/controllers/v0/terms_of_use_agreements_controller.rb b/app/controllers/v0/terms_of_use_agreements_controller.rb index 12d56b625b1..0a188bc5315 100644 --- a/app/controllers/v0/terms_of_use_agreements_controller.rb +++ b/app/controllers/v0/terms_of_use_agreements_controller.rb @@ -17,8 +17,7 @@ def latest def accept terms_of_use_agreement = acceptor.perform! - - recache_user + recache_user unless terms_code_temporary_auth? render_success(action: 'accept', body: { terms_of_use_agreement: }, status: :created) rescue TermsOfUse::Errors::AcceptorError => e render_error(action: 'accept', message: e.message) @@ -26,10 +25,9 @@ def accept def accept_and_provision terms_of_use_agreement = acceptor(async: false).perform! - if terms_of_use_agreement.accepted? && provisioner.perform create_cerner_cookie - recache_user + recache_user unless terms_code_temporary_auth? render_success(action: 'accept_and_provision', body: { terms_of_use_agreement:, provisioned: true }, status: :created) else @@ -41,8 +39,7 @@ def accept_and_provision def decline terms_of_use_agreement = decliner.perform! - - recache_user + recache_user unless terms_code_temporary_auth? render_success(action: 'decline', body: { terms_of_use_agreement: }, status: :created) rescue TermsOfUse::Errors::DeclinerError => e render_error(action: 'decline', message: e.message) @@ -63,8 +60,8 @@ def update_provisioning def acceptor(async: true) TermsOfUse::Acceptor.new( - user_account: current_user.user_account, - common_name: current_user.common_name, + user_account: @user_account, + common_name:, version: params[:version], async: ) @@ -72,18 +69,18 @@ def acceptor(async: true) def decliner TermsOfUse::Decliner.new( - user_account: current_user.user_account, - common_name: current_user.common_name, + user_account: @user_account, + common_name:, version: params[:version] ) end def provisioner TermsOfUse::Provisioner.new( - icn: current_user.icn, - first_name: current_user.first_name, - last_name: current_user.last_name, - mpi_gcids: current_user.mpi_gcids + icn: @user_account.icn, + first_name: mpi_profile.given_names.first, + last_name: mpi_profile.family_name, + mpi_gcids: mpi_profile.full_mvi_ids ) end @@ -102,29 +99,51 @@ def create_cerner_cookie end def find_latest_agreement_by_version(version) - current_user.user_account.terms_of_use_agreements.where(agreement_version: version).last + @user_account.terms_of_use_agreements.where(agreement_version: version).last end def authenticate_one_time_terms_code terms_code_container = SignIn::TermsCodeContainer.find(params[:terms_code]) - @current_user = User.find(terms_code_container.user_uuid) + return unless terms_code_container + + @user_account = UserAccount.find(terms_code_container.user_account_uuid) ensure terms_code_container&.destroy end + def authenticate_current_user + load_user(skip_terms_check: true) + return unless current_user + + @user_account = current_user.user_account + end + + def terms_code_temporary_auth? + params[:terms_code].present? + end + def terms_authenticate - params[:terms_code].present? ? authenticate_one_time_terms_code : load_user(skip_terms_check: true) + terms_code_temporary_auth? ? authenticate_one_time_terms_code : authenticate_current_user + + raise Common::Exceptions::Unauthorized unless @user_account + end + + def mpi_profile + @mpi_profile ||= MPI::Service.new.find_profile_by_identifier(identifier: @user_account.icn, + identifier_type: MPI::Constants::ICN)&.profile + end - raise Common::Exceptions::Unauthorized unless @current_user + def common_name + "#{mpi_profile.given_names.first} #{mpi_profile.family_name}" end def render_success(action:, body:, status: :ok) - Rails.logger.info("[TermsOfUseAgreementsController] #{action} success", { icn: current_user.icn }) + Rails.logger.info("[TermsOfUseAgreementsController] #{action} success", { icn: @user_account.icn }) render json: body, status: end def render_error(action:, message:, status: :unprocessable_entity) - Rails.logger.error("[TermsOfUseAgreementsController] #{action} error: #{message}", { icn: current_user.icn }) + Rails.logger.error("[TermsOfUseAgreementsController] #{action} error: #{message}", { icn: @user_account.icn }) render json: { error: message }, status: end end diff --git a/app/models/sign_in/terms_code_container.rb b/app/models/sign_in/terms_code_container.rb index 5e033b23d55..fe1d2a962ea 100644 --- a/app/models/sign_in/terms_code_container.rb +++ b/app/models/sign_in/terms_code_container.rb @@ -7,8 +7,8 @@ class TermsCodeContainer < Common::RedisStore redis_key :code attribute :code, String - attribute :user_uuid, String + attribute :user_account_uuid, String - validates(:code, :user_uuid, presence: true) + validates(:code, :user_account_uuid, presence: true) end end diff --git a/app/services/sign_in/user_code_map_creator.rb b/app/services/sign_in/user_code_map_creator.rb index 5e4dd7c20f2..95b8b69e3f3 100644 --- a/app/services/sign_in/user_code_map_creator.rb +++ b/app/services/sign_in/user_code_map_creator.rb @@ -42,11 +42,11 @@ def create_credential_email end def create_user_acceptable_verified_credential - Login::UserAcceptableVerifiedCredentialUpdater.new(user_account: user_verification.user_account).perform + Login::UserAcceptableVerifiedCredentialUpdater.new(user_account:).perform end def create_terms_code_container - TermsCodeContainer.new(code: terms_code, user_uuid:).save! + TermsCodeContainer.new(code: terms_code, user_account_uuid: user_account.id).save! end def create_code_container @@ -79,6 +79,10 @@ def user_verification @user_verification ||= Login::UserVerifier.new(user_verifier_object).perform end + def user_account + @user_account ||= user_verification.user_account + end + def sign_in @sign_in ||= { service_name: state_payload.type, @@ -98,7 +102,7 @@ def access_token_attributes end def needs_accepted_terms_of_use? - client_config.va_terms_enforced? && user_verification.user_account.needs_accepted_terms_of_use? + client_config.va_terms_enforced? && user_account.needs_accepted_terms_of_use? end def client_config diff --git a/spec/controllers/v0/terms_of_use_agreements_controller_spec.rb b/spec/controllers/v0/terms_of_use_agreements_controller_spec.rb index fa9119ea4cb..bcd5245818f 100644 --- a/spec/controllers/v0/terms_of_use_agreements_controller_spec.rb +++ b/spec/controllers/v0/terms_of_use_agreements_controller_spec.rb @@ -4,10 +4,24 @@ RSpec.describe V0::TermsOfUseAgreementsController, type: :controller do let(:user) { create(:user) } - let(:user_account) { create(:user_account) } + let(:user_account) { create(:user_account, icn:) } + let(:icn) { user.icn } + let(:first_name) { user.first_name } + let(:last_name) { user.last_name } let!(:user_verification) { create(:user_verification, user_account:, idme_uuid: user.uuid) } let(:agreement_version) { 'v1' } let(:terms_code) { nil } + let(:find_profile_response) { create(:find_profile_response, profile: mpi_profile) } + let(:mpi_profile) do + build(:mpi_profile, + icn:, + given_names: [first_name], + family_name: last_name) + end + + before do + allow_any_instance_of(MPI::Service).to receive(:find_profile_by_identifier).and_return(find_profile_response) + end describe 'GET #latest' do subject { get :latest, params: { version: agreement_version, terms_code: } } @@ -60,7 +74,9 @@ context 'when user is authenticated with a one time terms code' do let(:terms_code) { SecureRandom.hex } - let!(:terms_code_container) { create(:terms_code_container, code: terms_code, user_uuid: user.uuid) } + let!(:terms_code_container) do + create(:terms_code_container, code: terms_code, user_account_uuid: user_account.id) + end it_behaves_like 'authenticated get latest agreement' end @@ -116,17 +132,6 @@ end end - context 'when the user does not have a common_name' do - let(:user) { create(:user, first_name: nil, middle_name: nil, last_name: nil, suffix: nil) } - let(:expected_error) { 'Validation failed: Common name can\'t be blank' } - - it 'returns an unprocessable_entity' do - subject - expect(response).to have_http_status(:unprocessable_entity) - expect(JSON.parse(response.body)['error']).to eq(expected_error) - end - end - context 'when the agreement acceptance fails' do before do allow_any_instance_of(TermsOfUseAgreement).to receive(:accepted!).and_raise(ActiveRecord::RecordInvalid) @@ -158,7 +163,9 @@ context 'when user is authenticated with a one time terms code' do let(:terms_code) { SecureRandom.hex } - let!(:terms_code_container) { create(:terms_code_container, code: terms_code, user_uuid: user.uuid) } + let!(:terms_code_container) do + create(:terms_code_container, code: terms_code, user_account_uuid: user_account.id) + end it_behaves_like 'authenticated agreements acceptance' end @@ -214,17 +221,6 @@ end end - context 'when the user does not have a common_name' do - let(:user) { create(:user, first_name: nil, middle_name: nil, last_name: nil, suffix: nil) } - let(:expected_error) { 'Validation failed: Common name can\'t be blank' } - - it 'returns an unprocessable_entity' do - subject - expect(response).to have_http_status(:unprocessable_entity) - expect(JSON.parse(response.body)['error']).to eq(expected_error) - end - end - context 'when the agreement declination fails' do before do allow_any_instance_of(TermsOfUseAgreement).to receive(:declined!).and_raise(ActiveRecord::RecordInvalid) @@ -256,7 +252,9 @@ context 'when user is authenticated with a one time terms code' do let(:terms_code) { SecureRandom.hex } - let!(:terms_code_container) { create(:terms_code_container, code: terms_code, user_uuid: user.uuid) } + let!(:terms_code_container) do + create(:terms_code_container, code: terms_code, user_account_uuid: user_account.id) + end it_behaves_like 'authenticated agreements decline' end @@ -306,7 +304,7 @@ it 'logs the expected log' do subject - expect(Rails.logger).to have_received(:info).with(expected_log, { icn: user.icn }) + expect(Rails.logger).to have_received(:info).with(expected_log, { icn: }) end end diff --git a/spec/factories/sign_in/terms_code_containers.rb b/spec/factories/sign_in/terms_code_containers.rb index 6215f8a9a4c..c391cf23c42 100644 --- a/spec/factories/sign_in/terms_code_containers.rb +++ b/spec/factories/sign_in/terms_code_containers.rb @@ -3,6 +3,6 @@ FactoryBot.define do factory :terms_code_container, class: 'SignIn::TermsCodeContainer' do code { SecureRandom.hex } - user_uuid { SecureRandom.uuid } + user_account_uuid { create(:user_account).id } end end diff --git a/spec/models/sign_in/terms_code_container_spec.rb b/spec/models/sign_in/terms_code_container_spec.rb index ef4b6c2b591..052195fa04b 100644 --- a/spec/models/sign_in/terms_code_container_spec.rb +++ b/spec/models/sign_in/terms_code_container_spec.rb @@ -3,10 +3,10 @@ require 'rails_helper' RSpec.describe SignIn::TermsCodeContainer, type: :model do - let(:terms_code_container) { create(:terms_code_container, user_uuid:, code:) } + let(:terms_code_container) { create(:terms_code_container, user_account_uuid:, code:) } let(:code) { SecureRandom.hex } - let(:user_uuid) { SecureRandom.uuid } + let(:user_account_uuid) { SecureRandom.uuid } describe 'validations' do describe '#code' do @@ -23,11 +23,11 @@ end end - describe '#user_uuid' do - subject { terms_code_container.user_uuid } + describe '#user_account_uuid' do + subject { terms_code_container.user_account_uuid } - context 'when user_uuid is nil' do - let(:user_uuid) { nil } + context 'when user_account_uuid is nil' do + let(:user_account_uuid) { nil } let(:expected_error) { Common::Exceptions::ValidationErrors } let(:expected_error_message) { 'Validation error' } diff --git a/spec/services/sign_in/user_code_map_creator_spec.rb b/spec/services/sign_in/user_code_map_creator_spec.rb index c88571a3805..42a948d447c 100644 --- a/spec/services/sign_in/user_code_map_creator_spec.rb +++ b/spec/services/sign_in/user_code_map_creator_spec.rb @@ -77,14 +77,16 @@ context 'if client config enforced terms is set to va terms' do let(:enforced_terms) { SignIn::Constants::Auth::VA_TERMS } + let(:user_account_uuid) { user_verification.user_account.id } context 'and user needs accepted terms of use' do it 'sets terms_code on returned user code map' do expect(subject.terms_code).not_to eq(nil) end - it 'creates a terms code container associated with terms code' do - expect(SignIn::TermsCodeContainer.find(subject.terms_code)).not_to eq(nil) + it 'creates a terms code container associated with terms code and with expected user attributes' do + terms_code_container = SignIn::TermsCodeContainer.find(subject.terms_code) + expect(terms_code_container.user_account_uuid).to eq(user_account_uuid) end end From 69ddf155fdf8cbc81e21abd3c65155cebccf8bf2 Mon Sep 17 00:00:00 2001 From: Gregg P <117232882+GcioGregg@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:38:51 -0700 Subject: [PATCH 02/15] add guardian suffix (#16449) --- app/sidekiq/education_form/templates/1995.erb | 2 ++ .../1995/ch30_guardian_graduated_sponsor.json | 1 + .../1995/ch30_guardian_graduated_sponsor.spl | 2 ++ .../1995/ch30_guardian_not_graduated.spl | 2 ++ 4 files changed, 7 insertions(+) diff --git a/app/sidekiq/education_form/templates/1995.erb b/app/sidekiq/education_form/templates/1995.erb index 78f99e3717a..471cd52fe3f 100644 --- a/app/sidekiq/education_form/templates/1995.erb +++ b/app/sidekiq/education_form/templates/1995.erb @@ -139,6 +139,8 @@ Middle name of Parent, Guardian or Custodian: <%= @applicant.minorQuestions.guar Last name of Parent, Guardian or Custodian: <%= @applicant.minorQuestions.guardianLastName %> +Suffix of Parent, Guardian or Custodian: <%= @applicant.minorQuestions.guardianSuffix %> + Address of Parent, Guardian or Custodian: Country: <%= @applicant.minorQuestions.guardianAddress.country %> Street: <%= @applicant.minorQuestions.guardianAddress.street %> diff --git a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.json b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.json index d7a705c734b..b682ac22c96 100644 --- a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.json +++ b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.json @@ -35,6 +35,7 @@ "guardianFirstName":"Richard", "guardianMiddleName":"D", "guardianLastName":"Van Dyke", + "guardianSuffix":"Jr.", "guardianAddress":{ "street":"456 oak blvd", "street2":"apt 88", diff --git a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.spl b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.spl index 179ad95507a..0d267bc05af 100644 --- a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.spl +++ b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_graduated_sponsor.spl @@ -87,6 +87,8 @@ Middle name of Parent, Guardian or Custodian: D Last name of Parent, Guardian or Custodian: Van Dyke +Suffix of Parent, Guardian or Custodian: Jr. + Address of Parent, Guardian or Custodian: Country: USA Street: 456 oak blvd diff --git a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_not_graduated.spl b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_not_graduated.spl index 3282cb24760..aba47288f60 100644 --- a/spec/fixtures/education_benefits_claims/1995/ch30_guardian_not_graduated.spl +++ b/spec/fixtures/education_benefits_claims/1995/ch30_guardian_not_graduated.spl @@ -76,6 +76,8 @@ Middle name of Parent, Guardian or Custodian: D Last name of Parent, Guardian or Custodian: Van Dyke +Suffix of Parent, Guardian or Custodian: + Address of Parent, Guardian or Custodian: Country: USA Street: 456 oak blvd From c1d23cc1d666158af0bbb6bc1b35d51503a7be46 Mon Sep 17 00:00:00 2001 From: jvcAdHoc <144135615+jvcAdHoc@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:54:17 -0400 Subject: [PATCH 03/15] [78732] Create AccreditedIndividual and AccreditedOrganization models (#16413) * add rough models and factories * add methods and unit tests --- .github/CODEOWNERS | 7 ++ app/models/accredited_individual.rb | 43 +++++++ app/models/accredited_organization.rb | 37 ++++++ lib/accredited_representation/constants.rb | 9 ++ spec/factories/accredited_individuals.rb | 34 ++++++ spec/factories/accredited_organizations.rb | 21 ++++ spec/models/accredited_individual_spec.rb | 125 ++++++++++++++++++++ spec/models/accredited_organization_spec.rb | 86 ++++++++++++++ 8 files changed, 362 insertions(+) create mode 100644 app/models/accredited_individual.rb create mode 100644 app/models/accredited_organization.rb create mode 100644 lib/accredited_representation/constants.rb create mode 100644 spec/factories/accredited_individuals.rb create mode 100644 spec/factories/accredited_organizations.rb create mode 100644 spec/models/accredited_individual_spec.rb create mode 100644 spec/models/accredited_organization_spec.rb diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0f84418b13b..76c7f1639f9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -186,6 +186,8 @@ app/mailers/views/veteran_readiness_employment_cmp.html.erb @department-of-veter app/mailers/views/veteran_readiness_employment.html.erb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/models/account_login_stat.rb @department-of-veterans-affairs/octo-identity app/models/account.rb @department-of-veterans-affairs/octo-identity +app/models/accredited_individual.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing +app/models/accredited_organization.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing app/models/adapters/payment_history_adapter.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers app/models/appeal_submission.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/models/appeal_submission_upload.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -786,6 +788,7 @@ docs/setup/codespaces.md @department-of-veterans-affairs/backend-review-group @d docs/setup/va_forms.md @department-of-veterans-affairs/platform-va-product-forms @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers docs/setup/virtual_machine_access.md @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers .github @department-of-veterans-affairs/backend-review-group +lib/accredited_representation/constants.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing lib/aes_256_cbc_encryptor.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/apps @department-of-veterans-affairs/vfs-facilities-frontend @department-of-veterans-affairs/lighthouse-pivot lib/bb @department-of-veterans-affairs/vfs-vaos @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1068,6 +1071,8 @@ spec/factories/686c/form_686c_674.rb @department-of-veterans-affairs/benefits-de spec/factories/686c/spouse.rb @department-of-veterans-affairs/benefits-dependents-management @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/686c/step_child_lives_with_veteran.rb @department-of-veterans-affairs/benefits-dependents-management @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/accounts.rb @department-of-veterans-affairs/octo-identity +spec/factories/accredited_individuals.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing +spec/factories/accredited_organizations.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing spec/factories/appeal_submissions.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/appeal_submission_uploads.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/ask.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1430,6 +1435,8 @@ spec/mailers/transactional_email_mailer_spec.rb @department-of-veterans-affairs/ spec/mailers/veteran_readiness_employment_mailer_spec.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/middleware @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/account_spec.rb @department-of-veterans-affairs/octo-identity +spec/models/accredited_individual_spec.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing +spec/models/accredited_organization_spec.rb @department-of-veterans-affairs/accredited-representation-management @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-accredited-rep-facing spec/models/async_transaction/base_spec.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/async_transaction/va_profile @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/async_transaction/vet360 @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group diff --git a/app/models/accredited_individual.rb b/app/models/accredited_individual.rb new file mode 100644 index 00000000000..1f9b3b69afd --- /dev/null +++ b/app/models/accredited_individual.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'accredited_representation/constants' + +class AccreditedIndividual < ApplicationRecord + # rubocop:disable Rails/HasAndBelongsToMany + has_and_belongs_to_many :accredited_organizations, + class: 'AccreditedOrganization', + join_table: 'accredited_individuals_accredited_organizations' + + # rubocop:enable Rails/HasAndBelongsToMany + + validates :ogc_id, :registration_number, :individual_type, presence: true + validates :poa_code, length: { is: 3 }, allow_blank: true + validates :individual_type, uniqueness: { scope: :registration_number } + + enum individual_type: { + 'attorney' => 'attorney', + 'claims_agent' => 'claims_agent', + 'representative' => 'representative' + } + + # Find all [AccreditedIndividuals] that are located within a distance of a specific location + # @param long [Float] longitude of the location of interest + # @param lat [Float] latitude of the location of interest + # @param max_distance [Float] the maximum search distance in meters + # + # @return [AccreditedIndividual::ActiveRecord_Relation] an ActiveRecord_Relation of + # all individuals matching the search criteria + def self.find_within_max_distance(long, lat, max_distance = AccreditedRepresentation::Constants::DEFAULT_MAX_DISTANCE) + query = 'ST_DWithin(ST_SetSRID(ST_MakePoint(:long, :lat), 4326)::geography, location, :max_distance)' + params = { long:, lat:, max_distance: } + + where(query, params) + end + + # return all poa_codes associated with the individual + # + # @return [Array] + def poa_codes + ([poa_code] + accredited_organizations.pluck(:poa_code)).compact + end +end diff --git a/app/models/accredited_organization.rb b/app/models/accredited_organization.rb new file mode 100644 index 00000000000..d532d9c9cbb --- /dev/null +++ b/app/models/accredited_organization.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'accredited_representation/constants' + +class AccreditedOrganization < ApplicationRecord + # rubocop:disable Rails/HasAndBelongsToMany + has_and_belongs_to_many :accredited_individuals, + class: 'AccreditedIndividual', + join_table: 'accredited_individuals_accredited_organizations' + # rubocop:enable Rails/HasAndBelongsToMany + + validates :ogc_id, :poa_code, presence: true + validates :poa_code, length: { is: 3 } + validates :poa_code, uniqueness: true + + # + # Find all [AccreditedOrganizations] that are located within a distance of a specific location + # @param long [Float] longitude of the location of interest + # @param lat [Float] latitude of the location of interest + # @param max_distance [Float] the maximum search distance in meters + # + # @return [AccreditedOrganization::ActiveRecord_Relation] an ActiveRecord_Relation of + # all organizations matching the search criteria + def self.find_within_max_distance(long, lat, max_distance = AccreditedRepresentation::Constants::DEFAULT_MAX_DISTANCE) + query = 'ST_DWithin(ST_SetSRID(ST_MakePoint(:long, :lat), 4326)::geography, location, :max_distance)' + params = { long:, lat:, max_distance: } + + where(query, params) + end + + # return all registration_numbers associated with the individual + # + # @return [Array] + def registration_numbers + accredited_individuals.pluck(:registration_number) + end +end diff --git a/lib/accredited_representation/constants.rb b/lib/accredited_representation/constants.rb new file mode 100644 index 00000000000..6543901a366 --- /dev/null +++ b/lib/accredited_representation/constants.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module AccreditedRepresentation + module Constants + METERS_PER_MILE = 1609.344 + DEFAULT_MAX_MILES = 50 + DEFAULT_MAX_DISTANCE = DEFAULT_MAX_MILES * METERS_PER_MILE + end +end diff --git a/spec/factories/accredited_individuals.rb b/spec/factories/accredited_individuals.rb new file mode 100644 index 00000000000..19ed9247e16 --- /dev/null +++ b/spec/factories/accredited_individuals.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :accredited_individual do + transient do + org_count { 1 } + end + + ogc_id { SecureRandom.uuid } + registration_number { Faker::Alphanumeric.alphanumeric(number: 5, min_numeric: 5) } + individual_type { 'representative' } + first_name { Faker::Name.first_name } + last_name { Faker::Name.last_name } + full_name { "#{first_name} #{last_name}" } + + trait :with_organizations do + after(:create) do |individual, evaluator| + create_list(:accredited_organization, evaluator.org_count, accredited_individuals: [individual]) + + individual.reload + end + end + + trait :attorney do + poa_code { Faker::Alphanumeric.alphanumeric(number: 3).upcase } + individual_type { 'attorney' } + end + + trait :claims_agent do + poa_code { Faker::Alphanumeric.alphanumeric(number: 3).upcase } + individual_type { 'claims_agent' } + end + end +end diff --git a/spec/factories/accredited_organizations.rb b/spec/factories/accredited_organizations.rb new file mode 100644 index 00000000000..73cbf239240 --- /dev/null +++ b/spec/factories/accredited_organizations.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :accredited_organization do + transient do + rep_count { 1 } + end + + ogc_id { SecureRandom.uuid } + poa_code { Faker::Alphanumeric.alphanumeric(number: 3).upcase } + name { Faker::Company.name } + + trait :with_representatives do + after(:create) do |organization, evaluator| + create_list(:accredited_individual, evaluator.rep_count, accredited_organizations: [organization]) + + organization.reload + end + end + end +end diff --git a/spec/models/accredited_individual_spec.rb b/spec/models/accredited_individual_spec.rb new file mode 100644 index 00000000000..13a7a15cb0f --- /dev/null +++ b/spec/models/accredited_individual_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe AccreditedIndividual, type: :model do + describe 'validations' do + subject { build(:accredited_individual) } + + it { expect(subject).to validate_presence_of(:ogc_id) } + it { expect(subject).to validate_presence_of(:registration_number) } + it { expect(subject).to validate_presence_of(:individual_type) } + it { expect(subject).to validate_length_of(:poa_code).is_equal_to(3).allow_blank } + + it { + expect(subject).to validate_uniqueness_of(:individual_type) + .scoped_to(:registration_number) + .ignoring_case_sensitivity + } + + it { + expect(subject).to define_enum_for(:individual_type) + .with_values({ + 'attorney' => 'attorney', + 'claims_agent' => 'claims_agent', + 'representative' => 'representative' + }) + .backed_by_column_of_type(:string) + } + end + + describe '.find_within_max_distance' do + # ~6 miles from Washington, D.C. + let!(:ai1) do + create(:accredited_individual, registration_number: '12300', long: -77.050552, lat: 38.820450, + location: 'POINT(-77.050552 38.820450)') + end + + # ~35 miles from Washington, D.C. + let!(:ai2) do + create(:accredited_individual, registration_number: '23400', long: -76.609383, lat: 39.299236, + location: 'POINT(-76.609383 39.299236)') + end + + # ~47 miles from Washington, D.C. + let!(:ai3) do + create(:accredited_individual, registration_number: '34500', long: -77.466316, lat: 38.309875, + location: 'POINT(-77.466316 38.309875)') + end + + # ~57 miles from Washington, D.C. + let!(:ai4) do + create(:accredited_individual, registration_number: '45600', long: -76.3483, lat: 39.5359, + location: 'POINT(-76.3483 39.5359)') + end + + context 'when there are individuals within the max search distance' do + it 'returns all individuals located within the default max distance' do + # check within 50 miles of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072) + + expect(results.pluck(:id)).to match_array([ai1.id, ai2.id, ai3.id]) + end + + it 'returns all individuals located within the specified max distance' do + # check within 40 miles of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072, 64_373.8) + + expect(results.pluck(:id)).to match_array([ai1.id, ai2.id]) + end + end + + context 'when there are no individuals within the max search distance' do + it 'returns an empty array' do + # check within 1 mile of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072, 1609.344) + + expect(results).to eq([]) + end + end + end + + describe '#poa_codes' do + context 'when the individual has no poa code' do + let(:individual) { create(:accredited_individual) } + + context 'when the individual has no accredited_organization associations' do + it 'returns an empty array' do + expect(individual.poa_codes).to eq([]) + end + end + + context 'when the individual has accredited_organization associations' do + let(:org1) { create(:accredited_organization, poa_code: 'ABC') } + let(:org2) { create(:accredited_organization, poa_code: 'DEF') } + + it 'returns an array of the associated accredited_organizations poa_codes' do + individual.accredited_organizations.push(org1, org2) + + expect(individual.reload.poa_codes).to match_array(%w[ABC DEF]) + end + end + end + + context 'when the individual has a poa code' do + let(:individual) { create(:accredited_individual, individual_type: 'attorney', poa_code: 'A12') } + + context 'when the individual has no accredited_organization associations' do + it 'returns an array of only the individual poa_code' do + expect(individual.poa_codes).to eq(['A12']) + end + end + + context 'when the individual has accredited_organization associations' do + let(:org1) { create(:accredited_organization, poa_code: 'ABC') } + let(:org2) { create(:accredited_organization, poa_code: 'DEF') } + + it 'returns an array of all associated poa_codes' do + individual.accredited_organizations.push(org1, org2) + + expect(individual.reload.poa_codes).to match_array(%w[ABC DEF A12]) + end + end + end + end +end diff --git a/spec/models/accredited_organization_spec.rb b/spec/models/accredited_organization_spec.rb new file mode 100644 index 00000000000..eeec3dc02dd --- /dev/null +++ b/spec/models/accredited_organization_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe AccreditedOrganization, type: :model do + describe 'validations' do + subject { build(:accredited_organization) } + + it { expect(subject).to validate_presence_of(:ogc_id) } + it { expect(subject).to validate_presence_of(:poa_code) } + it { expect(subject).to validate_length_of(:poa_code).is_equal_to(3) } + it { expect(subject).to validate_uniqueness_of(:poa_code) } + end + + describe '.find_within_max_distance' do + # ~6 miles from Washington, D.C. + let!(:ai1) do + create(:accredited_organization, poa_code: '123', long: -77.050552, lat: 38.820450, + location: 'POINT(-77.050552 38.820450)') + end + + # ~35 miles from Washington, D.C. + let!(:ai2) do + create(:accredited_organization, poa_code: '234', long: -76.609383, lat: 39.299236, + location: 'POINT(-76.609383 39.299236)') + end + + # ~47 miles from Washington, D.C. + let!(:ai3) do + create(:accredited_organization, poa_code: '345', long: -77.466316, lat: 38.309875, + location: 'POINT(-77.466316 38.309875)') + end + + # ~57 miles from Washington, D.C. + let!(:ai4) do + create(:accredited_organization, poa_code: '456', long: -76.3483, lat: 39.5359, + location: 'POINT(-76.3483 39.5359)') + end + + context 'when there are organizations within the max search distance' do + it 'returns all organizations located within the default max distance' do + # check within 50 miles of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072) + + expect(results.pluck(:id)).to match_array([ai1.id, ai2.id, ai3.id]) + end + + it 'returns all organizations located within the specified max distance' do + # check within 40 miles of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072, 64_373.8) + + expect(results.pluck(:id)).to match_array([ai1.id, ai2.id]) + end + end + + context 'when there are no organizations within the max search distance' do + it 'returns an empty array' do + # check within 1 mile of Washington, D.C. + results = described_class.find_within_max_distance(-77.0369, 38.9072, 1609.344) + + expect(results).to eq([]) + end + end + end + + describe '#registration_numbers' do + let(:organization) { create(:accredited_organization) } + + context 'when the organization has no accredited_individual associations' do + it 'returns an empty array' do + expect(organization.registration_numbers).to eq([]) + end + end + + context 'when the organization has accredited_individual associations' do + let(:ind1) { create(:accredited_individual, registration_number: '12300') } + let(:ind2) { create(:accredited_individual, registration_number: '45600') } + + it 'returns an array of all associated registration_numbers' do + organization.accredited_individuals.push(ind1, ind2) + + expect(organization.reload.registration_numbers).to match_array(%w[12300 45600]) + end + end + end +end From 7233ff195400a5f4c1e7e57622ca3e2bee3e7459 Mon Sep 17 00:00:00 2001 From: John Bramley Date: Tue, 23 Apr 2024 07:53:57 -0600 Subject: [PATCH 04/15] makes PDFTK homebrew & local constants conditionally set (#16440) --- modules/ivc_champva/lib/tasks/forms.rake | 4 ++-- modules/simple_forms_api/lib/tasks/forms.rake | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ivc_champva/lib/tasks/forms.rake b/modules/ivc_champva/lib/tasks/forms.rake index c70f17e175d..d8b827c7dd9 100644 --- a/modules/ivc_champva/lib/tasks/forms.rake +++ b/modules/ivc_champva/lib/tasks/forms.rake @@ -1,7 +1,7 @@ # frozen_string_literal: true -PDFTK_HOMEBREW_PATH = '/opt/homebrew/bin/pdftk' -PDFTK_LOCAL_PATH = '/usr/local/bin/pdftk' +PDFTK_HOMEBREW_PATH = '/opt/homebrew/bin/pdftk' unless defined?(PDFTK_HOMEBREW_PATH) +PDFTK_LOCAL_PATH = '/usr/local/bin/pdftk' unless defined?(PDFTK_LOCAL_PATH) MODELS_PATH = 'modules/ivc_champva/app/models/ivc_champva' MAPPINGS_PATH = 'modules/ivc_champva/app/form_mappings' diff --git a/modules/simple_forms_api/lib/tasks/forms.rake b/modules/simple_forms_api/lib/tasks/forms.rake index 650532edc2f..9004382cea9 100644 --- a/modules/simple_forms_api/lib/tasks/forms.rake +++ b/modules/simple_forms_api/lib/tasks/forms.rake @@ -1,7 +1,7 @@ # frozen_string_literal: true -PDFTK_HOMEBREW_PATH = '/opt/homebrew/bin/pdftk' -PDFTK_LOCAL_PATH = '/usr/local/bin/pdftk' +PDFTK_HOMEBREW_PATH = '/opt/homebrew/bin/pdftk' unless defined?(PDFTK_HOMEBREW_PATH) +PDFTK_LOCAL_PATH = '/usr/local/bin/pdftk' unless defined?(PDFTK_LOCAL_PATH) SIMPLE_FORMS_API_MODELS_PATH = 'modules/simple_forms_api/app/models/simple_forms_api' SIMPLE_FORMS_API_MAPPINGS_PATH = 'modules/simple_forms_api/app/form_mappings' From 7dbcb3e395760068fb30f09369750c56937066bf Mon Sep 17 00:00:00 2001 From: Kevin Suarez Date: Tue, 23 Apr 2024 09:55:14 -0400 Subject: [PATCH 05/15] 80453 create a method to upsert IPFs based on 5655 metadata (#16386) * 80453 create a method to upsert IPFs based on 5655 metadata * move user verification logic out of UserVerification model at reviewer's request --- .../debts_api/v0/form5655_submission.rb | 33 +++++++++++++++++ .../debt_api/v0/form5655_submission_spec.rb | 36 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/modules/debts_api/app/models/debts_api/v0/form5655_submission.rb b/modules/debts_api/app/models/debts_api/v0/form5655_submission.rb index 9451214c3a3..8d9452efc34 100644 --- a/modules/debts_api/app/models/debts_api/v0/form5655_submission.rb +++ b/modules/debts_api/app/models/debts_api/v0/form5655_submission.rb @@ -96,5 +96,38 @@ def register_success def streamlined? public_metadata.dig('streamlined', 'value') == true end + + def upsert_in_progress_form + form = InProgressForm.find_or_initialize_by(form_id: '5655', user_uuid:) + form.user_account = user_account_from_uuid(user_uuid) + form.real_user_uuid = user_uuid + + form.update!(form_data: ipf_data, metadata: fresh_metadata) + end + + def fresh_metadata + { + 'return_url' => '/review-and-submit', + 'submission' => { + 'status' => false, + 'error_message' => false, + 'id' => false, + 'timestamp' => false, + 'has_attempted_submit' => false + }, + 'saved_at' => Time.now.to_i, + 'created_at' => Time.now.to_i, + 'expiresAt' => (DateTime.now + 60).to_time.to_i, + 'lastUpdated' => Time.now.to_i, + 'inProgressFormId' => '5655' + } + end + + def user_account_from_uuid(user_uuid) + UserVerification.where(idme_uuid: user_uuid) + .or(UserVerification.where(logingov_uuid: user_uuid)) + .or(UserVerification.where(backing_idme_uuid: user_uuid)) + .last&.user_account + end end end diff --git a/modules/debts_api/spec/models/debt_api/v0/form5655_submission_spec.rb b/modules/debts_api/spec/models/debt_api/v0/form5655_submission_spec.rb index c9ef1655198..12984d5e70d 100644 --- a/modules/debts_api/spec/models/debt_api/v0/form5655_submission_spec.rb +++ b/modules/debts_api/spec/models/debt_api/v0/form5655_submission_spec.rb @@ -39,6 +39,42 @@ end end + describe '.upsert_in_progress_form' do + let(:user) { create(:form5655_submission) } + let(:form5655_submission) { create(:debts_api_form5655_submission, user_uuid: 'b2fab2b56af045e1a9e2394347af91ef') } + let(:in_progress_form) { create(:in_progress_5655_form, user_uuid: 'b2fab2b5-6af0-45e1-a9e2-394347af91ef') } + + context 'without a related InProgressForm' do + it 'updates the related form' do + in_progress_form.destroy! + form = InProgressForm.find_by(form_id: '5655', user_uuid: form5655_submission.user_uuid) + expect(form).to be_nil + + data = '{"its":"me"}' + form5655_submission.ipf_data = data + form5655_submission.upsert_in_progress_form + form = InProgressForm.find_by(form_id: '5655', user_uuid: form5655_submission.user_uuid) + expect(form&.form_data).to eq(data) + end + end + + context 'with a related InProgressForm' do + it 'updates the related form' do + data = '{"its":"me"}' + form5655_submission + in_progress_form + form = InProgressForm.find_by(form_id: '5655', user_uuid: form5655_submission.user_uuid) + expect(form).to be_present + expect(form&.form_data).not_to eq(data) + + form5655_submission.ipf_data = data + form5655_submission.upsert_in_progress_form + form = InProgressForm.find_by(form_id: '5655', user_uuid: form5655_submission.user_uuid) + expect(form&.form_data).to eq(data) + end + end + end + describe '.submit_to_vba' do let(:form5655_submission) { create(:debts_api_form5655_submission) } let(:guy) { create(:form5655_submission) } From 8afe93cd32497ea971396888f178699a429b0a99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:26:58 -0400 Subject: [PATCH 06/15] Bump rubocop from 1.63.2 to 1.63.3 (#16452) Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.63.2 to 1.63.3. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.63.2...v1.63.3) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6971cc5e88e..c378ff0f47d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -890,7 +890,7 @@ GEM rswag-ui (2.13.0) actionpack (>= 3.1, < 7.2) railties (>= 3.1, < 7.2) - rubocop (1.63.2) + rubocop (1.63.3) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) From 13bbc4498ae4731e18825b8e6865d585c7dbb250 Mon Sep 17 00:00:00 2001 From: mchristiansonVA <95487885+mchristiansonVA@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:40:46 -0400 Subject: [PATCH 07/15] Api 35333 update document filenames (#16257) * Updates to set file name for BD uploads * Rubocop adjustments * Update to remove filename params, modify calls to BD * Add handling for supporting doc filename * Remove check for blank filename for v1 form 526 uploads * Disable Rubocop method length * Update tests for filename handling * Update test for filename handling --- .../app/sidekiq/claims_api/claim_uploader.rb | 15 +++++----- modules/claims_api/lib/bd/bd.rb | 28 ++++++++++++++++--- .../claims_api/spec/lib/claims_api/bd_spec.rb | 3 +- .../spec/sidekiq/claim_uploader_spec.rb | 10 +++++-- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/modules/claims_api/app/sidekiq/claims_api/claim_uploader.rb b/modules/claims_api/app/sidekiq/claims_api/claim_uploader.rb index bc6bb6cacc0..e0a87d2c5eb 100644 --- a/modules/claims_api/app/sidekiq/claims_api/claim_uploader.rb +++ b/modules/claims_api/app/sidekiq/claims_api/claim_uploader.rb @@ -7,7 +7,7 @@ module ClaimsApi class ClaimUploader < ClaimsApi::ServiceBase sidekiq_options retry: true, unique_until: :success - def perform(uuid) + def perform(uuid) # rubocop:disable Metrics/MethodLength claim_object = ClaimsApi::SupportingDocument.find_by(id: uuid) || ClaimsApi::AutoEstablishedClaim.find_by(id: uuid) @@ -21,11 +21,12 @@ def perform(uuid) else auth_headers = auto_claim.auth_headers uploader = claim_object.uploader - uploader.retrieve_from_store!(claim_object.file_data['filename']) + original_filename = claim_object.file_data['filename'] + uploader.retrieve_from_store!(original_filename) file_body = uploader.read ClaimsApi::Logger.log('lighthouse_claim_uploader', claim_id: auto_claim.id, attachment_id: uuid) if Flipper.enabled? :claims_claim_uploader_use_bd - bd_upload_body(auto_claim:, file_body:, doc_type:) + bd_upload_body(auto_claim:, file_body:, doc_type:, original_filename:) else EVSS::DocumentsService.new(auth_headers).upload(file_body, claim_upload_document(claim_object)) end @@ -34,19 +35,19 @@ def perform(uuid) private - def bd_upload_body(auto_claim:, file_body:, doc_type:) + def bd_upload_body(auto_claim:, file_body:, doc_type:, original_filename:) fh = Tempfile.new(['pdf_path', '.pdf'], binmode: true) begin fh.write(file_body) fh.close - claim_bd_upload_document(auto_claim, doc_type, fh.path) + claim_bd_upload_document(auto_claim, doc_type, fh.path, original_filename) ensure fh.unlink end end - def claim_bd_upload_document(claim, doc_type, pdf_path) # rubocop:disable Metrics/MethodLength - ClaimsApi::BD.new.upload(claim:, doc_type:, pdf_path:) + def claim_bd_upload_document(claim, doc_type, pdf_path, original_filename) # rubocop:disable Metrics/MethodLength + ClaimsApi::BD.new.upload(claim:, doc_type:, pdf_path:, original_filename:) # Temporary errors (returning HTML, connection timeout), retry call rescue Faraday::ParsingError, Faraday::TimeoutError => e message = get_error_message(e) diff --git a/modules/claims_api/lib/bd/bd.rb b/modules/claims_api/lib/bd/bd.rb index a435e1bf067..dc72ecb6625 100644 --- a/modules/claims_api/lib/bd/bd.rb +++ b/modules/claims_api/lib/bd/bd.rb @@ -33,7 +33,7 @@ def search(claim_id, file_number) # Upload document of mapped claim # # @return success or failure - def upload(claim:, pdf_path:, doc_type: 'L122', file_number: nil) + def upload(claim:, pdf_path:, doc_type: 'L122', file_number: nil, original_filename: nil) unless File.exist? pdf_path ClaimsApi::Logger.log('benefits_documents', detail: "Error uploading doc to BD: #{pdf_path} doesn't exist", claim_id: claim&.id) @@ -41,7 +41,7 @@ def upload(claim:, pdf_path:, doc_type: 'L122', file_number: nil) end @multipart = true - body = generate_upload_body(claim:, doc_type:, pdf_path:, file_number:) + body = generate_upload_body(claim:, doc_type:, pdf_path:, file_number:, original_filename:) res = client.post('documents', body)&.body&.deep_symbolize_keys request_id = res&.dig(:data, :requestId) ClaimsApi::Logger.log( @@ -62,10 +62,11 @@ def upload(claim:, pdf_path:, doc_type: 'L122', file_number: nil) # Generate form body to upload a document # # @return {parameters, file} - def generate_upload_body(claim:, doc_type:, pdf_path:, file_number: nil) + def generate_upload_body(claim:, doc_type:, pdf_path:, file_number: nil, original_filename: nil) payload = {} veteran_name = "#{claim.auth_headers['va_eauth_firstName']}_#{claim.auth_headers['va_eauth_lastName']}" - file_name = "526EZ_#{veteran_name}_#{claim.evss_id}.pdf" + file_name = generate_file_name(doc_type:, veteran_name:, claim_id: claim.evss_id, original_filename:) + data = { data: { systemName: 'VA.gov', @@ -83,6 +84,25 @@ def generate_upload_body(claim:, doc_type:, pdf_path:, file_number: nil) payload end + def generate_file_name(doc_type:, veteran_name:, claim_id:, original_filename:) + if doc_type == 'L122' + "#{veteran_name}_#{claim_id}_526EZ.pdf" + else + filename = get_original_supporting_doc_file_name(original_filename) + "#{veteran_name}_#{claim_id}_#{filename}.pdf" + end + end + + ## + # DisabilityCompensationDocuments method create_unique_filename adds a random 11 digit + # hex string to the original filename, so we remove that to yield the user-submitted + # filename to use as part of the filename uploaded to the BD service. + def get_original_supporting_doc_file_name(original_filename) + file_extension = File.extname(original_filename) + base_filename = File.basename(original_filename, file_extension) + base_filename[0...-12] + end + ## # Configure Faraday base class (and do auth) # diff --git a/modules/claims_api/spec/lib/claims_api/bd_spec.rb b/modules/claims_api/spec/lib/claims_api/bd_spec.rb index 67b2cb70d8c..33fa48aaf72 100644 --- a/modules/claims_api/spec/lib/claims_api/bd_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bd_spec.rb @@ -24,7 +24,8 @@ end it 'uploads an attachment to BD' do - result = subject.send(:generate_upload_body, claim:, doc_type: 'L023', pdf_path:) + result = subject.send(:generate_upload_body, claim:, doc_type: 'L023', original_filename: '21-526EZ.pdf', + pdf_path:) js = JSON.parse(result[:parameters].read) expect(js['data']['docType']).to eq 'L023' end diff --git a/modules/claims_api/spec/sidekiq/claim_uploader_spec.rb b/modules/claims_api/spec/sidekiq/claim_uploader_spec.rb index 099cfb01737..0c2b94d2ef9 100644 --- a/modules/claims_api/spec/sidekiq/claim_uploader_spec.rb +++ b/modules/claims_api/spec/sidekiq/claim_uploader_spec.rb @@ -56,6 +56,8 @@ claim end + let(:original_filename) { 'extras' } + it 'submits successfully' do expect do subject.perform_async(supporting_document.id) @@ -134,7 +136,7 @@ allow(Tempfile).to receive(:new).and_return tf allow(Flipper).to receive(:enabled?).with(:claims_claim_uploader_use_bd).and_return true - args = { claim: auto_claim, doc_type: 'L122', pdf_path: tf.path } + args = { claim: auto_claim, doc_type: 'L122', original_filename: 'extras.pdf', pdf_path: tf.path } expect_any_instance_of(ClaimsApi::BD).to receive(:upload).with(args).and_return true subject.new.perform(auto_claim.id) end @@ -144,7 +146,8 @@ allow(Tempfile).to receive(:new).and_return tf allow(Flipper).to receive(:enabled?).with(:claims_claim_uploader_use_bd).and_return true - args = { claim: supporting_document.auto_established_claim, doc_type: 'L023', pdf_path: tf.path } + args = { claim: supporting_document.auto_established_claim, doc_type: 'L023', + original_filename: 'extras.pdf', pdf_path: tf.path } expect_any_instance_of(ClaimsApi::BD).to receive(:upload).with(args).and_return true subject.new.perform(supporting_document.id) end @@ -161,7 +164,8 @@ text: 'Error calling external service to upload claim document.' } ] } - args = { claim: supporting_document.auto_established_claim, doc_type: 'L023', pdf_path: tf.path } + args = { claim: supporting_document.auto_established_claim, doc_type: 'L023', + original_filename: 'extras.pdf', pdf_path: tf.path } allow_any_instance_of(ClaimsApi::BD).to( receive(:upload).with(args).and_raise(Common::Exceptions::BackendServiceException.new( '', {}, 500, body From 68ed41bd91f089359b098910af3a70f1ed3d1497 Mon Sep 17 00:00:00 2001 From: evansmith Date: Tue, 23 Apr 2024 11:02:34 -0400 Subject: [PATCH 08/15] fix tribal land not appearing (#16461) --- lib/pdf_fill/forms/va21p530v2.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/pdf_fill/forms/va21p530v2.rb b/lib/pdf_fill/forms/va21p530v2.rb index f3ab58cdec1..2dd78c06b2b 100644 --- a/lib/pdf_fill/forms/va21p530v2.rb +++ b/lib/pdf_fill/forms/va21p530v2.rb @@ -619,6 +619,14 @@ def expand_cemetery_location @form_data['stateCemeteryOrTribalTrustZip'] = cemetery_location['zip'] if cemetery_location['zip'].present? end + def expand_tribal_land_location + cemetery_location = @form_data['tribalLandLocation'] + return if cemetery_location.blank? + + @form_data['stateCemeteryOrTribalTrustName'] = cemetery_location['name'] if cemetery_location['name'].present? + @form_data['stateCemeteryOrTribalTrustZip'] = cemetery_location['zip'] if cemetery_location['zip'].present? + end + # VA file number can be up to 10 digits long; An optional leading 'c' or 'C' followed by # 7-9 digits. The file number field on the 4142 form has space for 9 characters so trim the # potential leading 'c' to ensure the file number will fit into the form without overflow. @@ -654,7 +662,7 @@ def expand_confirmation_question def expand_location_question cemetery_location = @form_data['cemetaryLocationQuestion'] @form_data['cemetaryLocationQuestionCemetery'] = select_checkbox(cemetery_location == 'cemetery') - @form_data['cemetaryLocationQuestionTribal'] = select_checkbox(cemetery_location == 'tribal') + @form_data['cemetaryLocationQuestionTribal'] = select_checkbox(cemetery_location == 'tribalLand') @form_data['cemetaryLocationQuestionNone'] = select_checkbox(cemetery_location == 'none') end @@ -718,6 +726,7 @@ def merge_fields(_options = {}) end expand_cemetery_location + expand_tribal_land_location # special case: the UI only has a 'yes' checkbox, so the PDF 'noTransportation' checkbox can never be true. @form_data['hasTransportation'] = @form_data['transportation'] == true ? 'YES' : nil From 5312b9711d061f193eacda7057077a577f7209da Mon Sep 17 00:00:00 2001 From: Eric Tillberg Date: Tue, 23 Apr 2024 11:40:09 -0400 Subject: [PATCH 09/15] Start tracking users in 20-10207 (#16456) --- .../app/models/simple_forms_api/vba_20_10207.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/simple_forms_api/app/models/simple_forms_api/vba_20_10207.rb b/modules/simple_forms_api/app/models/simple_forms_api/vba_20_10207.rb index c0b5c505672..85446b97de0 100644 --- a/modules/simple_forms_api/app/models/simple_forms_api/vba_20_10207.rb +++ b/modules/simple_forms_api/app/models/simple_forms_api/vba_20_10207.rb @@ -3,6 +3,7 @@ module SimpleFormsApi class VBA2010207 include Virtus.model(nullify_blank: true) + STATS_KEY = 'api.simple_forms_api.20_10207' attribute :data @@ -97,7 +98,11 @@ def submission_date_stamps [] end - def track_user_identity(confirmation_number); end + def track_user_identity(confirmation_number) + identity = "#{data['preparer_type']} #{data['third_party_type']}" + StatsD.increment("#{STATS_KEY}.#{identity}") + Rails.logger.info('Simple forms api - 20-10207 submission user identity', identity:, confirmation_number:) + end private From 373a339040fe71b63be33ce90b98a07a7777d73c Mon Sep 17 00:00:00 2001 From: kanchanasuriya <89944361+kanchanasuriya@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:53:54 -0700 Subject: [PATCH 10/15] 71790 CIE facilities payload serializer (#16361) * 71790 CIE facilities payload serializer * Addressing review comments * Fixing rubocop failures --------- Co-authored-by: kanchanasuriya --- .../facilities/facilities_data_serializer.rb | 13 ++ .../facilities_data_serializer_spec.rb | 144 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 modules/check_in/app/serializers/check_in/facilities/facilities_data_serializer.rb create mode 100644 modules/check_in/spec/serializers/check_in/facilities/facilities_data_serializer_spec.rb diff --git a/modules/check_in/app/serializers/check_in/facilities/facilities_data_serializer.rb b/modules/check_in/app/serializers/check_in/facilities/facilities_data_serializer.rb new file mode 100644 index 00000000000..6566d04578f --- /dev/null +++ b/modules/check_in/app/serializers/check_in/facilities/facilities_data_serializer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module CheckIn + module Facilities + class FacilitiesDataSerializer + include JSONAPI::Serializer + + set_id(&:id) + + attribute :name, :type, :classification, :timezone, :phone, :physicalAddress + end + end +end diff --git a/modules/check_in/spec/serializers/check_in/facilities/facilities_data_serializer_spec.rb b/modules/check_in/spec/serializers/check_in/facilities/facilities_data_serializer_spec.rb new file mode 100644 index 00000000000..8dd6b7968a5 --- /dev/null +++ b/modules/check_in/spec/serializers/check_in/facilities/facilities_data_serializer_spec.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CheckIn::Facilities::FacilitiesDataSerializer do + subject { described_class } + + let(:facilities_data) do + { + id: '442', + facilitiesApiId: 'vha_442', + vistaSite: '442', + vastParent: '442', + type: 'va_health_facility', + name: 'Cheyenne VA Medical Center', + classification: 'VA Medical Center (VAMC)', + timezone: { + timeZoneId: 'America/Denver' + }, + lat: 41.148026, + long: -104.786255, + website: 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/', + phone: { + main: '307-778-7550', + fax: '307-778-7381', + pharmacy: '866-420-6337', + afterHours: '307-778-7550', + patientAdvocate: '307-778-7550 x7573', + mentalHealthClinic: '307-778-7349', + enrollmentCoordinator: '307-778-7550 x7579' + }, + mailingAddress: { + type: 'postal', + line: [nil, nil, nil] + }, + physicalAddress: { + type: 'physical', + line: ['2360 East Pershing Boulevard', nil, nil], + city: 'Cheyenne', + state: 'WY', + postalCode: '82001-5356' + }, + mobile: false, + healthService: %w[Audiology Cardiology CaregiverSupport Covid19Vaccine DentalServices Dermatology EmergencyCare + Gastroenterology Gynecology MentalHealthCare Nutrition Ophthalmology Optometry Orthopedics + Podiatry PrimaryCare Urology WomensHealth], + operatingStatus: { + code: 'NORMAL' + }, + visn: '19' + } + end + + describe '#serializable_hash' do + context 'when all the necessary fields exist' do + let(:serialized_hash_response) do + { + data: { + id: '442', + type: :facilities_data, + attributes: { + type: 'va_health_facility', + name: 'Cheyenne VA Medical Center', + classification: 'VA Medical Center (VAMC)', + timezone: { + timeZoneId: 'America/Denver' + }, + phone: { + main: '307-778-7550', + fax: '307-778-7381', + pharmacy: '866-420-6337', + afterHours: '307-778-7550', + patientAdvocate: '307-778-7550 x7573', + mentalHealthClinic: '307-778-7349', + enrollmentCoordinator: '307-778-7550 x7579' + }, + physicalAddress: { + type: 'physical', + line: ['2360 East Pershing Boulevard', nil, nil], + city: 'Cheyenne', + state: 'WY', + postalCode: '82001-5356' + } + } + } + } + end + + it 'returns a serialized hash' do + facilities_struct = OpenStruct.new(facilities_data) + facilities_serializer = CheckIn::Facilities::FacilitiesDataSerializer.new(facilities_struct) + + expect(facilities_serializer.serializable_hash).to eq(serialized_hash_response) + end + end + + context 'when name does not exist' do + let(:facilities_data_without_name) do + facilities_data.except!(:name) + facilities_data + end + + let(:serialized_hash_response) do + { + data: { + id: '442', + type: :facilities_data, + attributes: { + name: nil, + type: 'va_health_facility', + classification: 'VA Medical Center (VAMC)', + timezone: { + timeZoneId: 'America/Denver' + }, + phone: { + main: '307-778-7550', + fax: '307-778-7381', + pharmacy: '866-420-6337', + afterHours: '307-778-7550', + patientAdvocate: '307-778-7550 x7573', + mentalHealthClinic: '307-778-7349', + enrollmentCoordinator: '307-778-7550 x7579' + }, + physicalAddress: { + type: 'physical', + line: ['2360 East Pershing Boulevard', nil, nil], + city: 'Cheyenne', + state: 'WY', + postalCode: '82001-5356' + } + } + } + } + end + + it 'returns a serialized hash with nil in name field' do + facilities_struct = OpenStruct.new(facilities_data_without_name) + facilities_serializer = CheckIn::Facilities::FacilitiesDataSerializer.new(facilities_struct) + + expect(facilities_serializer.serializable_hash).to eq(serialized_hash_response) + end + end + end +end From 116d8c762c256410ad5beeaddf6ea0ee38615825 Mon Sep 17 00:00:00 2001 From: Khoa Nguyen <134089461+Khoa-V-Nguyen@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:36:51 -0600 Subject: [PATCH 11/15] Update `Inquiries::Creator` and `InquiriesController` (#16466) - Updated `inquiry_params` - Fix argumet in `Inquiries::Creator` Co-authored-by: khoa-v-nguyen --- .../ask_va_api/v0/inquiries_controller.rb | 12 ++-- .../app/lib/ask_va_api/inquiries/creator.rb | 4 +- .../lib/ask_va_api/inquiries/creator_spec.rb | 28 ++++---- .../spec/requests/v0/inquiries_spec.rb | 68 +++++++++---------- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/modules/ask_va_api/app/controllers/ask_va_api/v0/inquiries_controller.rb b/modules/ask_va_api/app/controllers/ask_va_api/v0/inquiries_controller.rb index 28b3dbf113f..9cc82b5a60e 100644 --- a/modules/ask_va_api/app/controllers/ask_va_api/v0/inquiries_controller.rb +++ b/modules/ask_va_api/app/controllers/ask_va_api/v0/inquiries_controller.rb @@ -26,12 +26,12 @@ def test_create end def create - response = Inquiries::Creator.new(icn: current_user.icn).call(params: inquiry_params) + response = Inquiries::Creator.new(icn: current_user.icn).call(payload: inquiry_params) render json: response.to_json, status: :created end def unauth_create - response = Inquiries::Creator.new(icn: nil).call(params: inquiry_params) + response = Inquiries::Creator.new(icn: nil).call(payload: inquiry_params) render json: response.to_json, status: :created end @@ -101,7 +101,7 @@ def inquiry_params *dependant_parameters, *submitter_parameters, *veteran_parameters, - school_obj: school_parameters + SchoolObj: school_parameters ).to_h end @@ -112,7 +112,7 @@ def base_parameters InquiryCategory InquirySource InquirySubtopic InquirySummary InquiryTopic InquiryType IsVAEmployee IsVeteran IsVeteranAnEmployee IsVeteranDeceased LevelOfAuthentication MedicalCenter MiddleName PreferredName Pronouns - StreetAddress2 SupervisorFlag VaEmployeeTimeStamp ZipCode + StreetAddress2 SupervisorFlag VaEmployeeTimeStamp ZipCode Suffix ] end @@ -130,7 +130,7 @@ def submitter_parameters Submitter SubmitterDependent SubmitterDOB SubmitterGender SubmitterProvince SubmitterSSN SubmitterState SubmitterStateOfResidency SubmitterStateOfSchool SubmitterStateProperty SubmitterStreetAddress SubmitterVetCenter - SubmitterZipCodeOfResidency SubmitterQuestion + SubmitterZipCodeOfResidency SubmitterQuestion SubmittersDodIdEdipiNumber ] end @@ -143,7 +143,7 @@ def veteran_parameters VeteranRelationship VeteranServiceEndDate VeteranServiceNumber VeteranServiceStartDate VeteranSSN VeteransState VeteranStreetAddress VeteranSuffix VeteranSuiteAptOther VeteranZipCode WhoWasTheirCounselor - YourLastName + YourLastName VeteranDodIdEdipiNumber ] end diff --git a/modules/ask_va_api/app/lib/ask_va_api/inquiries/creator.rb b/modules/ask_va_api/app/lib/ask_va_api/inquiries/creator.rb index dbd8e4e7ef0..5accb20ee13 100644 --- a/modules/ask_va_api/app/lib/ask_va_api/inquiries/creator.rb +++ b/modules/ask_va_api/app/lib/ask_va_api/inquiries/creator.rb @@ -13,8 +13,8 @@ def initialize(icn:, service: nil) @service = service || default_service end - def call(params:) - post_data(payload: { params: }) + def call(payload:) + post_data(payload:) rescue => e ErrorHandler.handle_service_error(e) end diff --git a/modules/ask_va_api/spec/app/lib/ask_va_api/inquiries/creator_spec.rb b/modules/ask_va_api/spec/app/lib/ask_va_api/inquiries/creator_spec.rb index 6bb4e6d651b..91029a32a3d 100644 --- a/modules/ask_va_api/spec/app/lib/ask_va_api/inquiries/creator_spec.rb +++ b/modules/ask_va_api/spec/app/lib/ask_va_api/inquiries/creator_spec.rb @@ -6,7 +6,7 @@ let(:icn) { '123456' } let(:service) { instance_double(Crm::Service) } let(:creator) { described_class.new(icn:, service:) } - let(:params) { { FirstName: 'Fake', YourLastName: 'Smith' } } + let(:payload) { { FirstName: 'Fake', YourLastName: 'Smith' } } let(:endpoint) { AskVAApi::Inquiries::Creator::ENDPOINT } before do @@ -17,21 +17,21 @@ context 'when the API call is successful' do before do allow(service).to receive(:call).with(endpoint:, method: :put, - payload: { params: }).and_return({ - Data: { - InquiryNumber: '530d56a8-affd-ee11' \ - '-a1fe-001dd8094ff1' - }, - Message: '', - ExceptionOccurred: false, - ExceptionMessage: '', - MessageId: 'b8ebd8e7-3bbf-49c5' \ - '-aff0-99503e50ee27' - }) + payload:).and_return({ + Data: { + InquiryNumber: '530d56a8-affd-ee11' \ + '-a1fe-001dd8094ff1' + }, + Message: '', + ExceptionOccurred: false, + ExceptionMessage: '', + MessageId: 'b8ebd8e7-3bbf-49c5' \ + '-aff0-99503e50ee27' + }) end it 'posts data to the service and returns the response' do - expect(creator.call(params:)).to eq({ InquiryNumber: '530d56a8-affd-ee11-a1fe-001dd8094ff1' }) + expect(creator.call(payload:)).to eq({ InquiryNumber: '530d56a8-affd-ee11-a1fe-001dd8094ff1' }) end end @@ -45,7 +45,7 @@ end it 'raise InquiriesCreatorError' do - expect { creator.call(params:) }.to raise_error(ErrorHandler::ServiceError) + expect { creator.call(payload:) }.to raise_error(ErrorHandler::ServiceError) end end end diff --git a/modules/ask_va_api/spec/requests/v0/inquiries_spec.rb b/modules/ask_va_api/spec/requests/v0/inquiries_spec.rb index 111e5fe58fb..3d0a6216ddc 100644 --- a/modules/ask_va_api/spec/requests/v0/inquiries_spec.rb +++ b/modules/ask_va_api/spec/requests/v0/inquiries_spec.rb @@ -332,24 +332,24 @@ end describe 'POST #create' do - let(:params) { { FirstName: 'Fake', YourLastName: 'Smith' } } + let(:payload) { { FirstName: 'Fake', YourLastName: 'Smith' } } let(:endpoint) { AskVAApi::Inquiries::Creator::ENDPOINT } context 'when successful' do before do allow_any_instance_of(Crm::Service).to receive(:call) .with(endpoint:, method: :put, - payload: { params: }).and_return({ - Data: { - Id: '530d56a8-affd-ee11-a1fe-001dd8094ff1' - }, - Message: '', - ExceptionOccurred: false, - ExceptionMessage: '', - MessageId: 'b8ebd8e7-3bbf-49c5-aff0-99503e50ee27' - }) + payload:).and_return({ + Data: { + Id: '530d56a8-affd-ee11-a1fe-001dd8094ff1' + }, + Message: '', + ExceptionOccurred: false, + ExceptionMessage: '', + MessageId: 'b8ebd8e7-3bbf-49c5-aff0-99503e50ee27' + }) sign_in(authorized_user) - post '/ask_va_api/v0/inquiries/auth', params: + post '/ask_va_api/v0/inquiries/auth', params: payload end it { expect(response).to have_http_status(:created) } @@ -360,13 +360,13 @@ before do allow_any_instance_of(Crm::Service).to receive(:call) .with(endpoint:, method: :put, - payload: { params: }).and_return({ Data: nil, - Message: 'Data Validation: missing InquiryCategory', - ExceptionOccurred: true, - ExceptionMessage: 'Data Validation: missing InquiryCategory', - MessageId: '13bc59ea-c90a-4d48-8979-fe71e0f7ddeb' }) + payload:).and_return({ Data: nil, + Message: 'Data Validation: missing InquiryCategory', + ExceptionOccurred: true, + ExceptionMessage: 'Data Validation: missing InquiryCategory', + MessageId: '13bc59ea-c90a-4d48-8979-fe71e0f7ddeb' }) sign_in(authorized_user) - post '/ask_va_api/v0/inquiries/auth', params: + post '/ask_va_api/v0/inquiries/auth', params: payload end it 'raise InquiriesCreatorError' do @@ -380,23 +380,23 @@ end describe 'POST #unauth_create' do - let(:params) { { FirstName: 'Fake', YourLastName: 'Smith' } } + let(:payload) { { FirstName: 'Fake', YourLastName: 'Smith' } } let(:endpoint) { AskVAApi::Inquiries::Creator::ENDPOINT } context 'when successful' do before do allow_any_instance_of(Crm::Service).to receive(:call) .with(endpoint:, method: :put, - payload: { params: }).and_return({ - Data: { - Id: '530d56a8-affd-ee11-a1fe-001dd8094ff1' - }, - Message: '', - ExceptionOccurred: false, - ExceptionMessage: '', - MessageId: 'b8ebd8e7-3bbf-49c5-aff0-99503e50ee27' - }) - post inquiry_path, params: + payload:).and_return({ + Data: { + Id: '530d56a8-affd-ee11-a1fe-001dd8094ff1' + }, + Message: '', + ExceptionOccurred: false, + ExceptionMessage: '', + MessageId: 'b8ebd8e7-3bbf-49c5-aff0-99503e50ee27' + }) + post inquiry_path, params: payload end it { expect(response).to have_http_status(:created) } @@ -407,12 +407,12 @@ before do allow_any_instance_of(Crm::Service).to receive(:call) .with(endpoint:, method: :put, - payload: { params: }).and_return({ Data: nil, - Message: 'Data Validation: missing InquiryCategory', - ExceptionOccurred: true, - ExceptionMessage: 'Data Validation: missing InquiryCategory', - MessageId: '13bc59ea-c90a-4d48-8979-fe71e0f7ddeb' }) - post '/ask_va_api/v0/inquiries', params: + payload:).and_return({ Data: nil, + Message: 'Data Validation: missing InquiryCategory', + ExceptionOccurred: true, + ExceptionMessage: 'Data Validation: missing InquiryCategory', + MessageId: '13bc59ea-c90a-4d48-8979-fe71e0f7ddeb' }) + post '/ask_va_api/v0/inquiries', params: payload end it 'raise InquiriesCreatorError' do From 5c0fef3b1430fed8be96458365d4411bc70d482d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:36:49 -0400 Subject: [PATCH 12/15] Bump coverband from 6.1.0 to 6.1.1 (#16451) Bumps [coverband](https://github.com/danmayer/coverband) from 6.1.0 to 6.1.1. - [Release notes](https://github.com/danmayer/coverband/releases) - [Changelog](https://github.com/danmayer/coverband/blob/main/changes.md) - [Commits](https://github.com/danmayer/coverband/compare/v6.1.0...v6.1.1) --- updated-dependencies: - dependency-name: coverband dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c378ff0f47d..fb14fa4d562 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -317,7 +317,7 @@ GEM content_disposition (1.0.0) cork (0.3.0) colored2 (~> 3.1) - coverband (6.1.0) + coverband (6.1.1) redis (>= 3.0) crack (1.0.0) bigdecimal From ab4d44fe29698ec8d63a54388874f0dfc9b9debb Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 23 Apr 2024 13:21:13 -0700 Subject: [PATCH 13/15] 80854 appts svc using check in UUID (#16432) * 80854 update token service specs * 80854 update base service and specs * 80854 update appointment service specs * 80854 fix specs to get icn from Lorota Redis client --- .../services/check_in/vaos/base_service.rb | 24 ++++++++-- .../check_in/map/token_service_spec.rb | 21 +++++---- .../check_in/vaos/appointment_service_spec.rb | 45 ++++++++++--------- .../check_in/vaos/base_service_spec.rb | 28 +++++++++++- 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/modules/check_in/app/services/check_in/vaos/base_service.rb b/modules/check_in/app/services/check_in/vaos/base_service.rb index 8cf947ab968..8a906d04d98 100644 --- a/modules/check_in/app/services/check_in/vaos/base_service.rb +++ b/modules/check_in/app/services/check_in/vaos/base_service.rb @@ -9,13 +9,25 @@ class BaseService < Common::Client::Base include SentryLogging include Common::Client::Concerns::Monitoring - attr_reader :patient_icn, :token_service + attr_reader :check_in_session, :patient_icn STATSD_KEY_PREFIX = 'api.check_in.vaos' - def initialize(patient_icn:) - @patient_icn = patient_icn - @token_service = CheckIn::Map::TokenService.build({ patient_icn: }) + ## + # Builds a Service instance + # + # @param opts [Hash] options to create the object + # + # @return [Service] an instance of this class + # + def self.build(opts = {}) + new(opts) + end + + def initialize(opts) + @check_in_session = opts[:check_in_session] + @patient_icn = ::V2::Lorota::RedisClient.build.icn(uuid: check_in_session.uuid) + super() end @@ -35,6 +47,10 @@ def headers } end + def token_service + @token_service ||= Map::TokenService.build(patient_icn:) + end + def referrer if Settings.hostname.ends_with?('.gov') "https://#{Settings.hostname}".gsub('vets', 'va') diff --git a/modules/check_in/spec/services/check_in/map/token_service_spec.rb b/modules/check_in/spec/services/check_in/map/token_service_spec.rb index a87429a51f1..a03e1e7b073 100644 --- a/modules/check_in/spec/services/check_in/map/token_service_spec.rb +++ b/modules/check_in/spec/services/check_in/map/token_service_spec.rb @@ -3,14 +3,10 @@ require 'rails_helper' describe CheckIn::Map::TokenService do - subject { described_class } + subject { described_class.build(opts) } let(:patient_icn) { '123' } - let(:opts) do - { - patient_icn: - } - end + let(:opts) { { patient_icn: } } let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) } before do @@ -21,13 +17,13 @@ describe '.build' do it 'returns an instance of Service' do - expect(subject.build(opts)).to be_an_instance_of(described_class) + expect(subject).to be_an_instance_of(described_class) end end describe '#initialize' do it 'has a redis client' do - expect(subject.build(opts).redis_client).to be_a(CheckIn::Map::RedisClient) + expect(subject.redis_client).to be_a(CheckIn::Map::RedisClient) end end @@ -41,7 +37,7 @@ end it 'returns token from redis' do - expect(subject.build(opts).token).to eq(access_token) + expect(subject.token).to eq(access_token) end end @@ -51,8 +47,11 @@ .and_return({ access_token:, expiration: }) end - it 'returns token by calling client' do - expect(subject.build(opts).token).to eq(access_token) + it 'returns token by calling client and saves it in redis' do + redis_client = subject.redis_client + expect(redis_client).to receive(:save_token) + + expect(subject.token).to eq(access_token) end end end diff --git a/modules/check_in/spec/services/check_in/vaos/appointment_service_spec.rb b/modules/check_in/spec/services/check_in/vaos/appointment_service_spec.rb index ca71d3cb05c..37adf918027 100644 --- a/modules/check_in/spec/services/check_in/vaos/appointment_service_spec.rb +++ b/modules/check_in/spec/services/check_in/vaos/appointment_service_spec.rb @@ -3,17 +3,17 @@ require 'rails_helper' describe CheckIn::VAOS::AppointmentService do - subject { described_class.new(patient_icn:) } + subject { described_class } + let(:uuid) { 'd602d9eb-9a31-484f-9637-13ab0b507e0d' } + let(:check_in_session) { CheckIn::V2::Session.build(data: { uuid: }) } let(:patient_icn) { '123' } let(:token) { 'test_token' } let(:request_id) { SecureRandom.uuid } - describe '#initialize' do - it 'returns an instance of service' do - service_obj = subject - expect(service_obj).to be_an_instance_of(CheckIn::VAOS::AppointmentService) - expect(service_obj.token_service).to be_an_instance_of(CheckIn::Map::TokenService) + describe '.build' do + it 'returns an instance of Service' do + expect(subject.build(check_in_session:)).to be_an_instance_of(described_class) end end @@ -42,21 +42,27 @@ let(:faraday_response) { double('Faraday::Response') } let(:faraday_env) { double('Faraday::Env', status: 200, body: appointments_response.to_json) } + before do + allow_any_instance_of(V2::Lorota::RedisClient).to receive(:icn).with(uuid:) + .and_return(patient_icn) + allow_any_instance_of(CheckIn::Map::TokenService).to receive(:token) + .and_return(token) + end + context 'when vaos returns successful response' do before do - allow_any_instance_of(CheckIn::Map::TokenService).to receive(:token) - .and_return(token) - allow_any_instance_of(Faraday::Connection).to receive(:get).with('/vaos/v1/patients/123/appointments', - { start: start_date, end: end_date, - statuses: }) - .and_return(faraday_response) + allow_any_instance_of(Faraday::Connection).to receive(:get) + .with("/vaos/v1/patients/#{patient_icn}/appointments", + { start: start_date, end: end_date, statuses: }) + .and_return(faraday_response) allow(faraday_response).to receive(:env).and_return(faraday_env) end it 'returns appointments' do - response = subject.get_appointments(DateTime.parse(start_date).in_time_zone, - DateTime.parse(end_date).in_time_zone, - statuses) + svc = subject.build(check_in_session:) + response = svc.get_appointments(DateTime.parse(start_date).in_time_zone, + DateTime.parse(end_date).in_time_zone, + statuses) expect(response).to eq(appointments_response) end end @@ -66,8 +72,6 @@ let(:exception) { Common::Exceptions::BackendServiceException.new(nil, {}, resp.status, resp.body) } before do - allow_any_instance_of(CheckIn::Map::TokenService).to receive(:token) - .and_return(token) allow_any_instance_of(Faraday::Connection).to receive(:get).with('/vaos/v1/patients/123/appointments', { start: start_date, end: end_date, statuses: }) @@ -75,10 +79,11 @@ end it 'throws exception' do + svc = subject.build(check_in_session:) expect do - subject.get_appointments(DateTime.parse(start_date).in_time_zone, - DateTime.parse(end_date).in_time_zone, - statuses) + svc.get_appointments(DateTime.parse(start_date).in_time_zone, + DateTime.parse(end_date).in_time_zone, + statuses) end.to(raise_error do |error| expect(error).to be_a(Common::Exceptions::BackendServiceException) end) diff --git a/modules/check_in/spec/services/check_in/vaos/base_service_spec.rb b/modules/check_in/spec/services/check_in/vaos/base_service_spec.rb index f523aab9fc6..e7733b82363 100644 --- a/modules/check_in/spec/services/check_in/vaos/base_service_spec.rb +++ b/modules/check_in/spec/services/check_in/vaos/base_service_spec.rb @@ -3,12 +3,35 @@ require 'rails_helper' describe CheckIn::VAOS::BaseService do - subject { described_class.new(patient_icn:) } + subject { described_class.build(check_in_session:) } + let(:uuid) { 'd602d9eb-9a31-484f-9637-13ab0b507e0d' } + let(:check_in_session) { CheckIn::V2::Session.build(data: { uuid: }) } let(:patient_icn) { '123' } let(:token) { 'test_token' } let(:request_id) { SecureRandom.uuid } + describe '.build' do + it 'returns an instance of Service' do + expect(subject).to be_an_instance_of(described_class) + end + end + + describe '#initialize' do + before do + allow_any_instance_of(V2::Lorota::RedisClient).to receive(:icn).with(uuid:) + .and_return(patient_icn) + end + + it 'has a check_in_session object' do + expect(subject.check_in_session).to be_a(CheckIn::V2::Session) + end + + it 'has a patient icn' do + expect(subject.patient_icn).to eq(patient_icn) + end + end + describe '#config' do it 'returns an instance of Configuration' do expect(subject.config).to be_an_instance_of(CheckIn::VAOS::Configuration) @@ -17,8 +40,9 @@ describe '#headers' do before do - allow_any_instance_of(CheckIn::Map::TokenService).to receive(:token).and_return(token) RequestStore.store['request_id'] = request_id + + allow_any_instance_of(CheckIn::Map::TokenService).to receive(:token).and_return(token) end it 'returns correct headers' do From 5494d604ca12fa2b09b139b9190d594eebd2a9d8 Mon Sep 17 00:00:00 2001 From: mchristiansonVA <95487885+mchristiansonVA@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:46:04 -0400 Subject: [PATCH 14/15] Api 35334 526 docs max length (#16464) * Add schema updates for max length to match pdf generator * Remove maxLength on states, enforced via pattern * Add generated swagger --- .../swagger/claims_api/v2/dev/swagger.json | 97 ++++++++++++++++--- .../claims_api/v2/production/swagger.json | 97 ++++++++++++++++--- modules/claims_api/config/schemas/v2/526.json | 23 +++++ 3 files changed, 189 insertions(+), 28 deletions(-) diff --git a/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json b/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json index 7094044200c..694ef3f86cd 100644 --- a/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json +++ b/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json @@ -967,7 +967,7 @@ "lighthouseId": null, "maxEstClaimDate": null, "minEstClaimDate": null, - "status": "COMPLETE", + "status": "CANCELED", "submitterApplicationCode": "EBN", "submitterRoleCode": "VET", "supportingDocuments": [ @@ -1625,6 +1625,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -1645,6 +1646,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -1687,6 +1689,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -1698,6 +1701,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -1783,6 +1787,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -1794,6 +1799,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -1913,6 +1919,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -1947,12 +1954,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -1978,6 +1987,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -2031,6 +2041,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -2069,12 +2080,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -2126,6 +2139,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -2136,6 +2150,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -2198,6 +2213,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -2208,6 +2224,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -2340,6 +2357,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -2419,10 +2437,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -2532,6 +2552,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -2543,6 +2564,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -2598,6 +2620,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -2885,6 +2908,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -2905,6 +2929,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -2947,6 +2972,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -2958,6 +2984,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -3043,6 +3070,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -3054,6 +3082,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -3173,6 +3202,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -3207,12 +3237,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -3238,6 +3270,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -3291,6 +3324,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -3329,12 +3363,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -3386,6 +3422,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -3396,6 +3433,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -3458,6 +3496,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -3468,6 +3507,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -3600,6 +3640,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -3679,10 +3720,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -3792,6 +3835,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -3803,6 +3847,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -3858,6 +3903,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -4690,6 +4736,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -4710,6 +4757,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -4752,6 +4800,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -4763,6 +4812,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -4848,6 +4898,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -4859,6 +4910,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -4978,6 +5030,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -5012,12 +5065,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -5043,6 +5098,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -5096,6 +5152,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -5134,12 +5191,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -5191,6 +5250,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -5201,6 +5261,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -5263,6 +5324,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -5273,6 +5335,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -5405,6 +5468,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -5484,10 +5548,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -5597,6 +5663,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -5608,6 +5675,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -5663,6 +5731,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -6003,7 +6072,7 @@ "application/json": { "example": { "data": { - "id": "cf532e4e-e89e-4f9c-aebd-ce8361336a41", + "id": "bcf33b41-f27f-429c-b860-f2b71567d87e", "type": "forms/526", "attributes": { "veteran": { @@ -8087,8 +8156,8 @@ "id": "1", "type": "intent_to_file", "attributes": { - "creationDate": "2024-03-26", - "expirationDate": "2025-03-26", + "creationDate": "2024-04-23", + "expirationDate": "2025-04-23", "type": "compensation", "status": "active" } @@ -8807,7 +8876,7 @@ "status": "422", "detail": "Could not retrieve Power of Attorney due to multiple representatives with code: A1Q", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:112:in `representative'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:130:in `representative'" } } ] @@ -8906,7 +8975,7 @@ "application/json": { "example": { "data": { - "id": "29b16b36-3108-411f-9f5f-2c1c2e147ea3", + "id": "48a79d49-eccd-4a89-adb8-e2c91ed3fab5", "type": "individual", "attributes": { "code": "083", @@ -9108,7 +9177,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/individual_controller.rb:35:in `validate_individual_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -9602,7 +9671,7 @@ "application/json": { "example": { "data": { - "id": "a7114d11-8ffd-4545-ad99-d70e74991e11", + "id": "7610ed19-baac-46fe-8ba5-5aa3e64a92ff", "type": "organization", "attributes": { "code": "083", @@ -9812,7 +9881,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/organization_controller.rb:35:in `validate_org_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -10431,7 +10500,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/individual_controller.rb:35:in `validate_individual_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -11126,7 +11195,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/organization_controller.rb:35:in `validate_org_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -11562,11 +11631,11 @@ "application/json": { "example": { "data": { - "id": "7b0c58e1-4bf7-413c-a3ab-c8f6b95208b0", + "id": "09a36719-ad88-46b9-97d1-80a746ef9e81", "type": "claimsApiPowerOfAttorneys", "attributes": { "status": "submitted", - "dateRequestAccepted": "2024-03-26", + "dateRequestAccepted": "2024-04-23", "representative": { "serviceOrganization": { "poaCode": "074" @@ -11802,4 +11871,4 @@ } } ] -} +} \ No newline at end of file diff --git a/modules/claims_api/app/swagger/claims_api/v2/production/swagger.json b/modules/claims_api/app/swagger/claims_api/v2/production/swagger.json index 64add52afd5..83308517114 100644 --- a/modules/claims_api/app/swagger/claims_api/v2/production/swagger.json +++ b/modules/claims_api/app/swagger/claims_api/v2/production/swagger.json @@ -684,7 +684,7 @@ "lighthouseId": null, "maxEstClaimDate": null, "minEstClaimDate": null, - "status": "COMPLETE", + "status": "CANCELED", "submitterApplicationCode": "EBN", "submitterRoleCode": "VET", "supportingDocuments": [ @@ -1342,6 +1342,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -1362,6 +1363,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -1404,6 +1406,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -1415,6 +1418,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -1500,6 +1504,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -1511,6 +1516,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -1630,6 +1636,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -1664,12 +1671,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -1695,6 +1704,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -1748,6 +1758,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -1786,12 +1797,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -1843,6 +1856,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -1853,6 +1867,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -1915,6 +1930,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -1925,6 +1941,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -2057,6 +2074,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -2136,10 +2154,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -2249,6 +2269,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -2260,6 +2281,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -2315,6 +2337,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -2602,6 +2625,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -2622,6 +2646,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -2664,6 +2689,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -2675,6 +2701,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -2760,6 +2787,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -2771,6 +2799,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -2890,6 +2919,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -2924,12 +2954,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -2955,6 +2987,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -3008,6 +3041,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -3046,12 +3080,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -3103,6 +3139,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -3113,6 +3150,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -3175,6 +3213,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -3185,6 +3224,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -3317,6 +3357,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -3396,10 +3437,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -3509,6 +3552,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -3520,6 +3564,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -3575,6 +3620,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -4407,6 +4453,7 @@ "serviceNumber": { "type": "string", "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -4427,6 +4474,7 @@ "type": "string", "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -4469,6 +4517,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -4480,6 +4529,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -4565,6 +4615,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -4576,6 +4627,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -4695,6 +4747,7 @@ "description": "International phone of point of contact.", "type": "string", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -4729,12 +4782,14 @@ "type": "string", "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": "string", "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -4760,6 +4815,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -4813,6 +4869,7 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -4851,12 +4908,14 @@ "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": "string", "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -4908,6 +4967,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -4918,6 +4978,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -4980,6 +5041,7 @@ "type": "string", "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": [ "Agent Orange", "radiation", @@ -4990,6 +5052,7 @@ "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -5122,6 +5185,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -5201,10 +5265,12 @@ "unitName": { "type": "string", "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": "string", + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -5314,6 +5380,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": "string", + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -5325,6 +5392,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -5380,6 +5448,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { @@ -5720,7 +5789,7 @@ "application/json": { "example": { "data": { - "id": "8b3c6607-078b-419b-8549-726da40193df", + "id": "cfe5b467-a176-4987-80a9-4350ba73f350", "type": "forms/526", "attributes": { "veteran": { @@ -7804,8 +7873,8 @@ "id": "1", "type": "intent_to_file", "attributes": { - "creationDate": "2024-03-26", - "expirationDate": "2025-03-26", + "creationDate": "2024-04-23", + "expirationDate": "2025-04-23", "type": "compensation", "status": "active" } @@ -8524,7 +8593,7 @@ "status": "422", "detail": "Could not retrieve Power of Attorney due to multiple representatives with code: A1Q", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:112:in `representative'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:130:in `representative'" } } ] @@ -8623,7 +8692,7 @@ "application/json": { "example": { "data": { - "id": "9a9b6db5-abfc-45f3-ab60-785e3fb052ed", + "id": "74658dd8-0176-4bd4-ac22-beedcef627df", "type": "individual", "attributes": { "code": "083", @@ -8825,7 +8894,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/individual_controller.rb:35:in `validate_individual_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -9319,7 +9388,7 @@ "application/json": { "example": { "data": { - "id": "618d9ba4-44cf-490a-bd56-8012b59b30e7", + "id": "f8eefcaa-020f-473e-9382-11dbd36f32ff", "type": "organization", "attributes": { "code": "083", @@ -9529,7 +9598,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/organization_controller.rb:35:in `validate_org_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -10148,7 +10217,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/individual_controller.rb:35:in `validate_individual_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -10843,7 +10912,7 @@ "status": "404", "detail": "Could not find an Accredited Representative with registration number: 67890 and poa code: 083", "source": { - "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/organization_controller.rb:35:in `validate_org_poa_code!'" + "pointer": "/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb:70:in `validate_registration_number!'" } } ] @@ -11279,11 +11348,11 @@ "application/json": { "example": { "data": { - "id": "5d78a9f2-fffb-4867-a621-6ddee5bd5e58", + "id": "c08567c4-6f1d-49bb-8416-01b91642b301", "type": "claimsApiPowerOfAttorneys", "attributes": { "status": "submitted", - "dateRequestAccepted": "2024-03-26", + "dateRequestAccepted": "2024-04-23", "representative": { "serviceOrganization": { "poaCode": "074" @@ -11519,4 +11588,4 @@ } } ] -} +} \ No newline at end of file diff --git a/modules/claims_api/config/schemas/v2/526.json b/modules/claims_api/config/schemas/v2/526.json index c498373a404..fe5cc33d1bc 100644 --- a/modules/claims_api/config/schemas/v2/526.json +++ b/modules/claims_api/config/schemas/v2/526.json @@ -27,6 +27,7 @@ "serviceNumber": { "type": ["null", "string"], "description": "Service identification number", + "maxLength": 1000, "nullable": true }, "veteranNumber": { @@ -47,6 +48,7 @@ "type": ["string", "null"], "description": "Veteran's international phone number.", "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -89,6 +91,7 @@ "description": "City for the Veteran's current mailing address.", "type": "string", "pattern": "^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -100,6 +103,7 @@ "country": { "description": "Country for the Veteran's current mailing address. Must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -182,6 +186,7 @@ "description": "City for the Veteran's new address.", "type": "string", "pattern": "^$|^([-a-zA-Z0-9'.#]([-a-zA-Z0-9'.# ])?)+$", + "maxLength": 1000, "example": "Portland" }, "state": { @@ -193,6 +198,7 @@ "country": { "description": "Country for the Veteran's new address. Value must match the values returned by the /countries endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current).", "type": "string", + "maxLength": 1000, "example": "USA" }, "zipFirstFive": { @@ -314,6 +320,7 @@ "description": "International phone of point of contact.", "type": ["string", "null"], "example": "+44 20 1234 5678", + "maxLength": 1000, "nullable": true } } @@ -345,12 +352,14 @@ "type": ["string", "null"], "nullable": true, "description": "Approximate begin date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" }, "endDate": { "type": ["string", "null"], "nullable": true, "description": "Approximate end date for serving in Gulf War hazard location.", + "pattern": "^(?:19|20)[0-9][0-9]$|^(?:19|20)[0-9][0-9]-(0[1-9]|1[0-2])$", "example": "2018-06 or 2018" } } @@ -373,6 +382,7 @@ "type": ["string", "null"], "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Other location(s) where Veteran served." }, "serviceDates": { @@ -427,6 +437,7 @@ "type": ["string", "null"], "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 5000, "description": "Exposure to asbestos." }, "exposureDates": { @@ -465,12 +476,14 @@ "type": ["string", "null"], "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Hazard the Veteran was exposed to." }, "exposureLocation": { "type": ["string", "null"], "nullable": true, "pattern": "^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", + "maxLength": 1000, "description": "Location where the exposure happened." }, "exposureDates": { @@ -519,12 +532,14 @@ "type": ["string", "null"], "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": ["Agent Orange", "radiation", "burn pits"] }, "serviceRelevance": { "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury. If the disabilityActionType is 'NEW', the serviceRelevance is required.", "type": ["string", "null"], "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "approximateDate": { @@ -583,12 +598,14 @@ "type": ["string", "null"], "description": "What caused the disability?", "nullable": true, + "maxLength": 1000, "examples": ["Agent Orange", "radiation", "burn pits"] }, "serviceRelevance": { "description": "Explanation of how the disability(ies) relates to the in-service event/exposure/injury.", "type": ["string", "null"], "nullable": true, + "maxLength": 1000, "example": "Heavy equipment operator in service." }, "disabilityActionType": { @@ -705,6 +722,7 @@ "serviceBranch": { "description": "Branch of service during period. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": "string", + "maxLength": 1000, "example": "Air Force" }, "serviceComponent": { @@ -774,10 +792,12 @@ "unitName": { "type": ["string", "null"], "nullable": true, + "maxLength": 1000, "pattern": "^$|([a-zA-Z0-9\\-'.,# ][a-zA-Z0-9\\-'.,# ]?)*$" }, "unitAddress": { "type": ["string", "null"], + "maxLength": 1000, "pattern": "^$|^([-a-zA-Z0-9'.,&#]([-a-zA-Z0-9'.,&# ])?)+$", "nullable": true }, @@ -878,6 +898,7 @@ "futureMilitaryRetiredPayExplanation": { "description": "Explains why future pay will be received.", "type": ["string", "null"], + "maxLength": 1000, "example": "Will be retiring soon.", "nullable": true }, @@ -889,6 +910,7 @@ "branchOfService": { "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": ["string", "null"], + "maxLength": 1000, "nullable": true, "example": "Air Force" }, @@ -942,6 +964,7 @@ "description": "Branch of service. The /service-branches endpoint on the [Benefits Reference Data API](https://developer.va.gov/explore/benefits/docs/benefits_reference_data?version=current) may be used to retrieve list of possible service branches.", "type": ["string", "null"], "nullable": true, + "maxLength": 1000, "example": "Air Force" }, "preTaxAmountReceived": { From 4995a8a476cdbe4e0d8ab619f56aa3e8a3f35a35 Mon Sep 17 00:00:00 2001 From: Aurora <19178435+aurora-a-k-a-lightning@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:57:32 -0700 Subject: [PATCH 15/15] bdex/80773: Toxic Exposure - [Lighthouse API] generatePdf hook up (#16411) * dbex/80773: enable benefits_claims/service#submit526 to call LH's /generatePDF endpoint * bdex/80773: enable benefits_claims/service#submit526 to call LH's /generatePDF endpoint * bdex/80773: enable benefits_claims/service#submit526 to call LH's /generatePDF endpoint * linting fix * bdex/80773: add removal of unicode carriage return and new line characters * bdex/80773: add generate pdf option unit test --------- Co-authored-by: Seth Darr <92405130+sethdarragile6@users.noreply.github.com> Co-authored-by: Seth Darr --- .../benefits_claims/configuration.rb | 2 +- lib/lighthouse/benefits_claims/service.rb | 42 +++++++-- .../benefits_claims/service_spec.rb | 9 ++ .../submit526/200_response_generate_pdf.yml | 85 +++++++++++++++++++ 4 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 spec/support/vcr_cassettes/lighthouse/benefits_claims/submit526/200_response_generate_pdf.yml diff --git a/lib/lighthouse/benefits_claims/configuration.rb b/lib/lighthouse/benefits_claims/configuration.rb index 394611f02d8..72e714d2f80 100644 --- a/lib/lighthouse/benefits_claims/configuration.rb +++ b/lib/lighthouse/benefits_claims/configuration.rb @@ -13,7 +13,7 @@ module BenefitsClaims class Configuration < Common::Client::Configuration::REST self.read_timeout = Settings.lighthouse.benefits_claims.timeout || 20 - API_SCOPES = %w[system/claim.read system/claim.write].freeze + API_SCOPES = %w[system/claim.read system/claim.write system/526-pdf.override].freeze CLAIMS_PATH = 'services/claims/v2/veterans' TOKEN_PATH = 'oauth2/claims/system/v1/token' diff --git a/lib/lighthouse/benefits_claims/service.rb b/lib/lighthouse/benefits_claims/service.rb index 46f200a43ea..57a8f83f54b 100644 --- a/lib/lighthouse/benefits_claims/service.rb +++ b/lib/lighthouse/benefits_claims/service.rb @@ -102,7 +102,8 @@ def create_intent_to_file(type, claimant_ssn, lighthouse_client_id = nil, lighth handle_error(e, lighthouse_client_id, endpoint) end - # submit form526 to Lighthouse API endpoint: /services/claims/v2/veterans/{veteranId}/526 + # submit form526 to Lighthouse API endpoint: /services/claims/v2/veterans/{veteranId}/526 or + # /services/claims/v2/veterans/{veteranId}/526/generatePdf # @param [hash || Requests::Form526] body: a hash representing the form526 # attributes in the Lighthouse request schema # @param [string] lighthouse_client_id: the lighthouse_client_id requested from Lighthouse @@ -111,28 +112,31 @@ def create_intent_to_file(type, claimant_ssn, lighthouse_client_id = nil, lighth # @option options [hash] :body_only only return the body from the request # @option options [string] :aud_claim_url option to override the aud_claim_url for LH Veteran Verification APIs # @option options [hash] :auth_params a hash to send in auth params to create the access token + # @option options [hash] :generate_pdf call the generatePdf endpoint to receive the 526 pdf def submit526(body, lighthouse_client_id = nil, lighthouse_rsa_key_path = nil, options = {}) - endpoint = 'benefits_claims/form/526' + endpoint = '{icn}/526' path = "#{@icn}/526" + if options[:generate_pdf].present? + path += '/generatePDF/minimum-validations' + endpoint += '/generatePDF/minimum-validations' + end + # if we're coming straight from the transformation service without # making this a jsonapi request body first ({data: {type:, attributes}}), # this will put it in the correct format for transmission - body = { - data: { - type: 'form/526', - attributes: body - } - }.as_json.deep_transform_keys { |k| k.camelize(:lower) } + body = build_request_body(body) # Inflection settings force 'current_va_employee' to render as 'currentVAEmployee' in the above camelize() call # Since Lighthouse needs 'currentVaEmployee', the following workaround renames it. fix_current_va_employee(body) + json_body = remove_unicode_characters(body) + response = config.post( path, - body, + json_body, lighthouse_client_id, lighthouse_rsa_key_path, options ) @@ -143,6 +147,26 @@ def submit526(body, lighthouse_client_id = nil, lighthouse_rsa_key_path = nil, o private + def build_request_body(body) + { + data: { + type: 'form/526', + attributes: body + } + }.as_json.deep_transform_keys { |k| k.camelize(:lower) } + end + + # this gsubbing is to fix an issue where the service that generates the 526PDF was failing due to + # unicoded carriage returns: + # i.e.: \n was throwing: "U+000A ('controlLF') is not available in the font Helvetica, encoding: WinAnsiEncoding" + def remove_unicode_characters(body) + body.to_json + .gsub('\\n', ' ') + .gsub('\\r', ' ') + .gsub('\\\\n', ' ') + .gsub('\\\\r', ' ') + end + def fix_current_va_employee(body) if body.dig('data', 'attributes', 'veteranIdentification')&.select do |field| field['currentVAEmployee'] diff --git a/spec/lib/lighthouse/benefits_claims/service_spec.rb b/spec/lib/lighthouse/benefits_claims/service_spec.rb index d5ecd4fe022..05d1fe2e084 100644 --- a/spec/lib/lighthouse/benefits_claims/service_spec.rb +++ b/spec/lib/lighthouse/benefits_claims/service_spec.rb @@ -113,6 +113,15 @@ expect(response.claim_id).to eq(1_234_567_890) end end + + context 'when given the option to use generate pdf' do + it 'calls the generate pdf endpoint' do + VCR.use_cassette('lighthouse/benefits_claims/submit526/200_response_generate_pdf') do + raw_response = @service.submit526({}, '', '', { generate_pdf: true }) + expect(raw_response.body).to eq('No example available') + end + end + end end end end diff --git a/spec/support/vcr_cassettes/lighthouse/benefits_claims/submit526/200_response_generate_pdf.yml b/spec/support/vcr_cassettes/lighthouse/benefits_claims/submit526/200_response_generate_pdf.yml new file mode 100644 index 00000000000..19741d9cff9 --- /dev/null +++ b/spec/support/vcr_cassettes/lighthouse/benefits_claims/submit526/200_response_generate_pdf.yml @@ -0,0 +1,85 @@ +--- +http_interactions: +- request: + method: post + uri: https://sandbox-api.va.gov/services/claims/v2/veterans/123498767V234859/526/generatePDF/minimum-validations + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - Vets.gov Agent + Authorization: + - Bearer blahblahblah + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 28 Feb 2023 21:02:39 GMT + Content-Type: + - text/html; charset=utf-8 + Connection: + - keep-alive + X-Ratelimit-Remaining-Minute: + - '59' + X-Ratelimit-Limit-Minute: + - '60' + Ratelimit-Remaining: + - '59' + Ratelimit-Limit: + - '60' + Ratelimit-Reset: + - '25' + Etag: + - W/"6571c42e57529000188d704a3cd1f46a" + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Git-Sha: + - b20885293917fd081d24899644d2718d2ab4ccf9 + X-Github-Repository: + - https://github.com/department-of-veterans-affairs/vets-api + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - d687047e-5004-43c1-babb-c2f52f2fda40 + X-Runtime: + - '3.569014' + X-Xss-Protection: + - 1; mode=block + Access-Control-Allow-Origin: + - "*" + X-Kong-Upstream-Latency: + - '3573' + X-Kong-Proxy-Latency: + - '24' + Via: + - kong/3.0.2 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Cache-Control: + - no-cache, no-store + Pragma: + - no-cache + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: 'No example available' + recorded_at: Tue, 28 Feb 2023 21:02:39 GMT +recorded_with: VCR 6.1.0