Skip to content

Commit

Permalink
Merge pull request #18625 from department-of-veterans-affairs/feature…
Browse files Browse the repository at this point in the history
…/APPEALS-19983

Tracking PR for APPEALS-19983
  • Loading branch information
ThorntonMatthew authored Jul 13, 2023
2 parents b9462c0 + 9180022 commit f0e85e8
Show file tree
Hide file tree
Showing 94 changed files with 5,191 additions and 203 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ Rails/LexicallyScopedActionFilter:
Rails/SkipsModelValidations:
Enabled: false

Rails/ApplicationController:
Exclude:
- app/controllers/idt/api/v1/base_controller.rb

Rails/FilePath:
Enabled: false

Expand Down
18 changes: 15 additions & 3 deletions Makefile.example
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,21 @@ db: ## Connect to your dev postgres (caseflow) db

audit: ## Create caseflow_audit schema, tables, and triggers in postgres
bundle exec rails r db/scripts/audit/create_caseflow_audit_schema.rb
bundle exec rails r db/scripts/audit/create_appeal_states_audit.rb
bundle exec rails r db/scripts/audit/add_row_to_appeal_states_audit_table_function.rb
bundle exec rails r db/scripts/audit/create_appeal_states_audit_trigger.rb
bundle exec rails r db/scripts/audit/tables/create_appeal_states_audit.rb
bundle exec rails r db/scripts/audit/tables/create_vbms_communication_packages_audit.rb
bundle exec rails r db/scripts/audit/tables/create_vbms_distributions_audit.rb
bundle exec rails r db/scripts/audit/tables/create_vbms_distribution_destinations_audit.rb
bundle exec rails r db/scripts/audit/tables/create_vbms_uploaded_documents_audit.rb
bundle exec rails r db/scripts/audit/functions/add_row_to_appeal_states_audit_table_function.rb
bundle exec rails r db/scripts/audit/functions/add_row_to_vbms_communication_packages_audit_table_function.rb
bundle exec rails r db/scripts/audit/functions/add_row_to_vbms_distributions_audit_table_function.rb
bundle exec rails r db/scripts/audit/functions/add_row_to_vbms_distribution_destinations_audit_table_function.rb
bundle exec rails r db/scripts/audit/functions/add_row_to_vbms_uploaded_documents_audit_table_function.rb
bundle exec rails r db/scripts/audit/triggers/create_appeal_states_audit_trigger.rb
bundle exec rails r db/scripts/audit/triggers/create_vbms_communication_packages_audit_trigger.rb
bundle exec rails r db/scripts/audit/triggers/create_vbms_distributions_audit_trigger.rb
bundle exec rails r db/scripts/audit/triggers/create_vbms_distribution_destinations_audit_trigger.rb
bundle exec rails r db/scripts/audit/triggers/create_vbms_uploaded_documents_audit_trigger.rb

audit-remove: ## Remove caseflow_audit schema, tables and triggers in postgres
bundle exec rails r db/scripts/audit/remove_caseflow_audit_schema.rb
Expand Down
1,281 changes: 1,281 additions & 0 deletions app/controllers/api/docs/pacman/idt-pacman-spec.yml

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions app/controllers/concerns/mail_package_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

# shared code for building mail packages to submit to Package Manager external service

module MailPackageConcern
extend ActiveSupport::Concern

private

def recipient_info
params[:recipient_info]
end

def copies
# Default value of 1 for copies
return 1 if params[:copies].blank?

params[:copies]
end

def mail_package
return nil if recipient_info.blank?

{ distributions: json_mail_requests, copies: copies, created_by_id: user.id }
end

# Purpose: - Creates and validates a MailRequest object for each recipient
# - Calls #call method on each MailRequest to save corresponding VbmsDistirbution and
# VbmsDistributionDestination to the db
# - Stores the distribution IDs (to be returned to the IDT user as an immediate means of tracking
# each distribution)
#
def build_mail_package
return if recipient_info.blank?

throw_error_if_copies_out_of_range
mail_requests.map do |request|
request.call
distribution_ids << request.vbms_distribution_id
end
end

def mail_requests
@mail_requests ||= create_mail_requests_and_track_errors
end

def json_mail_requests
mail_requests.map(&:to_json)
end

def create_mail_requests_and_track_errors
requests = recipient_info.map.with_index do |recipient, idx|
MailRequest.new(recipient).tap do |request|
if request.invalid?
recipient_errors["distribution #{idx + 1}"] = request.errors.full_messages.join(", ")
end
end
end
throw_error_if_recipient_info_invalid
requests
end

