From 21f7b56d1840c08d39d69e1692d50b7b863cc2bc Mon Sep 17 00:00:00 2001 From: Jacob Penner <161746194+pennja@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:23:51 -0400 Subject: [PATCH] add submission remediation data (#18769) --- .../submission_remediation_data.rb | 113 ++++++++++++++ .../submission_remediation_data_spec.rb | 144 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 modules/simple_forms_api/app/services/simple_forms_api/form_remediation/submission_remediation_data.rb create mode 100644 modules/simple_forms_api/spec/services/form_remediation/submission_remediation_data_spec.rb diff --git a/modules/simple_forms_api/app/services/simple_forms_api/form_remediation/submission_remediation_data.rb b/modules/simple_forms_api/app/services/simple_forms_api/form_remediation/submission_remediation_data.rb new file mode 100644 index 0000000000..e6ff02d180 --- /dev/null +++ b/modules/simple_forms_api/app/services/simple_forms_api/form_remediation/submission_remediation_data.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'simple_forms_api/form_remediation/configuration/base' + +module SimpleFormsApi + module FormRemediation + class SubmissionRemediationData + attr_reader :file_path, :submission, :attachments, :metadata + + def initialize(id:, config: Configuration::Base.new) + @config = config + + validate_input(id) + fetch_submission(id) + + @attachments = [] + @metadata = {} + rescue => e + config.handle_error("#{self.class.name} initialization failed", e) + end + + def hydrate! + form_number = fetch_submission_form_number + form = build_form(form_number) + filler = PdfFiller.new(form_number:, form:) + + handle_submission_data(filler, form, form_number) + self + rescue => e + config.handle_error('Error hydrating submission', e) + end + + private + + attr_reader :config + + def validate_input(id) + raise ArgumentError, "No #{config.id_type} was provided" unless id + end + + def fetch_submission(id) + @submission = config.submission_type.find_by(config.id_type => id) + validate_submission + end + + def validate_submission + raise 'Submission was not found or invalid' unless submission&.send(config.id_type) + raise "#{self.class} cannot be built: Only VFF forms are supported" unless vff_form? + end + + def fetch_submission_form_number + vff_forms_map.fetch(submission.form_type) + end + + def build_form(form_number) + form_class_name = "SimpleFormsApi::#{form_number.titleize.delete(' ')}" + form_class = form_class_name.constantize + form_class.new(form_data_hash) + rescue NameError => e + config.handle_error("Form class not found for #{form_class_name}", e) + end + + def handle_submission_data(filler, form, form_number) + @file_path = generate_pdf_file(filler) + @metadata = validate_metadata(form) + @attachments = process_attachments(form, form_number) + end + + def generate_pdf_file(filler) + filler.generate(timestamp: submission.created_at) + rescue => e + config.handle_error('Error generating filled submission PDF', e) + end + + def validate_metadata(form) + SimpleFormsApiSubmission::MetadataValidator.validate( + form.metadata, + zip_code_is_us_based: form.zip_code_is_us_based + ) + rescue => e + config.handle_error('Metadata validation failed', e) + end + + def process_attachments(form, form_number) + case form_number + when 'vba_40_0247', 'vba_40_10007' + form.handle_attachments(file_path) + [] + when 'vba_20_10207' + form.get_attachments + else + [] + end + rescue => e + config.handle_error("Attachment handling failed for #{form_number}", e) + end + + def form_data_hash + @form_data_hash ||= JSON.parse(submission.form_data) + rescue JSON::ParserError => e + config.handle_error('Error parsing form data', e) + end + + def vff_forms_map + SimpleFormsApi::V1::UploadsController::FORM_NUMBER_MAP + end + + def vff_form? + vff_forms_map.key?(submission.form_type) + end + end + end +end diff --git a/modules/simple_forms_api/spec/services/form_remediation/submission_remediation_data_spec.rb b/modules/simple_forms_api/spec/services/form_remediation/submission_remediation_data_spec.rb new file mode 100644 index 0000000000..b02ef9c08f --- /dev/null +++ b/modules/simple_forms_api/spec/services/form_remediation/submission_remediation_data_spec.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +require 'rails_helper' +require SimpleFormsApi::Engine.root.join('spec', 'spec_helper.rb') + +RSpec.describe SimpleFormsApi::FormRemediation::SubmissionRemediationData do + let(:form_type) { '20-10207' } + let(:fixtures_path) { 'modules/simple_forms_api/spec/fixtures' } + let(:form_data) { Rails.root.join(fixtures_path, 'form_json', 'vba_20_10207_with_supporting_documents.json').read } + let(:file_path) { Rails.root.join(fixtures_path, 'pdfs', 'vba_20_10207-completed.pdf').to_s } + let(:submission) { create(:form_submission, :pending, form_type:, form_data:) } + let(:benefits_intake_uuid) { submission.benefits_intake_uuid } + let(:submission_instance) { described_class.new(id: benefits_intake_uuid) } + let(:filler) { instance_double(SimpleFormsApi::PdfFiller) } + let(:attachments) { Array.new(5) { fixture_file_upload('doctors-note.pdf', 'application/pdf').path } } + let(:metadata) do + { + 'veteranFirstName' => 'John', + 'veteranLastName' => 'Veteran', + 'fileNumber' => '321540987', + 'zipCode' => '12345', + 'source' => 'VA Platform Digital Forms', + 'docType' => '20-10207', + 'businessLine' => 'CMP' + } + end + let(:vba_20_10207_instance) { instance_double(SimpleFormsApi::VBA2010207, metadata:) } + + before do + allow(FormSubmission).to receive(:find_by).with(benefits_intake_uuid:).and_return(submission) + allow(SecureRandom).to receive(:hex).and_return('random-letters-n-numbers') + allow(SimpleFormsApi::PdfFiller).to receive(:new).and_return(filler) + allow(filler).to receive(:generate).with(timestamp: submission.created_at).and_return(file_path) + allow(SimpleFormsApi::VBA2010207).to receive(:new).and_return(vba_20_10207_instance) + allow(vba_20_10207_instance).to receive_messages(get_attachments: attachments, zip_code_is_us_based: true) + end + + describe '#initialize' do + subject(:new) { submission_instance } + + context 'when benefits_intake_uuid is valid' do + before { new } + + it 'fetches the form submission data' do + expect(FormSubmission).to have_received(:find_by).with(benefits_intake_uuid:) + end + + it 'sets the form submission data' do + expect(new.submission).to be(submission) + end + + it 'sets attachments to empty array' do + expect(new.attachments).to eq([]) + end + end + + context 'when benefits_intake_uuid is nil' do + let(:benefits_intake_uuid) { nil } + + it 'throws an error' do + expect { new }.to raise_exception('No benefits_intake_uuid was provided') + end + end + + context 'when benefits_intake_uuid is missing' do + let(:submission_instance) { described_class.new(stuff: 'thangs') } + + it 'throws an error' do + expect { new }.to raise_exception(ArgumentError, 'missing keyword: :id') + end + end + + context 'when the form submission is not found' do + before { allow(FormSubmission).to receive(:find_by).and_return(nil) } + + it 'throws an error' do + expect { new }.to raise_exception('Submission was not found or invalid') + end + end + + context 'when benefits_intake_uuid is invalid' do + let(:benefits_intake_uuid) { 'complete-nonsense' } + + before { allow(FormSubmission).to receive(:find_by).and_call_original } + + it 'raises an error' do + expect { new }.to raise_exception('Submission was not found or invalid') + end + end + + context 'when the associated form submission is not VFF in nature' do + let(:submission) { create(:form_submission, :pending, form_type: '12-34567', form_data:) } + + it 'raises an error' do + expect { new }.to raise_exception( + 'SimpleFormsApi::FormRemediation::SubmissionRemediationData cannot be built: Only VFF forms are supported' + ) + end + end + end + + describe '#hydrate!' do + subject(:hydrated) { submission_instance.hydrate! } + + context 'when benefits_intake_uuid is valid' do + it 'generates a valid file_path' do + expect(hydrated.file_path).to include(file_path) + end + + it 'generates a valid submission' do + expect(hydrated.submission).to be(submission) + end + + it 'defaults to an empty array for attachments' do + expect(hydrated.attachments).to eq(attachments) + end + + it 'generates valid metadata' do + expect(hydrated.metadata).to eq(metadata) + end + + context 'when the form is 20-10207' do + it 'generates a valid array of attachments' do + expect(hydrated.attachments).to eq(attachments) + end + end + + context 'when the form is not 20-10207' do + let(:form_type) { '21-10210' } + let(:form_data) { Rails.root.join(fixtures_path, 'form_json', 'vba_21_10210.json').read } + let(:vba_21_10210_instance) { instance_double(SimpleFormsApi::VBA2110210, metadata:) } + + before do + allow(SimpleFormsApi::VBA2110210).to receive(:new).and_return(vba_20_10207_instance) + allow(vba_21_10210_instance).to receive_messages(zip_code_is_us_based: true) + end + + it 'defaults to an empty array for attachments' do + expect(hydrated.attachments).to eq([]) + end + end + end + end +end