diff --git a/Gemfile.lock b/Gemfile.lock index c97180f9bf7..d347bccd1bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -998,7 +998,7 @@ GEM stringio (3.1.0) strong_migrations (1.8.0) activerecord (>= 5.2) - super_diff (0.11.0) + super_diff (0.12.0) attr_extras (>= 6.2.4) diff-lcs patience_diff diff --git a/config/features.yml b/config/features.yml index 79b167cdef5..4f147f2f588 100644 --- a/config/features.yml +++ b/config/features.yml @@ -77,6 +77,10 @@ features: actor_type: user description: Enables Toxic Exposure questions for 10-10EZR applicants. enable_in_development: true + ezr_auth_only_enabled: + actor_type: user + description: Enables the auth-only experience, allowing only authenticated users to view any part of the form. + enable_in_development: true cerner_override_653: actor_type: user description: This will show the Cerner facility 653 as `isCerner`. diff --git a/db/migrate/20240424132506_remove_ssn_and_icn_from_vye_tables.vye.rb b/db/migrate/20240424132506_remove_ssn_and_icn_from_vye_tables.vye.rb new file mode 100644 index 00000000000..f2fe7877b21 --- /dev/null +++ b/db/migrate/20240424132506_remove_ssn_and_icn_from_vye_tables.vye.rb @@ -0,0 +1,20 @@ +# This migration comes from vye (originally 20240303145700) +class RemoveSsnAndIcnFromVyeTables < ActiveRecord::Migration[7.0] + def change + safety_assured do + remove_columns( + :vye_user_infos, + :icn, + :ssn_ciphertext, + :ssn_digest + ) + + remove_columns( + :vye_pending_documents, + :claim_no_ciphertext, + :ssn_ciphertext, + :ssn_digest + ) + end + end +end diff --git a/db/migrate/20240424132507_remove_address_details_from_vye_user_infos.vye.rb b/db/migrate/20240424132507_remove_address_details_from_vye_user_infos.vye.rb new file mode 100644 index 00000000000..42e3ef38cb9 --- /dev/null +++ b/db/migrate/20240424132507_remove_address_details_from_vye_user_infos.vye.rb @@ -0,0 +1,17 @@ +# This migration comes from vye (originally 20240305034315) +class RemoveAddressDetailsFromVyeUserInfos < ActiveRecord::Migration[7.0] + def change + safety_assured do + remove_columns( + :vye_user_infos, + :full_name_ciphertext, + :address_line2_ciphertext, + :address_line3_ciphertext, + :address_line4_ciphertext, + :address_line5_ciphertext, + :address_line6_ciphertext, + :zip_ciphertext + ) + end + end +end diff --git a/db/migrate/20240424132508_change_datetime_to_date.vye.rb b/db/migrate/20240424132508_change_datetime_to_date.vye.rb new file mode 100644 index 00000000000..c8231dac316 --- /dev/null +++ b/db/migrate/20240424132508_change_datetime_to_date.vye.rb @@ -0,0 +1,16 @@ +# This migration comes from vye (originally 20240415205522) +class ChangeDatetimeToDate < ActiveRecord::Migration[7.1] + def change + safety_assured do + change_column :vye_awards, :award_begin_date, :date + change_column :vye_awards, :award_end_date, :date + change_column :vye_awards, :payment_date, :date + + change_column :vye_pending_documents, :queue_date, :date + + change_column :vye_user_infos, :cert_issue_date, :date + change_column :vye_user_infos, :del_date, :date + change_column :vye_user_infos, :date_last_certified, :date + end + end +end diff --git a/db/migrate/20240424132509_update_verifications.vye.rb b/db/migrate/20240424132509_update_verifications.vye.rb new file mode 100644 index 00000000000..163cefe3b10 --- /dev/null +++ b/db/migrate/20240424132509_update_verifications.vye.rb @@ -0,0 +1,15 @@ +# This migration comes from vye (originally 20240415220728) +class UpdateVerifications < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + add_column :vye_verifications, :user_profile_id, :integer + add_column :vye_verifications, :monthly_rate, :decimal + add_column :vye_verifications, :number_hours, :integer + add_column :vye_verifications, :payment_date, :date + add_column :vye_verifications, :transact_date, :date + add_column :vye_verifications, :trace, :string + + add_index :vye_verifications, :user_profile_id, algorithm: :concurrently + end +end diff --git a/db/migrate/20240424132510_create_vye_bdn_clones.vye.rb b/db/migrate/20240424132510_create_vye_bdn_clones.vye.rb new file mode 100644 index 00000000000..e6ae09aeeae --- /dev/null +++ b/db/migrate/20240424132510_create_vye_bdn_clones.vye.rb @@ -0,0 +1,15 @@ +# This migration comes from vye (originally 20240422033815) +class CreateVyeBdnClones < ActiveRecord::Migration[7.1] + def change + create_table :vye_bdn_clones do |t| + t.boolean :is_active + t.boolean :export_ready + t.date :transact_date + + t.timestamps + end + + add_index :vye_bdn_clones, :is_active, unique: true, where: "is_active IS NOT NULL" + add_index :vye_bdn_clones, :export_ready, unique: true, where: "export_ready IS NOT NULL" + end +end diff --git a/db/migrate/20240424132511_add_to_vye_user_infos.vye.rb b/db/migrate/20240424132511_add_to_vye_user_infos.vye.rb new file mode 100644 index 00000000000..f7a66eec657 --- /dev/null +++ b/db/migrate/20240424132511_add_to_vye_user_infos.vye.rb @@ -0,0 +1,14 @@ +# This migration comes from vye (originally 20240422043836) +class AddToVyeUserInfos < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + add_column :vye_user_infos, :bdn_clone_id, :integer + add_column :vye_user_infos, :bdn_clone_line, :integer + add_column :vye_user_infos, :bdn_clone_active, :boolean + + add_index :vye_user_infos, :bdn_clone_id, algorithm: :concurrently + add_index :vye_user_infos, :bdn_clone_line, algorithm: :concurrently + add_index :vye_user_infos, :bdn_clone_active, algorithm: :concurrently + end +end diff --git a/db/migrate/20240424132512_remove_from_vye_pending_document.vye.rb b/db/migrate/20240424132512_remove_from_vye_pending_document.vye.rb new file mode 100644 index 00000000000..f430386e073 --- /dev/null +++ b/db/migrate/20240424132512_remove_from_vye_pending_document.vye.rb @@ -0,0 +1,8 @@ +# This migration comes from vye (originally 20240422051918) +class RemoveFromVyePendingDocument < ActiveRecord::Migration[7.1] + def change + safety_assured do + remove_column :vye_pending_documents, :encrypted_kms_key + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ce5577884fc..6e5d99a7c15 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_04_18_233828) do +ActiveRecord::Schema[7.1].define(version: 2024_04_24_132512) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" enable_extension "pg_stat_statements" @@ -1379,10 +1379,10 @@ create_table "vye_awards", force: :cascade do |t| t.integer "user_info_id" t.string "cur_award_ind" - t.datetime "award_begin_date" - t.datetime "award_end_date" + t.date "award_begin_date" + t.date "award_end_date" t.integer "training_time" - t.datetime "payment_date" + t.date "payment_date" t.decimal "monthly_rate" t.string "begin_rsn" t.string "end_rsn" @@ -1394,6 +1394,16 @@ t.index ["user_info_id"], name: "index_vye_awards_on_user_info_id" end + create_table "vye_bdn_clones", force: :cascade do |t| + t.boolean "is_active" + t.boolean "export_ready" + t.date "transact_date" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["export_ready"], name: "index_vye_bdn_clones_on_export_ready", unique: true, where: "(export_ready IS NOT NULL)" + t.index ["is_active"], name: "index_vye_bdn_clones_on_is_active", unique: true, where: "(is_active IS NOT NULL)" + end + create_table "vye_direct_deposit_changes", force: :cascade do |t| t.integer "user_info_id" t.string "rpo" @@ -1415,39 +1425,24 @@ end create_table "vye_pending_documents", force: :cascade do |t| - t.string "ssn_digest" - t.text "ssn_ciphertext" - t.string "claim_no_ciphertext" t.string "doc_type" - t.datetime "queue_date" + t.date "queue_date" t.string "rpo" - t.text "encrypted_kms_key" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "user_profile_id" - t.index ["ssn_digest"], name: "index_vye_pending_documents_on_ssn_digest" end create_table "vye_user_infos", force: :cascade do |t| - t.string "icn" - t.string "ssn_digest" - t.text "ssn_ciphertext" t.text "file_number_ciphertext" t.string "suffix" - t.text "full_name_ciphertext" - t.text "address_line2_ciphertext" - t.text "address_line3_ciphertext" - t.text "address_line4_ciphertext" - t.text "address_line5_ciphertext" - t.text "address_line6_ciphertext" - t.text "zip_ciphertext" t.text "dob_ciphertext" t.text "stub_nm_ciphertext" t.string "mr_status" t.string "rem_ent" - t.datetime "cert_issue_date" - t.datetime "del_date" - t.datetime "date_last_certified" + t.date "cert_issue_date" + t.date "del_date" + t.date "date_last_certified" t.integer "rpo_code" t.string "fac_code" t.decimal "payment_amt" @@ -1456,8 +1451,12 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "user_profile_id" - t.index ["icn"], name: "index_vye_user_infos_on_icn" - t.index ["ssn_digest"], name: "index_vye_user_infos_on_ssn_digest" + t.integer "bdn_clone_id" + t.integer "bdn_clone_line" + t.boolean "bdn_clone_active" + t.index ["bdn_clone_active"], name: "index_vye_user_infos_on_bdn_clone_active" + t.index ["bdn_clone_id"], name: "index_vye_user_infos_on_bdn_clone_id" + t.index ["bdn_clone_line"], name: "index_vye_user_infos_on_bdn_clone_line" end create_table "vye_user_profiles", force: :cascade do |t| @@ -1482,7 +1481,14 @@ t.string "source_ind" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "user_profile_id" + t.decimal "monthly_rate" + t.integer "number_hours" + t.date "payment_date" + t.date "transact_date" + t.string "trace" t.index ["user_info_id"], name: "index_vye_verifications_on_user_info_id" + t.index ["user_profile_id"], name: "index_vye_verifications_on_user_profile_id" end create_table "webhooks_notification_attempt_assocs", id: false, force: :cascade do |t| diff --git a/modules/veteran/app/sidekiq/veteran/vso_reloader.rb b/modules/veteran/app/sidekiq/veteran/vso_reloader.rb index 132688277c4..0f11aa7b51f 100644 --- a/modules/veteran/app/sidekiq/veteran/vso_reloader.rb +++ b/modules/veteran/app/sidekiq/veteran/vso_reloader.rb @@ -56,7 +56,7 @@ def reload_vso_reps state: vso_rep['Org State'] } end.compact.uniq - Veteran::Service::Organization.import(vso_orgs, on_duplicate_key_ignore: true) + Veteran::Service::Organization.import(vso_orgs, on_duplicate_key_update: %i[name phone state]) vso_reps end diff --git a/modules/veteran/spec/sidekiq/veteran/vso_reloader_spec.rb b/modules/veteran/spec/sidekiq/veteran/vso_reloader_spec.rb index e5d826d9754..1c5728b7e9c 100644 --- a/modules/veteran/spec/sidekiq/veteran/vso_reloader_spec.rb +++ b/modules/veteran/spec/sidekiq/veteran/vso_reloader_spec.rb @@ -46,6 +46,29 @@ end end + context 'existing organizations' do + let(:org) do + create(:organization, poa: '091', name: 'Testing', phone: '222-555-5555', state: 'ZZ', city: 'New York') + end + + it 'only updates name, phone, and state' do + VCR.use_cassette('veteran/ogc_vso_rep_data') do + expect(org.name).to eq('Testing') + expect(org.phone).to eq('222-555-5555') + expect(org.state).to eq('ZZ') + expect(org.city).to eq('New York') + + Veteran::VSOReloader.new.reload_vso_reps + org.reload + + expect(org.name).to eq('African American PTSD Association') + expect(org.phone).to eq('253-589-0766') + expect(org.state).to eq('WA') + expect(org.city).to eq('New York') + end + end + end + describe "storing a VSO's middle initial" do it 'stores the middle initial if it exists' do VCR.use_cassette('veteran/ogc_vso_rep_data') do diff --git a/modules/vye/app/controllers/vye/v1/verifications_controller.rb b/modules/vye/app/controllers/vye/v1/verifications_controller.rb index 40fc32ae139..413c53f6ade 100644 --- a/modules/vye/app/controllers/vye/v1/verifications_controller.rb +++ b/modules/vye/app/controllers/vye/v1/verifications_controller.rb @@ -12,7 +12,9 @@ class Vye::V1::VerificationsController < Vye::V1::ApplicationController def create authorize user_info, policy_class: UserInfoPolicy - user_info.verifications.create!(source_ind:) + award = user_info.awards.first + user_profile = user_info.user_profile + Verification.create!(source_ind:, award:, user_profile:) end private diff --git a/modules/vye/app/models/vye/address_change.rb b/modules/vye/app/models/vye/address_change.rb index 6a0b2f0c649..5ebeab75929 100644 --- a/modules/vye/app/models/vye/address_change.rb +++ b/modules/vye/app/models/vye/address_change.rb @@ -23,7 +23,17 @@ class Vye::AddressChange < ApplicationRecord presence: true, if: -> { origin == 'backend' } ) - enum origin: { frontend: 'f', backend: 'b' } + enum origin: { + + frontend: 'f', + + # This is a special case where the record was created on the frontend + # but will not have been reflected from the backend yet + cached: 'c', + + backend: 'b' + + } scope :created_today, lambda { includes(user_info: :user_profile) diff --git a/modules/vye/app/models/vye/award.rb b/modules/vye/app/models/vye/award.rb index f37db8962a3..f7755be9089 100644 --- a/modules/vye/app/models/vye/award.rb +++ b/modules/vye/app/models/vye/award.rb @@ -3,6 +3,7 @@ module Vye class Vye::Award < ApplicationRecord belongs_to :user_info + has_many :verifications, dependent: :nullify enum cur_award_ind: { current: 'C', future: 'F', past: 'P' } diff --git a/modules/vye/app/models/vye/bdn_clone.rb b/modules/vye/app/models/vye/bdn_clone.rb new file mode 100644 index 00000000000..1ae8895f9e1 --- /dev/null +++ b/modules/vye/app/models/vye/bdn_clone.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Vye::BdnClone < ApplicationRecord +end diff --git a/modules/vye/app/models/vye/pending_document.rb b/modules/vye/app/models/vye/pending_document.rb index c94555c2cdc..7130c96b84b 100644 --- a/modules/vye/app/models/vye/pending_document.rb +++ b/modules/vye/app/models/vye/pending_document.rb @@ -2,7 +2,7 @@ module Vye class Vye::PendingDocument < ApplicationRecord - self.ignored_columns += %i[claim_no_ciphertext ssn_ciphertext ssn_digest] + self.ignored_columns += %i[claim_no_ciphertext encrypted_kms_key ssn_ciphertext ssn_digest] belongs_to :user_profile diff --git a/modules/vye/app/models/vye/user_info.rb b/modules/vye/app/models/vye/user_info.rb index a846faf238d..4cae7864942 100644 --- a/modules/vye/app/models/vye/user_info.rb +++ b/modules/vye/app/models/vye/user_info.rb @@ -20,13 +20,16 @@ class Vye::UserInfo < ApplicationRecord has_many :address_changes, dependent: :destroy has_many :awards, dependent: :destroy has_many :direct_deposit_changes, dependent: :destroy - has_many :verifications, dependent: :destroy + + scope :with_bdn_clone_active, -> { where(bdn_clone_active: true) } enum mr_status: { active: 'A', expired: 'E' } enum indicator: { chapter1606: 'A', chapter1607: 'E', chapter30: 'B', D: 'D' } delegate :icn, to: :user_profile, allow_nil: true - delegate :pending_documents, to: :user_profile, allow_nil: true + delegate :ssn, to: :mpi_profile, allow_nil: true + delegate :pending_documents, to: :user_profile + delegate :verifications, to: :user_profile has_kms_key has_encrypted(:dob, :file_number, :stub_nm, key: :kms_key, **lockbox_options) @@ -43,10 +46,6 @@ def verification_required verifications.empty? end - def ssn - mpi_profile&.ssn - end - private def mpi_profile diff --git a/modules/vye/app/models/vye/user_profile.rb b/modules/vye/app/models/vye/user_profile.rb index 2c5119fc63f..d4e7265339f 100644 --- a/modules/vye/app/models/vye/user_profile.rb +++ b/modules/vye/app/models/vye/user_profile.rb @@ -4,17 +4,15 @@ class Vye::UserProfile < ApplicationRecord include Vye::DigestProtected has_many :user_infos, dependent: :restrict_with_exception - - has_many( - :active_user_infos, - lambda { - order(created_at: :desc).limit(1) - }, - class_name: 'Vye::UserInfo', inverse_of: :user_profile, + has_one( + :active_user_info, + -> { with_bdn_clone_active }, + class_name: 'Vye::UserInfo', + inverse_of: :user_profile, dependent: :restrict_with_exception ) - has_many :pending_documents, dependent: :restrict_with_exception + has_many :verifications, dependent: :restrict_with_exception digest_attribute :ssn digest_attribute :file_number @@ -28,11 +26,7 @@ class Vye::UserProfile < ApplicationRecord end end - scope :with_assos, -> { includes(:pending_documents, active_user_infos: %i[address_changes awards verifications]) } - - def active_user_info - active_user_infos.first - end + scope :with_assos, -> { includes(:pending_documents, :verifications, active_user_info: %i[address_changes awards]) } def self.find_and_update_icn(user:) return if user.blank? diff --git a/modules/vye/app/models/vye/verification.rb b/modules/vye/app/models/vye/verification.rb index 99e903b1917..9e1ef5316cf 100644 --- a/modules/vye/app/models/vye/verification.rb +++ b/modules/vye/app/models/vye/verification.rb @@ -2,25 +2,29 @@ module Vye class Vye::Verification < ApplicationRecord - belongs_to :user_info + belongs_to :user_profile + belongs_to :award, optional: true validates(:source_ind, presence: true) enum source_ind: { web: 'W', phone: 'P' } - scope :created_today, -> { includes(:user_info).where('created_at >= ?', Time.zone.now.beginning_of_day) } - def self.todays_verifications - created_today.each_with_object([]) do |record, result| - result << { - stub_nm: record.user_info.stub_nm, - ssn: record.user_info.ssn, - transact_date: record.created_at.strftime('%Y%m%d'), - rpo_code: record.user_info.rpo_code, - indicator: record.user_info.indicator, - source_ind: record.source_ind - } - end + UserInfo + .joins(awards: :verifications) + .includes(awards: :verifications) + .distinct + .each_with_object([]) do |user_info, result| + verification = user_info.awards.map(&:verifications).flatten.first + result << { + stub_nm: user_info.stub_nm, + ssn: user_info.ssn, + transact_date: verification.created_at.strftime('%Y%m%d'), + rpo_code: user_info.rpo_code, + indicator: user_info.indicator, + source_ind: verification.source_ind + } + end end def self.todays_verifications_report diff --git a/modules/vye/db/migrate/20240303145700_remove_ssn_and_icn_from_vye_tables.rb b/modules/vye/db/migrate/20240303145700_remove_ssn_and_icn_from_vye_tables.rb new file mode 100644 index 00000000000..60e78894fe5 --- /dev/null +++ b/modules/vye/db/migrate/20240303145700_remove_ssn_and_icn_from_vye_tables.rb @@ -0,0 +1,19 @@ +class RemoveSsnAndIcnFromVyeTables < ActiveRecord::Migration[7.0] + def change + safety_assured do + remove_columns( + :vye_user_infos, + :icn, + :ssn_ciphertext, + :ssn_digest + ) + + remove_columns( + :vye_pending_documents, + :claim_no_ciphertext, + :ssn_ciphertext, + :ssn_digest + ) + end + end +end diff --git a/modules/vye/db/migrate/20240305034315_remove_address_details_from_vye_user_infos.rb b/modules/vye/db/migrate/20240305034315_remove_address_details_from_vye_user_infos.rb new file mode 100644 index 00000000000..f565b836ffb --- /dev/null +++ b/modules/vye/db/migrate/20240305034315_remove_address_details_from_vye_user_infos.rb @@ -0,0 +1,16 @@ +class RemoveAddressDetailsFromVyeUserInfos < ActiveRecord::Migration[7.0] + def change + safety_assured do + remove_columns( + :vye_user_infos, + :full_name_ciphertext, + :address_line2_ciphertext, + :address_line3_ciphertext, + :address_line4_ciphertext, + :address_line5_ciphertext, + :address_line6_ciphertext, + :zip_ciphertext + ) + end + end +end diff --git a/modules/vye/db/migrate/20240415205522_change_datetime_to_date.rb b/modules/vye/db/migrate/20240415205522_change_datetime_to_date.rb new file mode 100644 index 00000000000..849c162cb1a --- /dev/null +++ b/modules/vye/db/migrate/20240415205522_change_datetime_to_date.rb @@ -0,0 +1,15 @@ +class ChangeDatetimeToDate < ActiveRecord::Migration[7.1] + def change + safety_assured do + change_column :vye_awards, :award_begin_date, :date + change_column :vye_awards, :award_end_date, :date + change_column :vye_awards, :payment_date, :date + + change_column :vye_pending_documents, :queue_date, :date + + change_column :vye_user_infos, :cert_issue_date, :date + change_column :vye_user_infos, :del_date, :date + change_column :vye_user_infos, :date_last_certified, :date + end + end +end diff --git a/modules/vye/db/migrate/20240415220728_update_verifications.rb b/modules/vye/db/migrate/20240415220728_update_verifications.rb new file mode 100644 index 00000000000..0032f0ef130 --- /dev/null +++ b/modules/vye/db/migrate/20240415220728_update_verifications.rb @@ -0,0 +1,14 @@ +class UpdateVerifications < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + add_column :vye_verifications, :user_profile_id, :integer + add_column :vye_verifications, :monthly_rate, :decimal + add_column :vye_verifications, :number_hours, :integer + add_column :vye_verifications, :payment_date, :date + add_column :vye_verifications, :transact_date, :date + add_column :vye_verifications, :trace, :string + + add_index :vye_verifications, :user_profile_id, algorithm: :concurrently + end +end diff --git a/modules/vye/db/migrate/20240422033815_create_vye_bdn_clones.rb b/modules/vye/db/migrate/20240422033815_create_vye_bdn_clones.rb new file mode 100644 index 00000000000..289ac896503 --- /dev/null +++ b/modules/vye/db/migrate/20240422033815_create_vye_bdn_clones.rb @@ -0,0 +1,14 @@ +class CreateVyeBdnClones < ActiveRecord::Migration[7.1] + def change + create_table :vye_bdn_clones do |t| + t.boolean :is_active + t.boolean :export_ready + t.date :transact_date + + t.timestamps + end + + add_index :vye_bdn_clones, :is_active, unique: true, where: "is_active IS NOT NULL" + add_index :vye_bdn_clones, :export_ready, unique: true, where: "export_ready IS NOT NULL" + end +end diff --git a/modules/vye/db/migrate/20240422043836_add_to_vye_user_infos.rb b/modules/vye/db/migrate/20240422043836_add_to_vye_user_infos.rb new file mode 100644 index 00000000000..23b81278a0f --- /dev/null +++ b/modules/vye/db/migrate/20240422043836_add_to_vye_user_infos.rb @@ -0,0 +1,13 @@ +class AddToVyeUserInfos < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + add_column :vye_user_infos, :bdn_clone_id, :integer + add_column :vye_user_infos, :bdn_clone_line, :integer + add_column :vye_user_infos, :bdn_clone_active, :boolean + + add_index :vye_user_infos, :bdn_clone_id, algorithm: :concurrently + add_index :vye_user_infos, :bdn_clone_line, algorithm: :concurrently + add_index :vye_user_infos, :bdn_clone_active, algorithm: :concurrently + end +end diff --git a/modules/vye/db/migrate/20240422051918_remove_from_vye_pending_document.rb b/modules/vye/db/migrate/20240422051918_remove_from_vye_pending_document.rb new file mode 100644 index 00000000000..0b87976e9aa --- /dev/null +++ b/modules/vye/db/migrate/20240422051918_remove_from_vye_pending_document.rb @@ -0,0 +1,7 @@ +class RemoveFromVyePendingDocument < ActiveRecord::Migration[7.1] + def change + safety_assured do + remove_column :vye_pending_documents, :encrypted_kms_key + end + end +end diff --git a/modules/vye/lib/tasks/vye.rake b/modules/vye/lib/tasks/vye.rake index e82afb3abf4..a9674415cdc 100644 --- a/modules/vye/lib/tasks/vye.rake +++ b/modules/vye/lib/tasks/vye.rake @@ -1,6 +1,15 @@ # frozen_string_literal: true namespace :vye do + namespace :feature do + desc 'Enables request_allowed feature flag' + task request_allowed: :environment do |_cmd, _args| + current_state = Flipper.enabled?(:vye_request_allowed) + puts format('Current state vye_request_allowed is: %s', current_state:) + Flipper.enable :vye_request_allowed + end + end + namespace :install do desc 'Installs config into config/settings.local.yml' task config: :environment do |_cmd, _args| @@ -13,4 +22,53 @@ namespace :vye do local_path.write(engine_dev_path.read, mode: 'a') end end + + namespace :data do + desc 'Clear VYE data from the database' + task clear: :environment do |_cmd, _args| + Vye::AddressChange.destroy_all + Vye::DirectDepositChange.destroy_all + Vye::Verification.destroy_all + Vye::Award.destroy_all + Vye::UserInfo.destroy_all + + Vye::PendingDocument.destroy_all + + Vye::UserProfile.destroy_all + end + + desc 'Build YAML files to load for development from team sensitive data' + task build: :environment do |_cmd, _args| + source = Pathname('/projects/va.gov-team-sensitive') + target = Rails.root / 'tmp' + handles = nil + + build = Vye::StagingData::Build.new(target:) do |paths| + handles = + paths + .transform_values do |value| + (source / value).open + end + end + + build.dump + handles.each_value(&:close) + end + + desc 'Load development YAML files into the database' + task :load, [:path] => :environment do |_cmd, args| + raise 'load path is required' if args[:path].nil? + + root = Pathname(args[:path]) + files = root.glob('**/*.yaml') + raise "No files found in #{root}" if files.empty? + + files.each do |file| + source = :team_sensitive + data = YAML.safe_load(file.read, permitted_classes: [Date, DateTime, Symbol, Time]) + records = data.slice(:profile, :info, :address, :awards, :pending_documents) + Vye::LoadData.new(source:, records:) + end + end + end end diff --git a/modules/vye/lib/vye/staging_data/build.rb b/modules/vye/lib/vye/staging_data/build.rb new file mode 100644 index 00000000000..36ae85826f4 --- /dev/null +++ b/modules/vye/lib/vye/staging_data/build.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +module Vye + module StagingData + class Build + MAX_AWARD_COUNT = 4 + MAX_PENDING_DOCUMENT_COUNT = 1 + PATHS = + { + test_users: 'Administrative/vagov-users/test_users.csv', + mvi_staging_users: 'Administrative/vagov-users/mvi-staging-users.csv' + }.freeze + + private_constant :PATHS, :MAX_AWARD_COUNT, :MAX_PENDING_DOCUMENT_COUNT + + def dump + @dump if defined?(@dump) + + rows.each do |row| + summary = row[:summary] + path = root / format('%s.yaml', summary[:full_name].downcase.gsub(/\s+/, '-')) + path.write(row.to_yaml) + end + + @dump = true + end + + private + + attr_reader :test_users, :mvi_staging_users, :target + + def initialize(target:) + yield(PATHS) => {test_users:, mvi_staging_users:} + @test_users = CSV.new(test_users, headers: true).each.to_a + @mvi_staging_users = CSV.new(mvi_staging_users, headers: true).each.to_a + @target = target + end + + def cross + return @cross if defined?(@cross) + + product = + test_users + .product(mvi_staging_users) + .select do |tu, msu| + tu['ssn'] == msu['ssn'] + end + + @cross = product.group_by { |x| x.first['ssn'] }.values.pluck(0) + end + + def rows + @rows ||= + cross + .map do |tu, msu| + tu = extract_from_tu(tu) + msu = extract_from_msu(msu) + + summary = {}.merge(tu).merge(msu) + + { + summary:, + profile: fake_user_profile(summary:), + info: fake_user_info(summary:), + address: fake_address_change(summary:), + awards: fake_awards, + pending_documents: fake_pending_documents + } + end + end + + def root + return @root if defined?(@root) + + timestamp = Time.zone.now.strftime('%Y%m%dT%H%M%S%z') + root = target / format('vye/staging-data-%s', timestamp:) + root.mkpath + + @root = root + end + + def fake_user_profile(summary:) + FactoryBot.attributes_for(:vye_user_profile).except(:ssn, :icn, :file_number).tap do |record| + record[:ssn] = summary[:ssn] + record[:file_number] = summary[:ssn] + record[:icn] = summary[:icn] + end + end + + def fake_user_info(summary:) + FactoryBot.attributes_for(:vye_user_info).except(:full_name).tap do |record| + name = summary[:full_name] + parts = name.split(/\s+/) + initials = parts.pluck(0).join + rest = parts[-1][1..(7 - initials.length)] + stub_nm = [initials, rest].join.upcase + + record[:stub_nm] = stub_nm + record[:file_number] = summary[:ssn] + end + end + + def fake_address_change(summary:) + FactoryBot.attributes_for(:vye_address_backend).tap do |record| + record[:veteran_name] = summary[:full_name] + end + end + + def fake_awards + (1..rand(1..4)).map do + FactoryBot.attributes_for(:vye_award) + end + end + + def fake_pending_documents + (0..rand(0..1)).map do + FactoryBot.attributes_for(:vye_pending_document) + end + end + + def extract_from_tu(row) + ssn = scrub_ssn(row['ssn']) + idme_uuid = row['idme_uuid']&.strip + email = row['email']&.strip + password = row['password']&.strip + full_name = + row.values_at( + 'first_name', + 'middle_name', + 'last_name' + ).compact.map(&:strip).map(&:capitalize).join(' ').strip + + { ssn:, idme_uuid:, email:, password:, full_name: } + end + + def extract_from_msu(row) + ssn = scrub_ssn(row['ssn']) + icn = row['icn']&.strip + full_name = + row.values_at( + 'first_name', + 'middle_name', + 'last_name' + ).compact.map(&:strip).map(&:capitalize).join(' ').strip + + { ssn:, icn:, full_name: } + end + + def scrub_ssn(value) + value&.gsub(/\D/, '')&.strip + end + end + end +end diff --git a/modules/vye/spec/factories/vye/address_changes.rb b/modules/vye/spec/factories/vye/address_changes.rb index 40bc66818a8..6c3a882994e 100644 --- a/modules/vye/spec/factories/vye/address_changes.rb +++ b/modules/vye/spec/factories/vye/address_changes.rb @@ -9,4 +9,13 @@ zip_code { Faker::Address.zip_code } origin { Vye::AddressChange.origins['frontend'] } end + + factory :vye_address_backend, class: 'Vye::AddressChange' do + veteran_name { Faker::Name.name } + address1 { Faker::Address.street_address } + city { Faker::Address.city } + state { Faker::Address.state_abbr } + zip_code { Faker::Address.zip_code } + origin { Vye::AddressChange.origins['backend'] } + end end diff --git a/modules/vye/spec/factories/vye/awards.rb b/modules/vye/spec/factories/vye/awards.rb index 06e09afaf7a..4411fc2f099 100644 --- a/modules/vye/spec/factories/vye/awards.rb +++ b/modules/vye/spec/factories/vye/awards.rb @@ -2,6 +2,8 @@ FactoryBot.define do factory :vye_award, class: 'Vye::Award' do + association :user_info, factory: :vye_user_info + cur_award_ind { Vye::Award.cur_award_inds.values.sample } award_begin_date { DateTime.now } award_end_date { DateTime.now + 1.month } diff --git a/modules/vye/spec/factories/vye/bdn_clones.rb b/modules/vye/spec/factories/vye/bdn_clones.rb new file mode 100644 index 00000000000..482a4db9704 --- /dev/null +++ b/modules/vye/spec/factories/vye/bdn_clones.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :vye_bdn_clone, class: 'Vye::BdnClone' do + is_active { true } + export_ready { false } + end +end diff --git a/modules/vye/spec/factories/vye/user_infos.rb b/modules/vye/spec/factories/vye/user_infos.rb index 20176580da1..a864f672c97 100644 --- a/modules/vye/spec/factories/vye/user_infos.rb +++ b/modules/vye/spec/factories/vye/user_infos.rb @@ -22,5 +22,6 @@ fac_code { Faker::Lorem.word } payment_amt { Faker::Number.decimal(l_digits: 4, r_digits: 2) } indicator { Vye::UserInfo.indicators.values.sample } + bdn_clone_active { true } end end diff --git a/modules/vye/spec/factories/vye/user_profiles.rb b/modules/vye/spec/factories/vye/user_profiles.rb index 673eef21678..a887e216c95 100644 --- a/modules/vye/spec/factories/vye/user_profiles.rb +++ b/modules/vye/spec/factories/vye/user_profiles.rb @@ -6,6 +6,6 @@ factory :vye_user_profile, class: 'Vye::UserProfile' do ssn { (1..9).map(&digit).join } file_number { (1..9).map(&digit).join } - icn { 'random-icn' } + icn { SecureRandom.uuid } end end diff --git a/modules/vye/spec/factories/vye/verifications.rb b/modules/vye/spec/factories/vye/verifications.rb index 4cee53aaf16..f82142385b3 100644 --- a/modules/vye/spec/factories/vye/verifications.rb +++ b/modules/vye/spec/factories/vye/verifications.rb @@ -2,6 +2,9 @@ FactoryBot.define do factory :vye_verification, class: 'Vye::Verification' do + association :user_profile, factory: :vye_user_profile + association :award, factory: :vye_award + source_ind { Vye::Verification.source_inds.values.sample } end end diff --git a/modules/vye/spec/lib/vye/staging_data/build_spec.rb b/modules/vye/spec/lib/vye/staging_data/build_spec.rb new file mode 100644 index 00000000000..f6555bfdf0e --- /dev/null +++ b/modules/vye/spec/lib/vye/staging_data/build_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Vye::StagingData::Build do + describe '#dump' do + let(:target) { double('Pathname (Target)') } + + let(:source) { double('Pathname (Source)') } + + let(:streams) do + { + test_users: StringIO.new(<<~TEST_USERS), + first_name,middle_name,last_name,gender,birth_date,ssn,phone,email,password,mfa_code,id_types,loa,idme_uuid,logingov_uuid,services,notes + John,A,Doe,M,1932-02-05T00:00:00-08:00,111111111,800-827-1000,user1@email.com,xxx,xxx,"idme,logingov",3,xxx,xxx,"notes", + Jane,B,Smith,M,1933-04-05T00:00:00-08:00,222222222,800-827-1000,user2@email.com,xxx,xxx,"idme,logingov",3,xxx,xxx,"notes", + TEST_USERS + mvi_staging_users: StringIO.new(<<~MVI_STAGING_USERS) + first_name,middle_name,last_name,gender,birth_date,ssn,phone,email,password,icn,edipi,has_data_for, notes + John,A,Doe,M,1932-02-05T00:00:00-08:00,111111111,800-827-1000,user1@email.com,xxx,xxx,xxx,,notes + Jane,B,Smith,M,1933-04-05T00:00:00-08:00,222222222,800-827-1000,user2@email.com,xxx,xxx,xxx,, + MVI_STAGING_USERS + }.freeze + end + + let(:staging_data_build) do + Vye::StagingData::Build.new(target:) do |_paths| + streams + end + end + + it 'returns an array of rows' do + root = double('Pathname (Root)') + dump_file = double('Pathname (File)') + + expect(target).to receive(:/).and_return(root) + expect(root).to receive(:mkpath).with(no_args).and_return(true) + expect(root).to receive(:/).twice.with(any_args).and_return(dump_file) + expect(dump_file).to receive(:write).twice.and_return(true) + + staging_data_build.dump + end + end +end diff --git a/modules/vye/spec/models/vye/bdn_clone_spec.rb b/modules/vye/spec/models/vye/bdn_clone_spec.rb new file mode 100644 index 00000000000..67c51b88bbb --- /dev/null +++ b/modules/vye/spec/models/vye/bdn_clone_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Vye::BdnClone, type: :model do + describe 'create' do + let(:attributes) { FactoryBot.attributes_for(:vye_bdn_clone) } + + it 'creates a record' do + expect do + described_class.create!(attributes) + end.to change(described_class, :count).by(1) + end + end +end diff --git a/modules/vye/spec/models/vye/verification_spec.rb b/modules/vye/spec/models/vye/verification_spec.rb index 9a545b36c70..14af09e52ba 100644 --- a/modules/vye/spec/models/vye/verification_spec.rb +++ b/modules/vye/spec/models/vye/verification_spec.rb @@ -3,20 +3,22 @@ require 'rails_helper' RSpec.describe Vye::Verification, type: :model do - let(:user_info) { create(:vye_user_info) } - describe 'create' do - let(:attributes) { FactoryBot.attributes_for(:vye_verification, user_info:) } + let!(:user_profile) { FactoryBot.create(:vye_user_profile) } + let(:verification) { FactoryBot.build(:vye_verification, user_profile:) } it 'creates a record' do expect do - Vye::Verification.create!(attributes) + verification.save! end.to change(Vye::Verification, :count).by(1) end end describe 'show todays verifications' do - let!(:verification) { FactoryBot.create(:vye_verification, user_info:) } + let!(:user_profile) { FactoryBot.create(:vye_user_profile) } + let!(:user_info) { FactoryBot.create(:vye_user_info, user_profile:) } + let!(:award) { FactoryBot.create(:vye_award, user_info:) } + let!(:verification) { FactoryBot.create(:vye_verification, award:, user_profile:) } before do ssn = '123456789' diff --git a/modules/vye/spec/requests/vye/v1/verify/create_spec.rb b/modules/vye/spec/requests/vye/v1/verify/create_spec.rb index c98fcb71d97..9e33dad14b3 100644 --- a/modules/vye/spec/requests/vye/v1/verify/create_spec.rb +++ b/modules/vye/spec/requests/vye/v1/verify/create_spec.rb @@ -47,11 +47,11 @@ describe 'in VYE' do let!(:user_profile) { FactoryBot.create(:vye_user_profile, icn: current_user.icn) } let!(:user_info) { FactoryBot.create(:vye_user_info, user_profile:) } - let(:award) { FactoryBot.create(:vye_award, user_info:) } + let!(:award) { FactoryBot.create(:vye_award, user_info:) } it 'creates a new verification' do post('/vye/v1/verify', params: {}) - # puts JSON.pretty_generate(JSON.parse(response)) + expect(response).to have_http_status(:no_content) end end @@ -63,7 +63,7 @@ end let!(:user_profile) { FactoryBot.create(:vye_user_profile, icn: current_user.icn) } let!(:user_info) { FactoryBot.create(:vye_user_info, user_profile:) } - let(:award) { create(:vye_award, user_info:) } + let!(:award) { create(:vye_award, user_info:) } it 'creates a new verification' do post('/vye/v1/verify', params: ivr_params) diff --git a/modules/vye/spec/serializers/verification_serializer_spec.rb b/modules/vye/spec/serializers/verification_serializer_spec.rb new file mode 100644 index 00000000000..f614e1e1cb5 --- /dev/null +++ b/modules/vye/spec/serializers/verification_serializer_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Vye::VerificationSerializer, type: :serializer do + let(:resource) { build(:vye_verification) } # Assuming you have a factory for verification + let(:serializer) { described_class.new(resource) } + let(:serialization) { ActiveModelSerializers::Adapter.create(serializer, {}) } + + it 'includes the expected attributes' do + expect do + serialization.as_json + end.not_to raise_error + end +end