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

Get Claim By ID Endpoint #18404

Merged
merged 16 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
31 changes: 30 additions & 1 deletion app/swagger/swagger/requests/travel_pay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Requests
class TravelPay
include Swagger::Blocks

swagger_path '/travel_pay/claims' do
swagger_path '/travel_pay/v0/claims' do
operation :get do
extend Swagger::Responses::AuthenticationError
extend Swagger::Responses::BadRequestError
Expand All @@ -31,6 +31,35 @@ class TravelPay
end
end
end

swagger_path '/travel_pay/v0/claims/{id}' do
operation :get do
extend Swagger::Responses::AuthenticationError
extend Swagger::Responses::BadRequestError
extend Swagger::Responses::RecordNotFoundError

key :description, 'Get a single travel reimbursment claim summary'
key :operationId, 'getTravelPayClaimById'
key :tags, %w[travel_pay]

parameter :authorization

parameter do
key :name, 'id'
key :in, :path
key :description, 'The non-PII/PHI id of a claim (UUIDv4)'
key :required, true
key :type, :string
end

response 200 do
key :description, 'Successfully retrieved claim for a user'
schema do
key :$ref, :TravelPayClaimSummary
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ def index
render json: claims, status: :ok
end

def show
begin
token_service.get_tokens(@current_user) => { veis_token:, btsss_token: }
claim = claims_service.get_claim_by_id(veis_token, btsss_token, params[:id])
rescue Faraday::Error => e
TravelPay::ServiceError.raise_mapped_error(e)
rescue ArgumentError => e
raise Common::Exceptions::BadRequest, message: e.message
end

if claim.nil?
raise Common::Exceptions::ResourceNotFound, message: "Claim not found. ID provided: #{params[:id]}"
end

render json: claim, status: :ok
end

private

def claims_service
Expand Down
20 changes: 20 additions & 0 deletions modules/travel_pay/app/services/travel_pay/claims_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ def get_claims(veis_token, btsss_token, params = {})
}
end

def get_claim_by_id(veis_token, btsss_token, claim_id)
# ensure claim ID is the right format
uuid_v4_format = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i

unless uuid_v4_format.match?(claim_id)
raise ArgumentError, message: "Expected claim id to be a valid v4 UUID, got #{claim_id}."
end

claims_response = client.get_claims(veis_token, btsss_token)

claims = claims_response.body['data']

claim = claims.find { |c| c['id'] == claim_id }

if claim
claim['claimStatus'] = claim['claimStatus'].underscore.titleize
claim
end
end

private

def filter_by_date(date_string, claims)
Expand Down
4 changes: 0 additions & 4 deletions modules/travel_pay/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# frozen_string_literal: true

TravelPay::Engine.routes.draw do
# TODO: remove this mapping once vets-website
# is pointing to the /v0/claims routes
resources :claims, controller: '/travel_pay/v0/claims'

namespace :v0 do
resources :claims
end
Expand Down
90 changes: 46 additions & 44 deletions modules/travel_pay/spec/requests/travel_pay/claims_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'rails_helper'
require 'securerandom'

RSpec.describe TravelPay::V0::ClaimsController, type: :request do
let(:user) { build(:user) }
Expand All @@ -19,70 +20,71 @@
]
end

context '(Older) unversioned API route' do
it 'responds with 200' do
it 'responds with 200' do
VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[method path]) do
get '/travel_pay/v0/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
expect(response).to have_http_status(:ok)
claim_ids = JSON.parse(response.body)['data'].pluck('id')
expect(claim_ids).to eq(expected_claim_ids)
end
end

context 'filtering claims' do
it 'returns a subset of claims' do
params = { 'appt_datetime' => '2024-04-09' }
headers = { 'Authorization' => 'Bearer vagov_token' }

VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[method path]) do
get '/travel_pay/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
get('/travel_pay/v0/claims', params:, headers:)
expect(response).to have_http_status(:ok)
claim_ids = JSON.parse(response.body)['data'].pluck('id')
expect(claim_ids).to eq(expected_claim_ids)
expect(claim_ids.length).to eq(1)
expect(claim_ids[0]).to eq('claim_id_2')
end
end
end