def throw_error_if_copies_out_of_range
unless (1..500).cover?(copies)
fail Caseflow::Error::MissingRecipientInfo, "Copies must be between 1 and 500 (inclusive)".to_json
end
end

def throw_error_if_recipient_info_invalid
return unless recipient_errors.any?

fail Caseflow::Error::MissingRecipientInfo, recipient_errors.to_json
end

def recipient_errors
@recipient_errors ||= {}
end

def distribution_ids
@distribution_ids ||= []
end
end
18 changes: 16 additions & 2 deletions app/controllers/idt/api/v1/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class Idt::Api::V1::BaseController < ActionController::Base
if error.class.method_defined?(:serialize_response)
render(error.serialize_response)
else
render json: { message: "IDT Standard Error ID: " + uuid + " Unexpected error: #{error.message}" }, status: :internal_server_error
render json: {
message: "IDT Standard Error ID: " + uuid + " Unexpected error: #{error.message}"
}, status: :internal_server_error
end
end
# :nocov:
Expand All @@ -32,7 +34,19 @@ class Idt::Api::V1::BaseController < ActionController::Base
log_error(error)
uuid = SecureRandom.uuid
Rails.logger.error("IDT Standard Error ID: " + uuid)
render(json: { message: "IDT Standard Error ID: " + uuid + " Please enter a file number in the 'FILENUMBER' header" }, status: :unprocessable_entity)
render(json:
{ message:
"IDT Standard Error ID: " +
uuid +
" Please enter a file number in the 'FILENUMBER' header" },
status: :unprocessable_entity)
end

rescue_from Caseflow::Error::MissingRecipientInfo do |error|
log_error(error)
uuid = SecureRandom.uuid
render(json: { message: "IDT Exception ID: " + uuid + " Recipient information received was invalid or incomplete.",
errors: JSON.parse(error.message) }, status: :bad_request)
end

rescue_from Caseflow::Error::VeteranNotFound do |error|
Expand Down
79 changes: 56 additions & 23 deletions app/controllers/idt/api/v1/upload_vbms_document_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,73 @@

class Idt::Api::V1::UploadVbmsDocumentController < Idt::Api::V1::BaseController
include ApiRequestLoggingConcern
include MailPackageConcern

protect_from_forgery with: :exception
skip_before_action :verify_authenticity_token, only: [:create]
before_action :verify_access

def bgs
@bgs ||= BGSService.new
end

def create
appeal = nil
# Find veteran from appeal id and check with db
if params["appeal_id"].present?
appeal = LegacyAppeal.find_by_vacols_id(params["appeal_id"]) || Appeal.find_by_uuid(params["appeal_id"])
if appeal.nil?
fail Caseflow::Error::AppealNotFound, "IDT Standard Error ID: " + SecureRandom.uuid + " The appeal was unable to be found."
else
params["veteran_file_number"] = appeal.veteran_file_number
end

else
file_number = bgs.fetch_veteran_info(params["veteran_identifier"])&.dig(:file_number) || bgs.fetch_file_number_by_ssn(params["veteran_identifier"])
if file_number.nil?
fail Caseflow::Error::VeteranNotFound, "IDT Standard Error ID: " + SecureRandom.uuid + " The veteran was unable to be found."
end
# Create distributions for Package Manager mail service if recipient info present
build_mail_package

params["veteran_file_number"] = file_number
end
result = PrepareDocumentUploadToVbms.new(params, current_user, appeal).call
result = PrepareDocumentUploadToVbms.new(params, current_user, appeal, mail_package).call

if result.success?
render json: { message: "Document successfully queued for upload." }
success_message = { message: "Document successfully queued for upload." }
if recipient_info.present?
success_message[:distribution_ids] = distribution_ids
end
render json: success_message
else
render json: result.errors[0], status: :bad_request
end
end

private

# Find veteran from appeal id and check with db
def appeal
if appeal_id.blank?
find_file_number_by_veteran_identifier
return nil
end

@appeal ||= find_veteran_by_appeal_id
end

def appeal_id
params[:appeal_id]
end

def veteran_identifier
params[:veteran_identifier]
end

def bgs
@bgs ||= BGSService.new
end

def find_veteran_by_appeal_id
appeal = LegacyAppeal.find_by_vacols_id(appeal_id) || Appeal.find_by_uuid(appeal_id)
throw_not_found_error(Caseflow::Error::AppealNotFound, "appeal") if appeal.nil?
update_veteran_file_number(appeal.veteran_file_number)
appeal
end

