Skip to content

Commit

Permalink
Merge branch 'master' into MBMS-58688/AddressValidationControllerWith…
Browse files Browse the repository at this point in the history
…outUserAuthentication
  • Loading branch information
ConnorJeff authored Mar 6, 2024
2 parents 675dbcf + a9599df commit 2b8a15e
Show file tree
Hide file tree
Showing 22 changed files with 563 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ GEM
ffi
ssrf_filter (1.1.2)
staccato (0.5.3)
statsd-instrument (3.6.1)
statsd-instrument (3.7.0)
strong_migrations (1.7.0)
activerecord (>= 5.2)
super_diff (0.11.0)
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/v0/claim_letters_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def service
# 68: 5103/DTA Letter
def allowed_doctypes
doctypes = %w[184]
doctypes << '27' if Flipper.enabled?(:cst_include_ddl_boa_letters, @user)
doctypes << '65' if Flipper.enabled?(:cst_include_ddl_5103_letters, @user)
doctypes << '68' if Flipper.enabled?(:cst_include_ddl_5103_letters, @user)
doctypes << '27' if Flipper.enabled?(:cst_include_ddl_boa_letters, @current_user)
doctypes << '65' if Flipper.enabled?(:cst_include_ddl_5103_letters, @current_user)
doctypes << '68' if Flipper.enabled?(:cst_include_ddl_5103_letters, @current_user)
doctypes
end
end
Expand Down
17 changes: 11 additions & 6 deletions app/controllers/v1/post911_gi_bill_statuses_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

require 'formatters/date_formatter'
require 'lighthouse/benefits_education/outside_working_hours'
require 'lighthouse/benefits_education/service'

module V1
class Post911GIBillStatusesController < ApplicationController
Expand All @@ -14,19 +16,22 @@ class Post911GIBillStatusesController < ApplicationController
STATSD_GI_BILL_FAIL_KEY = 'api.lighthouse.gi_bill_status.fail'

def show
response = service.get_gi_bill_status
render json: response.body['chapter33EducationInfo']
render json: service.get_gi_bill_status
rescue => e
status = e.errors.first[:status].to_i if e.errors&.first&.key?(:status)
log_vet_not_found(@current_user, Time.now.in_time_zone('Eastern Time (US & Canada)')) if status == 404
StatsD.increment(STATSD_GI_BILL_FAIL_KEY, tags: ["error:#{status}"])
render json: { error: e.errors.first }, status: status || :internal_server_error
handle_error(e)
ensure
StatsD.increment(STATSD_GI_BILL_TOTAL_KEY)
end

private

def handle_error(e)
status = e.errors.first[:status].to_i if e.errors&.first&.key?(:status)
log_vet_not_found(@current_user, Time.now.in_time_zone('Eastern Time (US & Canada)')) if status == 404
StatsD.increment(STATSD_GI_BILL_FAIL_KEY, tags: ["error:#{status}"])
render json: { error: e.errors.first }, status: status || :internal_server_error
end

def service_available?
unless BenefitsEducation::Service.within_scheduled_uptime?
StatsD.increment(STATSD_GI_BILL_FAIL_KEY, tags: ['error:scheduled_downtime'])
Expand Down
44 changes: 44 additions & 0 deletions app/models/lighthouse/education_benefits/education_benefit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require 'active_support/inflector'

module Lighthouse
module EducationBenefits
# The EducationBenefit model represents a veteran's education benefit status.
# This model is used to parse and manipulate the data returned from the Lighthouse API.
# Although it includes ActiveModel::Model for some ActiveRecord-like features such as validations and conversions,
# it does not persist any data to a database.
class EducationBenefit
include ActiveModel::Model
attr_accessor :first_name, :last_name, :name_suffix, :date_of_birth, :va_file_number, :active_duty,
:veteran_is_eligible, :regional_processing_office, :eligibility_date,
:percentage_benefit, :original_entitlement, :used_entitlement,
:remaining_entitlement, :delimiting_date
attr_reader :enrollments

def initialize(attributes = {})
super(attributes.deep_transform_keys { |key| key.to_s.underscore })
end

def enrollments=(values)
@enrollments = values.map do |value|
Enrollment.new(value)
end
end

# existing data contracts rely on eg `date_of_birth` so we must
# modify some of the field names
def date_time_of_birth=(value)
@date_of_birth = value
end

