Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Mobile appeal model #18197

Merged
merged 18 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,9 @@ features:
mobile_military_indicator_logger:
actor_type: user
description: For mobile app, enables logging of military discharge codes
mobile_appeal_model:
actor_type: user
description: For mobile app, enables use of strict models for parsing appeals
form526_backup_submission_temp_killswitch:
actor_type: user
description: Provide a temporary killswitch to disable form526 backup submission if something were to go awry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def get_claim

def get_appeal
appeal = evss_claims_proxy.get_appeal(params[:id])

appeal = appeal_adapter.parse(appeal) if Flipper.enabled?(:mobile_appeal_model, @current_user)
render json: Mobile::V0::AppealSerializer.new(appeal)
end

Expand Down Expand Up @@ -158,6 +160,10 @@ def lighthouse_claims_adapter
Mobile::V0::Adapters::LighthouseIndividualClaims.new
end

def appeal_adapter
Mobile::V0::Adapters::Appeal.new
end

def lighthouse_claims_proxy
Mobile::V0::LighthouseClaims::Proxy.new(@current_user)
end
Expand Down
31 changes: 31 additions & 0 deletions modules/mobile/app/models/mobile/v0/adapters/appeal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Mobile
module V0
module Adapters
class Appeal
def parse(appeal)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't depend on instance state (maybe move it to another class?) - UtilityFunction

Mobile::V0::Appeals::Appeal.new(
id: appeal[:id],
appealIds: appeal[:appealIds],
active: appeal[:active],
alerts: appeal[:alerts],
aod: appeal[:aod],
aoj: appeal[:aoj],
description: appeal[:description],
docket: appeal[:docket],
events: appeal[:events].map(&:deep_symbolize_keys),
evidence: appeal[:evidence],
incompleteHistory: appeal[:incompleteHistory],
issues: appeal[:issues].map(&:deep_symbolize_keys),
location: appeal[:location],
programArea: appeal[:programArea],
status: appeal[:status].deep_symbolize_keys,
type: appeal[:type],
updated: appeal[:updated]
)
end
end
end
end
end
147 changes: 147 additions & 0 deletions modules/mobile/app/models/mobile/v0/appeals/appeal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# frozen_string_literal: true

require 'common/models/resource'

module Mobile
module V0
module Appeals
class Appeal < Common::Resource
AOJ_TYPES = Types::String.enum(
'vba',
'vha',
'nca',
'other'
)

LOCATION_TYPES = Types::String.enum(
'aoj',
'bva'
)

PROGRAM_AREA_TYPES = Types::String.enum(
'compensation',
'pension',
'insurance',
'loan_guaranty',
'education',
'vre',
'medical',
'burial',
'bva',
'other',
'multiple'
)

ALERT_TYPES = Types::String.enum(
'form9_needed',
'scheduled_hearing',
'hearing_no_show',
'held_for_evidence',
'cavc_option',
'ramp_eligible',
'ramp_ineligible',
'decision_soon',
'blocked_by_vso',
'scheduled_dro_hearing',
'dro_hearing_no_show'
)

EVENT_TYPES = Types::String.enum(
'claim_decision',
'nod',
'soc',
'form9',
'ssoc',
'certified',
'hearing_held',
'hearing_no_show',
'bva_decision',
'field_grant',
'withdrawn',
'ftr',
'ramp',
'death',
'merged',
'record_designation',
'reconsideration',
'vacated',
'other_close',
'cavc_decision',
'ramp_notice',
'transcript',
'remand_return',
'dro_hearing_held',
'dro_hearing_cancelled',
'dro_hearing_no_show'
)

LAST_ACTION_TYPES = Types::String.enum(
'field_grant',
'withdrawn',
'allowed',
'denied',
'remand',
'cavc_remand'
)

