diff --git a/config/features.yml b/config/features.yml index 32d2fb4477e..c6ad6ac2985 100644 --- a/config/features.yml +++ b/config/features.yml @@ -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 diff --git a/modules/mobile/app/controllers/mobile/v0/claims_and_appeals_controller.rb b/modules/mobile/app/controllers/mobile/v0/claims_and_appeals_controller.rb index fb5b9e4b851..9f672583f14 100644 --- a/modules/mobile/app/controllers/mobile/v0/claims_and_appeals_controller.rb +++ b/modules/mobile/app/controllers/mobile/v0/claims_and_appeals_controller.rb @@ -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 @@ -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 diff --git a/modules/mobile/app/models/mobile/v0/adapters/appeal.rb b/modules/mobile/app/models/mobile/v0/adapters/appeal.rb new file mode 100644 index 00000000000..8479f3a2dd9 --- /dev/null +++ b/modules/mobile/app/models/mobile/v0/adapters/appeal.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Mobile + module V0 + module Adapters + class Appeal + def parse(appeal) + 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 diff --git a/modules/mobile/app/models/mobile/v0/appeals/appeal.rb b/modules/mobile/app/models/mobile/v0/appeals/appeal.rb new file mode 100644 index 00000000000..fa45e241788 --- /dev/null +++ b/modules/mobile/app/models/mobile/v0/appeals/appeal.rb @@ -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 diff --git a/modules/mobile/app/models/mobile/v0/appeals/docket.rb b/modules/mobile/app/models/mobile/v0/appeals/docket.rb new file mode 100644 index 00000000000..ae65da7ab05 --- /dev/null +++ b/modules/mobile/app/models/mobile/v0/appeals/docket.rb @@ -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 diff --git a/modules/mobile/app/models/mobile/v0/appeals/evidence.rb b/modules/mobile/app/models/mobile/v0/appeals/evidence.rb new file mode 100644 index 00000000000..08db285d309 --- /dev/null +++ b/modules/mobile/app/models/mobile/v0/appeals/evidence.rb @@ -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 diff --git a/modules/mobile/docs/schemas/AppealEvent.yml b/modules/mobile/docs/schemas/AppealEvent.yml index a9b0006a1e7..9fc9d5e3bd7 100644 --- a/modules/mobile/docs/schemas/AppealEvent.yml +++ b/modules/mobile/docs/schemas/AppealEvent.yml @@ -1,6 +1,6 @@ type: object properties: - data: + date: type: string type: type: string diff --git a/modules/mobile/spec/requests/mobile/v0/appeal_spec.rb b/modules/mobile/spec/requests/mobile/v0/appeal_spec.rb index 3d5aebe5499..69285c79764 100644 --- a/modules/mobile/spec/requests/mobile/v0/appeal_spec.rb +++ b/modules/mobile/spec/requests/mobile/v0/appeal_spec.rb @@ -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