def delimiting_date_time=(value)
@delimiting_date = value
end

def eligibility_date_time=(value)
@eligibility_date = value
end
end
end
end
33 changes: 33 additions & 0 deletions app/models/lighthouse/education_benefits/enrollment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

require 'active_support/inflector'

module Lighthouse
module EducationBenefits
# The Enrollment class encapsulates the concept of a veteran's enrollment in an education program.
# This model is used to parse and manipulate the data returned from the Lighthouse API.
# Although it includes ActiveModel::Model for some ActiveRecord-like features such as validations and conversions,
# it does not persist any data to a database.
class Enrollment
include ActiveModel::Model
attr_accessor :begin_date, :end_date, :facility_code, :facility_name, :participant_id,
:training_type, :term_id, :hour_type, :full_time_hours,
:full_time_credit_hour_under_grad, :vacation_day_count, :on_campus_hours,
:online_hours, :yellow_ribbon_amount, :status, :amendments

def initialize(attributes = {})
super(attributes.deep_transform_keys { |key| key.to_s.underscore })
end

# existing data contracts rely on eg `begin_date` so we must
# modify some of the field names
def begin_date_time=(value)
@begin_date = value
end

def end_date_time=(value)
@end_date = value
end
end
end
end
20 changes: 20 additions & 0 deletions app/models/schema_contract/validation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module SchemaContract
class Validation < ApplicationRecord
self.table_name = 'schema_contract_validations'

attribute :contract_name, :string
attribute :user_uuid, :string
attribute :response, :jsonb
attribute :error_details, :string

validates :contract_name, presence: true
validates :user_uuid, presence: true
validates :response, presence: true
validates :status, presence: true

enum status: { initialized: 0, success: 1, schema_errors_found: 2, schema_not_found: 3, error: 4 },
_default: :initialized
end
end
21 changes: 21 additions & 0 deletions app/models/schema_contract/validation_initiator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module SchemaContract
class ValidationInitiator
def self.call(user:, response:, contract_name:)
if response.success? && Flipper.enabled?("schema_contract_#{contract_name}")
return if SchemaContract::Validation.where(contract_name:, created_at: Time.zone.today.all_day).any?

record = SchemaContract::Validation.create(
contract_name:, user_uuid: user.uuid, response: response.to_json, status: 'initialized'
)
Rails.logger.info('Initiating schema contract validation', { contract_name:, record_id: record.id })
SchemaContract::ValidationJob.perform_async(record.id)
end
rescue => e
# blanket rescue to avoid blocking main thread execution
message = { response:, contract_name:, error_details: e.message }
Rails.logger.error('Error creating schema contract job', message)
end
end
end
55 changes: 55 additions & 0 deletions app/models/schema_contract/validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module SchemaContract
class Validator
class SchemaContractValidationError < StandardError; end

def initialize(record_id)
@record_id = record_id
end

def validate
errors = JSON::Validator.fully_validate(parsed_schema, record.response)
if errors.any?
@result = :schema_errors_found
record.update(error_details: errors)
detailed_message = { error_type: 'Schema discrepancy found', record_id: @record_id, response: record.response,
details: errors }
raise SchemaContractValidationError, detailed_message
else
@result = :success
end
rescue SchemaContractValidationError => e
raise e
rescue => e
@result = :error
detailed_message = { error_type: 'Unknown', record_id: @record_id, details: e.message }
raise SchemaContractValidationError, detailed_message
ensure
record&.update(status: @result) if defined?(@record)
end

private

def record
@record ||= Validation.find(@record_id)
end

def schema_file
@schema_file ||= begin
path = Settings.schema_contract[record.contract_name]
if path.nil?
@result = :schema_not_found
raise SchemaContractValidationError, "No schema file #{record.contract_name} found."
end

Rails.root.join(path)
end
end

def parsed_schema
file_contents = File.read(schema_file)
JSON.parse(file_contents)
end
end
end
13 changes: 13 additions & 0 deletions app/sidekiq/schema_contract/validation_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module SchemaContract
class ValidationJob
include Sidekiq::Job

sidekiq_options(retry: false)

