Skip to content

Commit

Permalink
98422: Port controller, model and monitor to module with mount for ne… (
Browse files Browse the repository at this point in the history
#20283)

* 98422: Port controller, model and monitor to module with mount for new engine and tests

* 98422: Fix rubocop

* 98422: Fix test

* 98422: Add test coverage

* 98422: Some tweaks to appease rubocop

* 98422: 100% test coverage for saved_claims

* 98422: Remove unnecessary require
  • Loading branch information
balexandr authored Jan 17, 2025
1 parent 486ace1 commit f2c698f
Show file tree
Hide file tree
Showing 8 changed files with 933 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@
mount AccreditedRepresentativePortal::Engine, at: '/accredited_representative_portal'
mount AskVAApi::Engine, at: '/ask_va_api'
mount Avs::Engine, at: '/avs'
mount Burials::Engine, at: '/burials'
mount CheckIn::Engine, at: '/check_in'
mount DebtsApi::Engine, at: '/debts_api'
mount DhpConnectedDevices::Engine, at: '/dhp_connected_devices'
Expand Down
103 changes: 103 additions & 0 deletions modules/burials/app/controllers/burials/v0/claims_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# frozen_string_literal: true

require 'pension_burial/tag_sentry'
require 'common/exceptions/validation_errors'

module Burials
module V0
class ClaimsController < ApplicationController
skip_before_action(:authenticate)
before_action :load_user, only: :create

service_tag 'burial-application'

def show
claim = claim_class.find_by!(guid: params[:id])
render json: SavedClaimSerializer.new(claim)
rescue ActiveRecord::RecordNotFound => e
monitor.track_show404(params[:id], current_user, e)
render(json: { error: e.to_s }, status: :not_found)
rescue => e
monitor.track_show_error(params[:id], current_user, e)
raise e
end

def create
PensionBurial::TagSentry.tag_sentry

claim = claim_class.new(form: filtered_params[:form])
monitor.track_create_attempt(claim, current_user)

in_progress_form = current_user ? InProgressForm.form_for_user(claim.form_id, current_user) : nil
claim.form_start_date = in_progress_form.created_at if in_progress_form

unless claim.save
Sentry.set_tags(team: 'benefits-memorial-1') # tag sentry logs with team name
monitor.track_create_validation_error(in_progress_form, claim, current_user)
log_validation_error_to_metadata(in_progress_form, claim)
raise Common::Exceptions::ValidationErrors, claim.errors
end

process_and_upload_to_lighthouse(in_progress_form, claim)

monitor.track_create_success(in_progress_form, claim, current_user)

clear_saved_form(claim.form_id)
render json: SavedClaimSerializer.new(claim)
rescue => e
monitor.track_create_error(in_progress_form, claim, current_user, e)
raise e
end

private

# an identifier that matches the parameter that the form will be set as in the JSON submission.
def short_name
'burial_claim'
end

# a subclass of SavedClaim, runs json-schema validations and performs any storage and attachment processing
def claim_class
Burials::SavedClaim
end

def process_and_upload_to_lighthouse(in_progress_form, claim)
claim.process_attachments!

Lighthouse::SubmitBenefitsIntakeClaim.perform_async(claim.id)
rescue => e
monitor.track_process_attachment_error(in_progress_form, claim, current_user)
raise e
end

# Filters out the parameters to form access.
def filtered_params
params.require(short_name.to_sym).permit(:form)
end

##
# include validation error on in_progress_form metadata.
# `noop` if in_progress_form is `blank?`
#
# @param in_progress_form [InProgressForm]
# @param claim [Pensions::SavedClaim]
#
def log_validation_error_to_metadata(in_progress_form, claim)
return if in_progress_form.blank?

metadata = in_progress_form.metadata
metadata['submission']['error_message'] = claim&.errors&.errors&.to_s
in_progress_form.update(metadata:)
end

##
# retreive a monitor for tracking
#
# @return [Burials::Monitor]
#
def monitor
@monitor ||= Burials::Monitor.new
end
end
end
end
80 changes: 80 additions & 0 deletions modules/burials/app/models/burials/saved_claim.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

require 'pension_burial/processing_office'

module Burials
class SavedClaim < ::SavedClaim
FORM = '21P-530EZ'

def process_attachments!
refs = attachment_keys.map { |key| Array(open_struct_form.send(key)) }.flatten
files = PersistentAttachment.where(guid: refs.map(&:confirmationCode))
files.find_each { |f| f.update(saved_claim_id: id) }
end

def regional_office
PensionBurial::ProcessingOffice.address_for(open_struct_form.claimantAddress.postalCode)
end

def attachment_keys
%i[transportationReceipts deathCertificate militarySeparationDocuments additionalEvidence].freeze
end

def email
parsed_form['claimantEmail']
end

def form_matches_schema
return unless form_is_string

JSON::Validator.fully_validate(VetsJsonSchema::SCHEMAS[form_id], parsed_form).each do |v|
errors.add(:form, v.to_s)
end
end

def process_pdf(pdf_path, timestamp = nil, form_id = nil)
processed_pdf = PDFUtilities::DatestampPdf.new(pdf_path).run(
text: 'Application Submitted on va.gov',
x: 400,
y: 675,
text_only: true, # passing as text only because we override how the date is stamped in this instance
timestamp:,
page_number: 6,
template: "lib/pdf_fill/forms/pdfs/#{form_id}.pdf",
multistamp: true
)
renamed_path = "tmp/pdfs/#{form_id}_#{id}_final.pdf"
File.rename(processed_pdf, renamed_path) # rename for vbms upload
renamed_path # return the renamed path
end

def business_line
'NCA'
end

##
# utility function to retrieve claimant first name from form
#
# @return [String] the claimant first name
#
def veteran_first_name
parsed_form.dig('veteranFullName', 'first')
end

def veteran_last_name
parsed_form.dig('veteranFullName', 'last')
end

def claimaint_first_name
parsed_form.dig('claimantFullName', 'first')
end

def benefits_claimed
claimed = []
claimed << 'Burial Allowance' if parsed_form['burialAllowance']
claimed << 'Plot Allowance' if parsed_form['plotAllowance']
claimed << 'Transportation' if parsed_form['transportation']
claimed
end
end
end
193 changes: 193 additions & 0 deletions modules/burials/lib/burials/monitor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# frozen_string_literal: true

require 'burials/notification_email'
require 'zero_silent_failures/monitor'

module Burials
##
# Monitor functions for Rails logging and StatsD
#
class Monitor < ::ZeroSilentFailures::Monitor
# statsd key for api
CLAIM_STATS_KEY = 'api.burial_claim'

# statsd key for sidekiq
SUBMISSION_STATS_KEY = 'app.burial.submit_benefits_intake_claim'

attr_reader :tags

def initialize
super('burial-application')

@tags = ['form_id:21P-530EZ']
end

##
# log GET 404 from controller
# @see BurialClaimsController
#
# @param confirmation_number [UUID] saved_claim guid
# @param current_user [User]
# @param e [ActiveRecord::RecordNotFound]
#
def track_show404(confirmation_number, current_user, e)
additional_context = {
confirmation_number:,
user_account_uuid: current_user&.user_account_uuid,
message: e&.message,
tags:
}
track_request('error', '21P-530EZ submission not found', CLAIM_STATS_KEY,
call_location: caller_locations.first, **additional_context)
end

##
# log GET 500 from controller
# @see BurialClaimsController
#
# @param confirmation_number [UUID] saved_claim guid
# @param current_user [User]
# @param e [Error]
#
def track_show_error(confirmation_number, current_user, e)
additional_context = {
confirmation_number:,
user_account_uuid: current_user&.user_account_uuid,
message: e&.message,
tags:
}
track_request('error', '21P-530EZ fetching submission failed', CLAIM_STATS_KEY,
call_location: caller_locations.first, **additional_context)
end

##
# log POST processing started
# @see BurialClaimsController
#
# @param claim [SavedClaim::Burial]
# @param current_user [User]
#
def track_create_attempt(claim, current_user)
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: current_user&.user_account_uuid,
tags:
}
track_request('info', '21P-530EZ submission to Sidekiq begun', "#{CLAIM_STATS_KEY}.attempt",
call_location: caller_locations.first, **additional_context)
end