context 'Versioned v0 API route' do
it 'responds with 200' do
it 'returns all claims if params not passed' do
VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[method path]) do
get '/travel_pay/v0/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
expect(response).to have_http_status(:ok)
claim_ids = JSON.parse(response.body)['data'].pluck('id')
expect(claim_ids).to eq(expected_claim_ids)
expect(claim_ids.length).to eq(3)
end
end

context 'filtering claims' do
it 'returns a subset of claims' do
params = { 'appt_datetime' => '2024-04-09' }
headers = { 'Authorization' => 'Bearer vagov_token' }

VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[method path]) do
get('/travel_pay/v0/claims', params:, headers:)
expect(response).to have_http_status(:ok)
claim_ids = JSON.parse(response.body)['data'].pluck('id')
expect(claim_ids.length).to eq(1)
expect(claim_ids[0]).to eq('claim_id_2')
end
end

it 'returns all claims if params not passed' do
VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[method path]) do
get '/travel_pay/v0/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
expect(response).to have_http_status(:ok)
claim_ids = JSON.parse(response.body)['data'].pluck('id')
expect(claim_ids.length).to eq(3)
end
end
end
end
end

context 'unsuccessful response from API' do
context '(Older) unversioned API route' do
it 'responds with a 404 if the API endpoint is not found' do
VCR.use_cassette('travel_pay/404_claims', match_requests_on: %i[method path]) do
get '/travel_pay/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
get '/travel_pay/v0/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
expect(response).to have_http_status(:bad_request)
end
end
end
end
end

context 'Versioned v0 API route' do
it 'responds with a 404 if the API endpoint is not found' do
VCR.use_cassette('travel_pay/404_claims', match_requests_on: %i[method path]) do
get '/travel_pay/v0/claims', params: nil, headers: { 'Authorization' => 'Bearer vagov_token' }
expect(response).to have_http_status(:bad_request)
end
end
describe '#show' do
it 'returns a single claim on success' do
VCR.use_cassette('travel_pay/show/success', match_requests_on: %i[method path]) do
# This claim ID matches a claim ID in the cassette.
claim_id = '33016896-ed7f-4d4f-a81b-cc4f2ca0832c'
expected_claim_num = 'TC092809828275'

get "/travel_pay/v0/claims/#{claim_id}", headers: { 'Authorization' => 'Bearer vagov_token' }
actual_claim_num = JSON.parse(response.body)['claimNumber']

expect(response).to have_http_status(:ok)
expect(actual_claim_num).to eq(expected_claim_num)
end
end

it 'returns a Not Found response if claim number valid but claim not found' do
VCR.use_cassette('travel_pay/show/success', match_requests_on: %i[method path]) do
# This claim ID matches a claim ID in the cassette.
claim_id = SecureRandom.uuid

get "/travel_pay/v0/claims/#{claim_id}", headers: { 'Authorization' => 'Bearer vagov_token' }

expect(response).to have_http_status(:not_found)
end
end
end
Expand Down
32 changes: 30 additions & 2 deletions modules/travel_pay/spec/services/claims_service_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'rails_helper'
require 'securerandom'