STATUS_TYPES = Types::String.enum(
'scheduled_hearing',
'pending_hearing_scheduling',
'on_docket',
'pending_certification_ssoc',
'pending_certification',
'pending_form9',
'pending_soc',
'stayed',
'at_vso',
'bva_development',
'decision_in_progress',
'bva_decision',
'field_grant',
'withdrawn',
'ftr',
'ramp',
'death',
'reconsideration',
'other_close',
'remand_ssoc',
'remand',
'merged'
)

attribute :id, Types::String
attribute :appealIds, Types::Array.of(Types::String)
attribute :active, Types::Bool
attribute :alerts, Types::Array do
attribute :type, ALERT_TYPES
attribute :details, Types::Hash
end
attribute :aod, Types::Bool.optional
attribute :aoj, AOJ_TYPES
attribute :description, Types::String
attribute :docket, Docket.optional
attribute :events, Types::Array do
attribute :type, EVENT_TYPES
attribute :date, Types::Date
end
attribute :evidence, Types::Array.of(Evidence).optional
attribute :incompleteHistory, Types::Bool
attribute :issues, Types::Array do
attribute :active, Types::Bool
attribute :lastAction, LAST_ACTION_TYPES.optional
attribute :description, Types::String
attribute :diagnosticCode, Types::String.optional
attribute :date, Types::Date
end
attribute :location, LOCATION_TYPES
attribute :programArea, PROGRAM_AREA_TYPES
attribute :status do
attribute :type, STATUS_TYPES
attribute :details, Types::Hash
end
attribute :type, Types::String
attribute :updated, Types::DateTime
end
end
end
end
16 changes: 16 additions & 0 deletions modules/mobile/app/models/mobile/v0/appeals/docket.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require 'common/models/resource'

module Mobile
module V0
module Appeals
class Docket < Common::Resource
attribute :type, Types::String
attribute :month, Types::Date
attribute :switchDueDate, Types::Date
attribute :eligibleToSwitch, Types::Bool
end
end
end
end
14 changes: 14 additions & 0 deletions modules/mobile/app/models/mobile/v0/appeals/evidence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

require 'common/models/resource'

module Mobile
module V0
module Appeals
class Evidence < Common::Resource
attribute :description, Types::String
attribute :date, Types::Date
end
end
end
end
2 changes: 1 addition & 1 deletion modules/mobile/docs/schemas/AppealEvent.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
type: object
properties:
data:
date:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

type: string
type:
type: string
Expand Down
102 changes: 93 additions & 9 deletions modules/mobile/spec/requests/mobile/v0/appeal_spec.rb
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we've been making an effort to not use request specs so much but it felt called for in this case since I wanted to make sure the response was identical to before the model was used.

Copy link
Contributor Author

@aherzberg aherzberg Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, when the flipper is removed, this will go back to being a basic request spec

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is a good use of request specs. You're only testing the happy/bad paths with the flag on and off. I think those are the correct cases. And I think we should probably always do a single happy path test of "expect the response data to match this exact data" because schema matchers aren't adequate. If you only test the matchers, there are cases where all the data could be nil because you made a mistake in the adapter and the matcher would still pass.

Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,102 @@
let!(:user) { sis_user }