##
# log POST claim save validation error
# @see BurialClaimsController
#
# @param in_progress_form [InProgressForm]
# @param claim [SavedClaim::Burial]
# @param current_user [User]
# @param e [Error]
#
def track_create_validation_error(in_progress_form, claim, current_user)
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: current_user&.user_account_uuid,
in_progress_form_id: in_progress_form&.id,
errors: claim&.errors&.errors,
tags:
}
track_request('error', '21P-530EZ submission validation error', "#{CLAIM_STATS_KEY}.validation_error",
call_location: caller_locations.first, **additional_context)
end

##
# log POST processing failure
# @see BurialClaimsController
#
# @param in_progress_form [InProgressForm]
# @param claim [SavedClaim::Burial]
# @param current_user [User]
# @param e [Error]
#
def track_create_error(in_progress_form, claim, current_user, e = nil)
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: current_user&.user_account_uuid,
in_progress_form_id: in_progress_form&.id,
errors: claim&.errors&.errors,
message: e&.message,
tags:
}
track_request('error', '21P-530EZ submission to Sidekiq failed', "#{CLAIM_STATS_KEY}.failure",
call_location: caller_locations.first, **additional_context)
end

##
# log POST processing success
# @see BurialClaimsController
#
# @param in_progress_form [InProgressForm]
# @param claim [SavedClaim::Burial]
# @param current_user [User]
#
def track_create_success(in_progress_form, claim, current_user)
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: current_user&.user_account_uuid,
in_progress_form_id: in_progress_form&.id,
errors: claim&.errors&.errors,
tags:
}
track_request('info', '21P-530EZ submission to Sidekiq success', "#{CLAIM_STATS_KEY}.success",
call_location: caller_locations.first, **additional_context)
end