def find_file_number_by_veteran_identifier
file_number = bgs.fetch_veteran_info(veteran_identifier)&.dig(:file_number) ||
bgs.fetch_file_number_by_ssn(veteran_identifier)
throw_not_found_error(Caseflow::Error::VeteranNotFound, "veteran") if file_number.nil?
update_veteran_file_number(file_number)
end

def update_veteran_file_number(file_number)
params["veteran_file_number"] = file_number
end

def throw_not_found_error(error, name)
uuid = SecureRandom.uuid
fail error, uuid + " The #{name} was unable to be found."
end
end
26 changes: 24 additions & 2 deletions app/controllers/idt/api/v2/appeals_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class Idt::Api::V2::AppealsController < Idt::Api::V1::BaseController
include MailPackageConcern

protect_from_forgery with: :exception
before_action :verify_access

Expand All @@ -23,10 +25,17 @@ def details
end

def outcode
result = BvaDispatchTask.outcode(appeal, outcode_params, user)
# Create distributions for Package Manager mail service if recipient info present
build_mail_package

result = BvaDispatchTask.outcode(appeal, outcode_params, user, mail_package)

if result.success?
return render json: { message: "Success!" }
success_response = { message: "Successful dispatch!" }
if recipient_info.present?
success_response[:distribution_ids] = distribution_ids
end
return render json: success_response
end

render json: { message: result.errors[0] }, status: :bad_request
Expand Down Expand Up @@ -151,4 +160,17 @@ def load_tags_by_doc_id
def outcode_params
params.permit(:citation_number, :decision_date, :redacted_document_location, :file)
end

def mail_params
params.permit(:copies, recipient_info: recipient_keys)
end

def recipient_keys
[
:recipient_type, :name, :first_name, :last_name, :claimant_station_of_jurisdiction, :postal_code,
:destination_type, :address_line_1, :address_line_2, :address_line_3, :address_line_4, :address_line_5,
:address_line_6, :treat_line_2_as_addressee, :treat_line_3_as_addressee, :city, :state, :country_name,
:country_code
]
end
end
74 changes: 74 additions & 0 deletions app/controllers/idt/api/v2/distributions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

class Idt::Api::V2::DistributionsController < Idt::Api::V1::BaseController
protect_from_forgery with: :exception
before_action :verify_access

def distribution
distribution_id = params[:distribution_id]
# Checks if the distribution id is blank and if it exists with the database
if distribution_id.blank? || !valid_id?(distribution_id)
return render_error(400, "Distribution Does Not Exist Or Id is blank", distribution_id)
end

distribution_uuid = distribution_uuid_from_id(distribution_id)

return pending_establishment(distribution_id) unless distribution_uuid

begin
# Retrieves the distribution package from the PacMan API
distribution_response = PacmanService.get_distribution_request(distribution_uuid)

response_code = distribution_response.code

fail StandardError if response_code != 200
# Handles errors when making any requests both from Pacman and the DB
rescue StandardError
return render_error(response_code, "Internal Server Error", distribution_id)
end

render json: format_response(distribution_response)
end

private

def pending_establishment(distribution_id)
render json: { id: distribution_id, status: "PENDING_ESTABLISHMENT" }, status: :ok
end

def format_response(response)
response_body = response.raw_body

begin
parsed_response = JSON.parse(response_body)

# Convert keys from camelCase to snake_case
parsed_response.deep_transform_keys do |key|
key.to_s.underscore.gsub(/e(\d)/, 'e_\1')
end
rescue JSON::ParseError => error
log_error(error + " Distribution ID: #{params[:distribution_id]}")

response_body
end
end

# Checks if the distribution exists in the database before sending request to Pacman
def valid_id?(distribution_id)
VbmsDistribution.exists?(id: distribution_id)
end

def distribution_uuid_from_id(pk_id)
VbmsDistribution.find(pk_id).uuid
end

# Renders errors and logs and tracks the here within Raven
# :reek:FeatureEnvy
def render_error(status, message, distribution_id)
error_uuid = SecureRandom.uuid
error_message = "[IDT] Http Status Code: #{status}, #{message}, (Distribution ID: #{distribution_id})"
Rails.logger.error(error_message.to_s + "Error ID: " + error_uuid)
Raven.capture_exception(error_message, extra: { error_uuid: error_uuid })
render json: { message: error_message + " #{error_uuid}" }, status: status
end
end
Loading

0 comments on commit f0e85e8

Please sign in to comment.