context 'with an authorized user' do
it 'and a result that matches our schema is successfully returned with the 200 status' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/3294289', headers: sis_headers
expect(response).to have_http_status(:ok)
let!(:appeal_response) do
{ 'data' =>
{ 'id' => '3294289',
'type' => 'appeal',
'attributes' =>
{ 'appealIds' => [],
'active' => true,
'alerts' => [],
'aod' => false,
'aoj' => 'vba',
'description' => '',
'docket' => nil,
'events' =>
[{ 'date' => '2008-04-24', 'type' => 'claim_decision' },
{ 'date' => '2008-06-11', 'type' => 'nod' },
{ 'date' => '2010-09-10', 'type' => 'soc' },
{ 'date' => '2010-11-08', 'type' => 'form9' },
{ 'date' => '2014-01-03', 'type' => 'ssoc' },
{ 'date' => '2014-07-28', 'type' => 'certified' },
{ 'date' => '2015-04-17', 'type' => 'hearing_held' },
{ 'date' => '2015-07-24', 'type' => 'bva_decision' },
{ 'date' => '2015-10-06', 'type' => 'ssoc' },
{ 'date' => '2016-05-03', 'type' => 'bva_decision' },
{ 'date' => '2018-01-16', 'type' => 'ssoc' }],
'evidence' => [],
'incompleteHistory' => false,
'issues' =>
[{ 'active' => true, 'date' => '2016-05-03', 'description' => 'Increased rating, migraines',
'diagnosticCode' => '8100', 'lastAction' => 'remand' },
{ 'active' => true, 'date' => '2016-05-03',
'description' => 'Increased rating, limitation of leg motion',
'diagnosticCode' => '5260', 'lastAction' => 'remand' },
{ 'active' => true, 'date' => '2016-05-03',
'description' => '100% rating for individual unemployability',
'diagnosticCode' => nil, 'lastAction' => 'remand' },
{ 'active' => false, 'date' => nil, 'description' => 'Service connection, ankylosis of hip',
'diagnosticCode' => '5250', 'lastAction' => nil },
{ 'active' => true, 'date' => '2015-07-24',
'description' => 'Service connection, degenerative spinal arthritis', 'diagnosticCode' => '5242',
'lastAction' => 'remand' },
{ 'active' => false, 'date' => nil, 'description' => 'Service connection, hearing loss',
'diagnosticCode' => '6100', 'lastAction' => nil },
{ 'active' => true, 'date' => '2015-07-24',
'description' => 'Service connection, sciatic nerve paralysis',
'diagnosticCode' => '8520', 'lastAction' => 'remand' },
{ 'active' => false, 'date' => nil, 'description' => 'Service connection, arthritis due to trauma',
'diagnosticCode' => '5010', 'lastAction' => nil },
{ 'active' => false, 'date' => '2015-07-24',
'description' =>
'New and material evidence for service connection, degenerative spinal arthritis',
'diagnosticCode' => '5242', 'lastAction' => 'allowed' }],
'location' => 'aoj',
'programArea' => 'compensation',
'status' => { 'details' => {}, 'type' => 'remand_ssoc' },
'type' => 'legacyAppeal',
'updated' => '2018-01-19T10:20:42-05:00' } } }
end

context 'with appeals model used' do
before { Flipper.enable(:mobile_appeal_model) }
after { Flipper.disable(:mobile_appeal_model) }

it 'and a result that matches our schema is successfully returned with the 200 status' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/3294289', headers: sis_headers
expect(response).to have_http_status(:ok)
expect(response.parsed_body).to eq(appeal_response)
end
end

it 'and attempting to access a nonexistent appeal returns a 404 with an error' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/1234567', headers: sis_headers
expect(response).to have_http_status(:not_found)
expect(response.body).to match_json_schema('evss_errors')
end
end
end

it 'and attempting to access a nonexistant appeal returns a 404 wtih an error' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/1234567', headers: sis_headers
expect(response).to have_http_status(:not_found)
expect(response.body).to match_json_schema('evss_errors')
context 'with appeals model NOT used' do
before { Flipper.disable(:mobile_appeal_model) }

it 'and a result that matches our schema is successfully returned with the 200 status' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/3294289', headers: sis_headers
expect(response).to have_http_status(:ok)
expect(response.parsed_body).to eq(appeal_response)
end
end

it 'and attempting to access a nonexistant appeal returns a 404 wtih an error' do
VCR.use_cassette('caseflow/appeals') do
get '/mobile/v0/appeal/1234567', headers: sis_headers
expect(response).to have_http_status(:not_found)
expect(response.body).to match_json_schema('evss_errors')
end
end
end
end
Expand Down
Loading