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

Champva 94284 pega reporting api connection #19951

Merged
merged 4 commits into from
Dec 31, 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
2 changes: 2 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ intent_to_file:

ivc_champva:
prefill: true
pega_api:
api_key: fake_api_key

form_upload_flow:
prefill: true
Expand Down
58 changes: 58 additions & 0 deletions modules/ivc_champva/lib/pega_api/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

require 'common/client/base'
require_relative 'configuration'

module IvcChampva
module PegaApi
class PegaApiError < StandardError; end

class Client < Common::Client::Base
configuration IvcChampva::PegaApi::Configuration

##
# HTTP POST call to the Pega API to retrieve a report
#
# @param date_start [Date, nil] the start date of the report
# @param date_end [Date, nil] the end date of the report
# @return [Array<Hash>] the report rows
def get_report(date_start, date_end)
resp = connection.post(config.base_path) do |req|
req.headers = headers(date_start, date_end)
end

raise "response code: #{resp.status}, response body: #{resp.body}" unless resp.status == 200

# We also need to check the StatusCode in the response body.
# It seems that when this API errors out, it will return responses with HTTP 200 statuses, but
# the StatusCode in the response body will be something other than 200.
response = JSON.parse(resp.body, symbolize_names: false)
unless response['statusCode'] == 200
raise "alternate response code: #{response['statusCode']}, response body: #{response['body']}"
end

# With both status codes checked and passing, we should now have a body that is more JSON embedded in a string.
# This is our report, let's decode it.
JSON.parse(response['body'])
rescue => e
raise PegaApiError, e.message.to_s
end

##
# Assembles headers for the Pega API request
#
# @param date_start [Date, nil] the start date of the report
# @param date_end [Date, nil] the end date of the report
# @return [Hash] the headers
def headers(date_start, date_end)
{
:content_type => 'application/json',
'x-api-key' => Settings.ivc_champva.pega_api.api_key.to_s,
'date_start' => date_start.to_s,
'date_end' => date_end.to_s,
'case_id' => '' # case_id seems to have no effect, but it is required by the API
}
end
end
end
end
29 changes: 29 additions & 0 deletions modules/ivc_champva/lib/pega_api/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require 'common/client/configuration/rest'
require 'common/client/middleware/response/raise_custom_error'

module IvcChampva
module PegaApi
class Configuration < Common::Client::Configuration::REST
def base_path
'https://bt41mfpkj5.execute-api.us-gov-west-1.amazonaws.com/prod/'
end
Comment on lines +9 to +11
Copy link
Contributor

@michaelclement michaelclement Dec 31, 2024

Choose a reason for hiding this comment

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

Not a blocker, but we may want to move this into our AWS param store at some point in the future.

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 was thinking about that, but this is how paths are being stored elsewhere in similar code so I decided to err on the side of consistency.


def connection
Faraday.new(base_path, headers: base_request_headers, request: request_options) do |conn|
conn.use :breakers
# conn.use :instrumentation, name: 'dhp.fitbit.request.faraday'

# Uncomment this if you want curlggg command equivalent or response output to log
# conn.request(:curl, ::Logger.new(STDOUT), :warn) unless Rails.env.production?
# conn.response(:logger, ::Logger.new(STDOUT), bodies: true) unless Rails.env.production?

# conn.response :raise_custom_error, error_prefix: service_name

conn.adapter Faraday.default_adapter
end
end
end
end
end

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"statusCode": 500,
"body": "'case_id'"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"message": "Forbidden"
}
122 changes: 122 additions & 0 deletions modules/ivc_champva/spec/lib/pega_api_client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

require 'rails_helper'
require 'pega_api/client'

RSpec.describe IvcChampva::PegaApi::Client do
subject { described_class.new }

describe 'get_report' do
let(:body200and200) do # pega api response with HTTP status 200 and alternate status 200
fixture_path = Rails.root.join('modules', 'ivc_champva', 'spec', 'fixtures', 'pega_api_json',
'report_response_200_200.json')
fixture_path.read
end

