diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 67a9686cabf..ddde7a275f1 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -842,6 +842,7 @@ lib/benefits_intake_service @department-of-veterans-affairs/benefits-dependents-
lib/bgs @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
lib/bid @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
lib/bip_claims @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
+lib/burials @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
lib/carma @department-of-veterans-affairs/vfs-10-10 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
lib/caseflow @department-of-veterans-affairs/lighthouse-banana-peels @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
lib/central_mail @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
@@ -1384,6 +1385,7 @@ spec/lib/bgs @department-of-veterans-affairs/benefits-dependents-management @dep
spec/lib/bid @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/lib/bip_claims @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/lib/breakers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
+spec/lib/burials @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/lib/carma @department-of-veterans-affairs/vfs-10-10 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/lib/caseflow @department-of-veterans-affairs/lighthouse-banana-peels @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/lib/central_mail @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
diff --git a/app/controllers/v0/burial_claims_controller.rb b/app/controllers/v0/burial_claims_controller.rb
index 5e3b70df986..132bddd4b01 100644
--- a/app/controllers/v0/burial_claims_controller.rb
+++ b/app/controllers/v0/burial_claims_controller.rb
@@ -1,52 +1,61 @@
# frozen_string_literal: true
require 'pension_burial/tag_sentry'
+require 'burials/monitor'
module V0
class BurialClaimsController < ClaimsBaseController
service_tag 'burial-application'
def show
- submission_attempt = determine_submission_attempt
+ claim = claim_class.find_by!(guid: params[:id])
+ form_submission = claim&.form_submissions&.last
+ submission_attempt = form_submission&.form_submission_attempts&.last
if submission_attempt
state = submission_attempt.aasm_state == 'failure' ? 'failure' : 'success'
render(json: { data: { attributes: { state: } } })
elsif central_mail_submission
render json: CentralMailSubmissionSerializer.new(central_mail_submission)
- else
- Rails.logger.error("ActiveRecord::RecordNotFound: Claim submission not found for claim_id: #{params[:id]}")
- render(json: { data: { attributes: { state: 'not found' } } }, status: :not_found)
end
+ rescue ActiveRecord::RecordNotFound => e
+ monitor.track_show404(params[:id], current_user, e)
+ render(json: { data: { attributes: { state: 'not found' } } }, status: :not_found)
rescue => e
- Rails.logger.error(e.to_s)
+ monitor.track_show_error(params[:id], current_user, e)
render(json: { data: { attributes: { state: 'error processing request' } } }, status: :unprocessable_entity)
end
def create
PensionBurial::TagSentry.tag_sentry
- claim = if Flipper.enabled?(:va_burial_v2)
- # cannot parse a nil form, to pass unit tests do a check for form presence
- form = filtered_params[:form]
- claim_class.new(form:, formV2: form.present? ? JSON.parse(form)['formV2'] : nil)
- else
- claim_class.new(form: filtered_params[:form])
- end
-
- unless claim.save
- StatsD.increment("#{stats_key}.failure")
- Sentry.set_tags(team: 'benefits-memorial-1') # tag sentry logs with team name
- Rails.logger.error('Burial claim was not saved', { error_messages: claim.errors,
- user_uuid: current_user&.uuid,
- in_progress_form_id: in_progress_form&.id })
- raise Common::Exceptions::ValidationErrors, claim
- end
+ claim = create_claim
+ monitor.track_create_attempt(claim, current_user)
+
+ track_claim_save_failure(claim) unless claim.save
+
# this method also calls claim.process_attachments!
claim.submit_to_structured_data_services!
Rails.logger.info "ClaimID=#{claim.confirmation_number} Form=#{claim.form_id}"
+
+ 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
+ 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
+
+ def create_claim
+ if Flipper.enabled?(:va_burial_v2)
+ form = filtered_params[:form]
+ claim_class.new(form:, formV2: form.present? ? JSON.parse(form)['formV2'] : nil)
+ else
+ claim_class.new(form: filtered_params[:form])
+ end
end
def short_name
@@ -59,12 +68,6 @@ def claim_class
private
- def determine_submission_attempt
- claim = claim_class.find_by(guid: params[:id])
- form_submission = claim&.form_submissions&.last
- form_submission&.form_submission_attempts&.last
- end
-
def central_mail_submission
CentralMailSubmission.joins(:central_mail_claim).find_by(saved_claims: { guid: params[:id] })
end
@@ -72,5 +75,39 @@ def central_mail_submission
def in_progress_form
current_user ? InProgressForm.form_for_user(claim.form_id, current_user) : nil
end
+
+ def track_claim_save_failure(claim)
+ StatsD.increment("#{stats_key}.failure")
+ Sentry.set_tags(team: 'benefits-memorial-1') # tag sentry logs with team name
+ Rails.logger.error('Burial claim was not saved', { error_messages: claim.errors,
+ user_uuid: current_user&.uuid,
+ in_progress_form_id: in_progress_form&.id })
+ log_validation_error_to_metadata(in_progress_form, claim)
+ raise Common::Exceptions::ValidationErrors, claim
+ 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
diff --git a/lib/burials/monitor.rb b/lib/burials/monitor.rb
new file mode 100644
index 00000000000..66d1436b735
--- /dev/null
+++ b/lib/burials/monitor.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+module Burials
+ ##
+ # Monitor functions for Rails logging and StatsD
+ #
+ class Monitor
+ CLAIM_STATS_KEY = 'api.burial_claim'
+
+ ##
+ # 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)
+ Rails.logger.error('21P-530EZ submission not found',
+ { confirmation_number:, user_uuid: current_user&.uuid, message: e&.message })
+ 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)
+ Rails.logger.error('21P-530EZ fetching submission failed',
+ { confirmation_number:, user_uuid: current_user&.uuid, message: e&.message })
+ end
+
+ ##
+ # log POST processing started
+ # @see BurialClaimsController
+ #
+ # @param claim [Pension::SavedClaim]
+ # @param current_user [User]
+ #
+ def track_create_attempt(claim, current_user)
+ StatsD.increment("#{CLAIM_STATS_KEY}.attempt")
+ Rails.logger.info('21P-530EZ submission to Sidekiq begun',
+ { confirmation_number: claim&.confirmation_number, user_uuid: current_user&.uuid })
+ 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)
+ StatsD.increment("#{CLAIM_STATS_KEY}.failure")
+ Rails.logger.error('21P-530EZ submission to Sidekiq failed',
+ { confirmation_number: claim&.confirmation_number, user_uuid: current_user&.uuid,
+ in_progress_form_id: in_progress_form&.id, errors: claim&.errors&.errors,
+ message: e&.message })
+ 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)
+ StatsD.increment("#{CLAIM_STATS_KEY}.success")
+ if claim.form_start_date
+ claim_duration = claim.created_at - claim.form_start_date
+ tags = ["form_id:#{claim.form_id}"]
+ StatsD.measure('saved_claim.time-to-file', claim_duration, tags:)
+ end
+ context = {
+ confirmation_number: claim&.confirmation_number,
+ user_uuid: current_user&.uuid,
+ in_progress_form_id: in_progress_form&.id
+ }
+ Rails.logger.info('21P-530EZ submission to Sidekiq success', context)
+ end
+ end
+end
diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb
index ed982050e53..b29ea73d914 100644
--- a/modules/claims_api/lib/bgs_service/local_bgs.rb
+++ b/modules/claims_api/lib/bgs_service/local_bgs.rb
@@ -437,6 +437,10 @@ def jrn
private
+ def builder_to_xml(builder)
+ builder.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
+ end
+
def transform_keys(hash_or_array)
transformer = lambda do |object|
case object
diff --git a/modules/claims_api/lib/bgs_service/person_web_service.rb b/modules/claims_api/lib/bgs_service/person_web_service.rb
index 1fde82af939..b108d4c70c9 100644
--- a/modules/claims_api/lib/bgs_service/person_web_service.rb
+++ b/modules/claims_api/lib/bgs_service/person_web_service.rb
@@ -7,15 +7,33 @@ def bean_name
end
def find_dependents_by_ptcpnt_id(id)
- body = Nokogiri::XML::DocumentFragment.parse <<~EOXML
-
{- "environment": "dev",
- "buildNumber": "10",
- "os": "android"
}
{- "data": {
- "id": "1.0",
- "type": "discovery",
- "attributes": {
- "endpoints": {
- "appointments": {
- "url": "/v0/appointments",
- "method": "GET"
}, - "uploadClaimDocuments": {
- "url": "/claim/:id/documents",
- "method": "POST"
}, - "createUserPhone": {
- "url": "/user/phones",
- "method": "POST"
}, - "updateUserPhone": {
- "url": "/user/phones",
- "method": "PUT"
}
}, - "webviews": {
}, - "appAccess": true,
- "displayMessage": "Please update the app to continue"
}
}
}
{- "environment": "dev",
- "buildNumber": "10",
- "os": "android"
}
{- "data": {
- "id": "1.0",
- "type": "discovery",
- "attributes": {
- "endpoints": {
- "appointments": {
- "url": "/v0/appointments",
- "method": "GET"
}, - "uploadClaimDocuments": {
- "url": "/claim/:id/documents",
- "method": "POST"
}, - "createUserPhone": {
- "url": "/user/phones",
- "method": "POST"
}, - "updateUserPhone": {
- "url": "/user/phones",
- "method": "PUT"
}
}, - "webviews": {
}, - "appAccess": true,
- "displayMessage": "Please update the app to continue"
}
}
}
Returns info on all user's claims and appeals for mobile overview page. Should be identical to the following docs https://developer.va.gov/explore/api/appeals-status/docs?version=current
id required | string Appeal Id |
X-Key-Inflection | string Default: snake Enum: "camel" "snake" Allows the API to return camelCase keys rather than snake_case |
{- "data": [
- {
- "type": "immunization",
- "id": "I2-3JYDMXC6RXTU4H25KRVXATSEJQ000000",
- "attributes": {
- "cvxCode": 140,
- "date": "2009-03-19T12:24:55Z",
- "doseNumber": "Booster",
- "doseSeries": 1,
- "groupName": "FLU",
- "manufacturer": "nil",
- "note": "Dose #45 of 101 of Influenza seasonal injectable preservative free vaccine administered.",
- "shortDescription": "Influenza seasonal injectable preservative free",
- "reaction": "Swelling"
}, - "relationships": {
- "location": {
- "data": {
- "id": "I2-4KG3N5YUSPTWD3DAFMLMRL5V5U000000",
- "type": "location"
}, - "links": {
- "related": "staging-api.va.gov/mobile/v0/health/locations/I2-2FPCKUIXVR7RJLLG34XVWGZERM000000"
}
}
}
}
], - "meta": {
- "pagination": {
- "currentPage": 1,
- "perPage": 10,
- "totalPages": 2,
- "totalEntries": 15
}
}
}