def perform(contract_name)
SchemaContract::Validator.new(contract_name).validate
end
end
end
2 changes: 1 addition & 1 deletion config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ features:
description: Enables new review page navigation for users completing the Financial Status Report (FSR) form.
enable_in_development: true
find_a_representative_enabled:
actor_type: user
actor_type: cookie_id
description: Enables Find a Representative tool
enable_in_development: true
find_a_representative_enable_frontend:
Expand Down
3 changes: 3 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1666,3 +1666,6 @@ vye:
r: 8
p: 1
length: 16

schema_contract:
test_index: 'spec/fixtures/schema_contract/test_schema.json'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddStateToForm526Submissions < ActiveRecord::Migration[7.0]
def change
add_column :form526_submissions, :aasm_state, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions lib/lighthouse/benefits_education/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,16 @@ def inspect

##
# Retrieve a veteran's Post-9/11 GI Bill Status
# @return [Faraday::Response] response from a GET request to Lighthouse API:
# A veteran's GI Bill status
# @return [String] A JSON string representing the veteran's GI Bill status.
def get_gi_bill_status
config.get(@icn)
rescue => e
handle_error(e, config.service_name, config.base_api_path)
response = begin
config.get(@icn)
rescue => e
handle_error(e, config.service_name, config.base_api_path)
end

education_info = response.body&.[]('chapter33EducationInfo') || {}
Lighthouse::EducationBenefits::EducationBenefit.new(education_info).to_json
end

def handle_error(error, lighthouse_client_id, endpoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def icn
end

def first_party?
%w[VETERAN SURVIVING_DEPENDENT].include?(params[:preparer_identification])
params[:preparer_identification] == 'VETERAN'
end

def get_form_id
Expand Down
30 changes: 14 additions & 16 deletions modules/simple_forms_api/spec/requests/v1/uploads_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,20 @@
end

describe 'request with intent to file' do
describe 'veteran or surviving dependent' do
%w[VETERAN SURVIVING_DEPENDENT].each do |identification|
it 'makes the request with an intent to file' do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/404_response') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/200_response_pension') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/200_response_survivor') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/create_compensation_200_response') do
fixture_path = Rails.root.join('modules', 'simple_forms_api', 'spec', 'fixtures', 'form_json',
'vba_21_0966-min.json')
data = JSON.parse(fixture_path.read)
data['preparer_identification'] = identification

post '/simple_forms_api/v1/simple_forms', params: data

expect(response).to have_http_status(:ok)
end
describe 'veteran' do
it 'makes the request with an intent to file' do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/404_response') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/200_response_pension') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/200_response_survivor') do
VCR.use_cassette('lighthouse/benefits_claims/intent_to_file/create_compensation_200_response') do
fixture_path = Rails.root.join('modules', 'simple_forms_api', 'spec', 'fixtures', 'form_json',
'vba_21_0966-min.json')
data = JSON.parse(fixture_path.read)
data['preparer_identification'] = 'VETERAN'

post '/simple_forms_api/v1/simple_forms', params: data

expect(response).to have_http_status(:ok)
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions spec/controllers/v1/post911_gi_bill_statuses_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
it 'returns a 200 success' do
# valid icn retrieved from
# https://github.com/department-of-veterans-affairs/vets-api-clients/blob/master/test_accounts/benefits_test_accounts.md
# 001 Tamara E Ellis F 6/19/67 796130115 1012667145V762142
valid_user = FactoryBot.create(:user, :loa3, icn: '1012667145V762142')
sign_in_as(valid_user)

Expand All @@ -29,6 +30,16 @@
end

expect(response).to have_http_status(:ok)
response_body = JSON.parse(response.body)

# assertions that the data returned will not be empty strings
expect(response_body['first_name']).not_to be_empty
expect(response_body['last_name']).not_to be_empty
expect(response_body['date_of_birth']).not_to be_empty
expect(response_body['delimiting_date']).not_to be_empty
expect(response_body['eligibility_date']).not_to be_empty
expect(response_body['enrollments'][0]['begin_date']).not_to be_empty
expect(response_body['enrollments'][0]['end_date']).not_to be_empty
end

it 'returns a 404 when vet isn\'t found' do
Expand Down
10 changes: 10 additions & 0 deletions spec/factories/schema_contract/validations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

FactoryBot.define do
factory :schema_contract_validation, class: 'SchemaContract::Validation' do
contract_name { 'test_index' }
user_uuid { '1234' }
response { { key: 'value' } }
status { 'initialized' }
end
end
Loading

0 comments on commit 2b8a15e

Please sign in to comment.