let(:body200and500) do # pega api response with HTTP status 200 and alternate status 500
fixture_path = Rails.root.join('modules', 'ivc_champva', 'spec', 'fixtures', 'pega_api_json',
'report_response_200_500.json')
fixture_path.read
end

let(:body403) do # pega api response with HTTP status 403 forbidden
fixture_path = Rails.root.join('modules', 'ivc_champva', 'spec', 'fixtures', 'pega_api_json',
'report_response_403.json')
fixture_path.read
end

context 'successful response from pega' do
let(:faraday_response) { double('Faraday::Response', status: 200, body: body200and200) }

before do
allow_any_instance_of(Faraday::Connection).to receive(:post).with(anything).and_return(faraday_response)
end

it 'returns the body as an array of hashes' do
result = subject.get_report(Date.new(2024, 11, 1), Date.new(2024, 12, 31))

expect(result[0]['Creation Date']).to eq('2024-11-27T08:42:11.372000')
expect(result[0]['PEGA Case ID']).to eq('D-55824')
expect(result[0]['Status']).to eq('Open')
end
end

context 'unsuccessful pega response with bad HTTP status' do
let(:faraday_response) { double('Faraday::Response', status: 403, body: body403) }

before do
allow_any_instance_of(Faraday::Connection).to receive(:post).with(anything).and_return(faraday_response)
end

it 'raises error when response is 404' do
expect { subject.get_report(nil, nil) }.to raise_error(IvcChampva::PegaApi::PegaApiError)
end
end

context 'unsuccessful pega response with bad alternate status' do
let(:faraday_response) { double('Faraday::Response', status: 200, body: body200and500) }

before do
allow_any_instance_of(Faraday::Connection).to receive(:post).with(anything).and_return(faraday_response)
end

it 'raises error when alternate status is 500' do
expect { subject.get_report(nil, nil) }.to raise_error(IvcChampva::PegaApi::PegaApiError)
end
end
end

describe 'headers' do
it 'returns the right headers' do
result = subject.headers(Date.new(2024, 11, 1), Date.new(2024, 12, 31))

expect(result[:content_type]).to eq('application/json')
expect(result['x-api-key']).to eq('fake_api_key')
expect(result['date_start']).to eq('2024-11-01')
expect(result['date_end']).to eq('2024-12-31')
expect(result['case_id']).to eq('')
end

it 'returns the right headers with nil dates' do
result = subject.headers(nil, nil)

expect(result[:content_type]).to eq('application/json')
expect(result['x-api-key']).to eq('fake_api_key')
expect(result['date_start']).to eq('')
expect(result['date_end']).to eq('')
expect(result['case_id']).to eq('')
end
end

# Temporary, delete me
# This test is used to hit the production endpoint when running locally.
# It can be removed once we have some real code that uses the Pega API client.
describe 'hit the production endpoint', skip: 'this is useful as a way to hit the API during local development' do
let(:forced_headers) do
{
:content_type => 'application/json',
# use the following line when running locally tp pull the key from an environment variable
'x-api-key' => ENV.fetch('PEGA_API_KEY'), # to set: export PEGA_API_KEY=insert1the2api3key4here
'date_start' => '', # '2024-11-01', # '11/01/2024',
'date_end' => '', # '2024-12-31', # '12/07/2024',
'case_id' => ''
}
end

before do
allow_any_instance_of(IvcChampva::PegaApi::Client).to receive(:headers).with(anything, anything)
.and_return(forced_headers)
end

it 'returns report data' do
VCR.configure do |c|
c.allow_http_connections_when_no_cassette = true
end

result = subject.get_report(Date.new(2024, 11, 1), Date.new(2024, 12, 31))
expect(result.count).to be_positive

# byebug # in byebug, type 'p result' to view the response
end
end
end
Loading