##
# log process_attachments! error
# @see BurialClaimsController
#
# @param in_progress_form [InProgressForm]
# @param claim [SavedClaim::Burial]
# @param current_user [User]
#
def track_process_attachment_error(in_progress_form, claim, current_user)
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: current_user&.user_account_uuid,
in_progress_form_id: in_progress_form&.id,
errors: claim&.errors&.errors,
tags:
}
track_request('error', '21P-530EZ process attachment error', "#{CLAIM_STATS_KEY}.process_attachment_error",
call_location: caller_locations.first, **additional_context)
end

##
# log Sidkiq job exhaustion, complete failure after all retries
# @see Lighthouse::SubmitBenefitsIntakeClaim
#
# @param msg [Hash] sidekiq exhaustion response
# @param claim [SavedClaim::Burial]
#
def track_submission_exhaustion(msg, claim = nil)
user_account_uuid = msg['args'].length <= 1 ? nil : msg['args'][1]
additional_context = {
confirmation_number: claim&.confirmation_number,
user_account_uuid: user_account_uuid,
form_id: claim&.form_id,
claim_id: msg['args'].first,
message: msg,
tags:
}
call_location = caller_locations.first

if claim
Burials::NotificationEmail.new(claim.id).deliver(:error)
log_silent_failure_avoided(additional_context, user_account_uuid, call_location:)
else
log_silent_failure(additional_context, user_account_uuid, call_location:)
end

track_request('error', 'Lighthouse::SubmitBenefitsIntakeClaim Burial 21P-530EZ submission to LH exhausted!',
"#{SUBMISSION_STATS_KEY}.exhausted", call_location:, **additional_context)
end
end
end
Loading

0 comments on commit f2c698f

Please sign in to comment.