Skip to content

Commit

Permalink
[SIMPLE_FORMS] fix: update attachment handling to account for path na…
Browse files Browse the repository at this point in the history
…mes instead of objects (#18674)

* update attachment handling to account for path names instead of objects

* add test coverage and logic changes

* fix dumb mistake

* add spec for metadata
  • Loading branch information
pennja authored Oct 1, 2024
1 parent 596c8f8 commit 8a4b12c
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ module SimpleFormsApi
module S3
class SubmissionArchiveBuilder < Utils
def initialize(**options) # rubocop:disable Lint/MissingSuper
defaults = default_options.merge(options)
hydrate_submission_data(defaults[:benefits_intake_uuid]) unless valid_submission_data?(defaults)
assign_instance_variables(defaults)
assign_defaults(options)
hydrate_submission_data unless submission_already_hydrated?
rescue => e
handle_error('SubmissionArchiveBuilder initialization failed', e)
end
Expand All @@ -27,55 +26,74 @@ def run

private

attr_reader :attachments, :benefits_intake_uuid, :file_path, :include_manifest, :metadata, :submission

def default_options
{
attachments: nil, # The confirmation codes of any attachments which were originally submitted
benefits_intake_uuid: nil, # The UUID returned from the Benefits Intake API upon original submission
file_path: nil, # The local path where the submission PDF is stored
include_manifest: true, # Include a CSV file containing manifest data
metadata: nil, # Data appended to the original submission headers
submission: nil # The FormSubmission object representing the original data payload submitted
}
attr_reader :attachments, :benefits_intake_uuid, :file_path, :include_manifest, :include_metadata, :metadata,
:submission

def assign_defaults(options)
# The file paths of hydrated attachments which were originally submitted
@attachments = options[:attachments] || nil
# The UUID returned from the Benefits Intake API upon original submission
@benefits_intake_uuid = options[:benefits_intake_uuid] || nil
# The local path where the submission PDF is stored
@file_path = options[:file_path] || nil
# Include a CSV file containing manifest data
@include_manifest = options[:include_manifest] || true
# Include a JSON file containing metadata of original submission
@include_metadata = options[:include_metadata] || true
# Data appended to the original submission headers
@metadata = options[:metadata] || nil
# The FormSubmission object representing the original data payload submitted
@submission = options[:submission] || nil
end

def valid_submission_data?(data)
data[:submission] && data[:file_path] && data[:attachments] && data[:metadata]
def submission_already_hydrated?
submission && file_path && attachments && metadata
end

def hydrate_submission_data
raise 'No benefits_intake_uuid was provided' unless benefits_intake_uuid

built_submission = SubmissionBuilder.new(benefits_intake_uuid:)
@file_path = built_submission.file_path
@submission = built_submission.submission
@benefits_intake_uuid = @submission&.benefits_intake_uuid
@attachments = built_submission.attachments || []
@metadata = built_submission.metadata
end

def process_submission_files
write_pdf
write_attachments if attachments&.any?
write_manifest if include_manifest
write_metadata
write_metadata if include_metadata
rescue => e
handle_error('Error during submission file processing', e)
handle_error('Error during submission files processing', e)
end

def write_pdf
write_tempfile("#{submission_file_path}.pdf", File.read(file_path))
rescue => e
handle_error('Error during submission pdf processing', e)
end

def write_metadata
write_tempfile("metadata_#{submission_file_path}.json", metadata.to_json)
rescue => e
handle_error('Error during metadata processing', e)
end

def write_attachments
log_info("Processing #{attachments.count} attachments")
attachments.each_with_index { |guid, i| process_attachment(i + 1, guid) }
attachments.each_with_index { |file_path, i| process_attachment(i + 1, file_path) }
rescue => e
handle_error('Error during attachments processing', e)
end

def process_attachment(attachment_number, guid)
log_info("Processing attachment ##{attachment_number}: #{guid}")
attachment = PersistentAttachment.find_by(guid:)
raise "Attachment not found: #{guid}" unless attachment

write_tempfile("attachment_#{attachment_number}.pdf", attachment.to_pdf)
def process_attachment(attachment_number, file_path)
log_info("Processing attachment ##{attachment_number}: #{file_path}")
write_tempfile("attachment_#{attachment_number}__#{submission_file_path}.pdf", File.read(file_path))
rescue => e
handle_error("Failed processing attachment #{attachment_number} (#{guid})", e)
handle_error("Failed processing attachment #{attachment_number} (#{file_path})", e)
end

def write_manifest
Expand Down Expand Up @@ -103,17 +121,10 @@ def write_tempfile(file_name, payload)
handle_error("Failed writing file #{file_name} for submission: #{benefits_intake_uuid}", e)
end

def hydrate_submission_data(benefits_intake_uuid)
built_submission = SubmissionBuilder.new(benefits_intake_uuid:)
@file_path = built_submission.file_path
@submission = built_submission.submission
@benefits_intake_uuid = @submission&.benefits_intake_uuid
@attachments = built_submission.attachments || []
@metadata = built_submission.metadata
end

def form_data_hash
@form_data_hash ||= JSON.parse(submission.form_data)
rescue => e
handle_error('Error parsing submission form data', e)
end

def submission_file_path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,50 @@
require 'rails_helper'
require SimpleFormsApi::Engine.root.join('spec', 'spec_helper.rb')

RSpec.describe SimpleFormsApi::S3::SubmissionArchiveBuilder, skip: 'These are flaky, need to be fixed.' do
let(:form_id) { '21-10210' }
let(:form_data) { File.read("modules/simple_forms_api/spec/fixtures/form_json/vba_#{form_id.gsub('-', '_')}.json") }
let(:submission) { create(:form_submission, :pending, form_type: form_id, form_data:) }
RSpec.describe SimpleFormsApi::S3::SubmissionArchiveBuilder 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') }
let(:attachments) { Array.new(5) { fixture_file_upload('doctors-note.pdf', 'application/pdf').path } }
let(:submission) { create(:form_submission, :pending, form_type:, form_data:) }
let(:benefits_intake_uuid) { submission.benefits_intake_uuid }
let(:metadata) do
{
veteranFirstName: 'John',
veteranLastName: 'Veteran',
fileNumber: '321540987',
zipCode: '12345',
source: 'VA Platform Digital Forms',
docType: '20-10207',
businessLine: 'CMP'
}
end
let(:submission_file_path) do
[Time.zone.today.strftime('%-m.%d.%y'), 'form', form_type, 'vagov', benefits_intake_uuid].join('_')
end
let(:submission_builder) { OpenStruct.new(submission:, file_path:, attachments:, metadata:) }
let(:archive_builder_instance) { described_class.new(benefits_intake_uuid:) }

before do
allow(FormSubmission).to receive(:find_by).and_return(submission)
allow(SecureRandom).to receive(:hex).and_return('random-letters-n-numbers')
allow_any_instance_of(described_class).to receive(:assign_instance_variables).and_call_original
allow(SimpleFormsApi::S3::SubmissionBuilder).to receive(:new).and_return(submission_builder)
allow(File).to receive(:write).and_return(true)
allow(CSV).to receive(:open).and_return(true)
allow(FileUtils).to receive(:mkdir_p).and_return(true)
end

describe '#initialize' do
subject(:new) { archive_builder_instance }

let(:defaults) do
{
attachments: nil,
benefits_intake_uuid:,
file_path: nil,
include_manifest: true,
metadata: nil,
submission: nil
}
end

it { is_expected.to have_received(:assign_instance_variables).with(defaults) } # rubocop:disable RSpec/SubjectStub

context 'when initialized with a valid benefits_intake_uuid' do
let(:archive_builder_instance) { described_class.new(benefits_intake_uuid:) }

it 'successfully completes initialization' do
expect { new }.not_to raise_exception
end
end

context 'when initialized with valid file_path, attachments, and metadata' do
let(:file_path) { 'modules/simple_forms_api/spec/fixtures/pdfs/vba_20_10207-completed.pdf' }
let(:attachments) { [] }
let(:metadata) do
{
veteranFirstName: 'John',
veteranLastName: 'Veteran',
fileNumber: '222773333',
zipCode: '12345',
source: 'VA Platform Digital Forms',
docType: '21-10210',
businessLine: 'CMP'
}
end
context 'when initialized with valid hydrated submission data' do
let(:archive_builder_instance) { described_class.new(submission:, file_path:, attachments:, metadata:) }

it 'successfully completes initialization' do
Expand All @@ -77,7 +70,35 @@

context 'when properly initialized' do
it 'completes successfully' do
expect(run).to eq(temp_file_path)
expect(run).to eq([temp_file_path, submission, submission_file_path])
end

it 'writes the submission pdf file' do
run
expect(File).to have_received(:write).with(
"#{temp_file_path}#{submission_file_path}.pdf", a_string_starting_with('%PDF')
)
end

it 'writes the attachment files' do
run
attachments.each_with_index do |_, i|
expect(File).to have_received(:write).with(
"#{temp_file_path}attachment_#{i + 1}__#{submission_file_path}.pdf", a_string_starting_with('%PDF')
)
end
end

it 'writes the manifest file' do
run
expect(CSV).to have_received(:open).with("#{temp_file_path}manifest_#{submission_file_path}.csv", 'wb')
end

it 'writes the metadata json file' do
run
expect(File).to have_received(:write).with(
"#{temp_file_path}metadata_#{submission_file_path}.json", metadata.to_json
)
end
end
end
Expand Down

0 comments on commit 8a4b12c

Please sign in to comment.