describe TravelPay::ClaimsService do
context 'get_claims' do
Expand Down Expand Up @@ -36,8 +37,8 @@
'modifiedOn' => '2024-02-01T00:00:00.0Z'
},
{
'id' => 'uuid4',
'claimNumber' => '73611905-71bf-46ed-b1ec-e790593b8565',
'id' => '73611905-71bf-46ed-b1ec-e790593b8565',
'claimNumber' => 'TC0004',
'claimName' => '9d81c1a1-cd05-47c6-be97-d14dec579893',
'claimStatus' => 'Claim Submitted',
'appointmentDateTime' => nil,
Expand Down Expand Up @@ -73,6 +74,33 @@
expect(actual_statuses).to match_array(expected_statuses)
end

context 'get claim by id' do
it 'returns a single claim when passed a valid id' do
claim_id = '73611905-71bf-46ed-b1ec-e790593b8565'
expected_claim = claims_data['data'].find { |c| c['id'] == claim_id }
service = TravelPay::ClaimsService.new
actual_claim = service.get_claim_by_id(*tokens, claim_id)

expect(actual_claim).to eq(expected_claim)
end

it 'returns nil if a claim with the given id was not found' do
claim_id = SecureRandom.uuid
service = TravelPay::ClaimsService.new
actual_claim = service.get_claim_by_id(*tokens, claim_id)

expect(actual_claim).to eq(nil)
end

it 'throws an ArgumentException if claim_id is invalid format' do
claim_id = 'this-is-definitely-a-uuid-right'
service = TravelPay::ClaimsService.new

expect { service.get_claim_by_id(*tokens, claim_id) }
.to raise_error(ArgumentError, /valid v4 UUID/i)
end
end

context 'filter by appt date' do
it 'returns claims that match appt date if specified' do
service = TravelPay::ClaimsService.new
Expand Down
56 changes: 53 additions & 3 deletions spec/requests/swagger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3282,20 +3282,70 @@
let(:mhv_user) { build(:user, :loa3) }

it 'returns unauthorized for unauthed user' do
expect(subject).to validate(:get, '/travel_pay/claims', 401)
expect(subject).to validate(:get, '/travel_pay/v0/claims', 401)
end

it 'returns 400 for invalid request' do
headers = { '_headers' => { 'Cookie' => sign_in(mhv_user, nil, true) } }
VCR.use_cassette('travel_pay/404_claims', match_requests_on: %i[host path method]) do
expect(subject).to validate(:get, '/travel_pay/claims', 400, headers)
expect(subject).to validate(:get, '/travel_pay/v0/claims', 400, headers)
end
end

it 'returns 200 for successful response' do
headers = { '_headers' => { 'Cookie' => sign_in(mhv_user, nil, true) } }
VCR.use_cassette('travel_pay/200_claims', match_requests_on: %i[host path method]) do
expect(subject).to validate(:get, '/travel_pay/claims', 200, headers)
expect(subject).to validate(:get, '/travel_pay/v0/claims', 200, headers)
end
end
end

context 'show' do
let(:mhv_user) { build(:user, :loa3) }

it 'returns unauthorized for unauthed user' do
expect(subject).to validate(
:get,
'/travel_pay/v0/claims/{id}',
401,
{}.merge('id' => '24e227ea-917f-414f-b60d-48b7743ee95d')
)
end

it 'returns 404 for missing claim' do
headers = { '_headers' => { 'Cookie' => sign_in(mhv_user, nil, true) } }
VCR.use_cassette('travel_pay/show/success', match_requests_on: %i[path method]) do
expect(subject).to validate(
:get,
'/travel_pay/v0/claims/{id}',
404,
headers.merge('id' => '8656ad4e-5cdf-41e2-bbd5-af843d2fa8fe')
)
end
end

it 'returns 400 for invalid request' do
headers = { '_headers' => { 'Cookie' => sign_in(mhv_user, nil, true) } }
VCR.use_cassette('travel_pay/show/success', match_requests_on: %i[path method]) do
expect(subject).to validate(
:get,
'/travel_pay/v0/claims/{id}',
400,
headers.merge('id' => '8656')
)
end
end

it 'returns 200 for successful response' do
headers = { '_headers' => { 'Cookie' => sign_in(mhv_user, nil, true) } }
claim_id = '33016896-ed7f-4d4f-a81b-cc4f2ca0832c'
VCR.use_cassette('travel_pay/show/success', match_requests_on: %i[path method]) do
expect(subject).to validate(
:get,
'/travel_pay/v0/claims/{id}',
200,
headers.merge('id' => claim_id)
)
end
end
end
Expand Down
Loading
Loading