Skip to content

Commit

Permalink
MHV-58591 MHV-57739 Authentication for MHV Classic radiology endpoint (
Browse files Browse the repository at this point in the history
…#17381)

* MHV-57739 Add radiology controller and backend connection

* MHV-57739 Got radiology authentication working with session locking

* MHV-58591 Fixed up unit test to add authentication

* MHV-58591 Added more spec tests

* Merge from master

* Revert "Merge from master"

This reverts commit 50b0a92.

* MHV-58591 Fixed BB client creation and unit tests

* MHV-58591 Updated session TTL and fixed a spec test

* MHV-58591 Accidentally deleted a cassette

* MHV-58591 Fixed unit test

* MHV-58591 Refactored for better readability
  • Loading branch information
mmoyer-va authored Jul 8, 2024
1 parent a4084af commit 7844ab5
Show file tree
Hide file tree
Showing 19 changed files with 422 additions and 4 deletions.
20 changes: 20 additions & 0 deletions config/locales/exceptions.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,26 @@ en:
code: "MEDICALRECORDS_404"
detail: "The resource could not be found"
status: 404
BBINTERNAL_400:
<<: *external_defaults
code: "BBINTERNAL_400"
detail: "Upstream service responded with Bad Request"
status: 400
BBINTERNAL_401:
<<: *external_defaults
code: "BBINTERNAL_401"
detail: "Authentication failed on the upstream server"
status: 400
BBINTERNAL_403:
<<: *external_defaults
code: "BBINTERNAL_403"
detail: "This user is forbidden from performing requests on the upstream server"
status: 403
BBINTERNAL_404:
<<: *external_defaults
code: "BBINTERNAL_404"
detail: "The resource could not be found"
status: 404
HCA422:
<<: *external_defaults
code: 'HCA422'
Expand Down
6 changes: 6 additions & 0 deletions config/redis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ development: &defaults
medical_records_store:
namespace: mr-service
each_ttl: 1200
bb_internal_store:
namespace: bb-internal-service
each_ttl: 86400
mhv_mr_fhir_session_lock:
namespace: mhv-mr-fhir-session-lock
each_ttl: 10
mhv_session_lock:
namespace: mhv-session-lock
each_ttl: 10
mhv_mr_bb_session_lock:
namespace: mhv-mr-bb-session-lock
each_ttl: 10
mdot:
namespace: mdot
each_ttl: 1800
Expand Down
63 changes: 63 additions & 0 deletions lib/medical_records/bb_internal/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

require 'common/client/base'
require 'common/client/concerns/mhv_session_based_client'
require 'medical_records/bb_internal/client_session'
require 'medical_records/bb_internal/configuration'

module BBInternal
##
# Core class responsible for PHR Manager API interface operations
#
class Client < Common::Client::Base
include Common::Client::Concerns::MHVSessionBasedClient

configuration BBInternal::Configuration
client_session BBInternal::ClientSession

def list_radiology
response = perform(:get, "bluebutton/radiology/phrList/#{session.patient_id}", nil, token_headers)
response.body
end

private

##
# Override this to ensure a unique namespace for the redis lock.
#
def session_config_key
:mhv_mr_bb_session_lock
end

##
# Override MHVSessionBasedClient's method so we can get the patientId and store it as well.
#
def get_session
# Call MHVSessionBasedClient.get_session
@session = super

# Supplement session with patientId
session.patient_id = get_patient_id

session.save
session
end

def get_patient_id
response = perform(:get, "usermgmt/patient/uid/#{@session.user_id}", nil, token_headers)

patient_id = response.body['ipas']&.first&.dig('patientId')

raise Common::Exceptions::ServiceError.new(detail: 'Patient ID not found for user') if patient_id.blank?

patient_id
end

##
# Override MHVSessionBasedClient's method, because we need more control over the path.
#
def get_session_tagged
perform(:get, 'usermgmt/auth/session', nil, auth_headers)
end
end
end
14 changes: 14 additions & 0 deletions lib/medical_records/bb_internal/client_session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

require 'common/client/session'

module BBInternal
class ClientSession < Common::Client::Session
# attribute :icn, String
attribute :patient_id, String

redis_store REDIS_CONFIG[:bb_internal_store][:namespace]
redis_ttl 3600
redis_key :user_id
end
end
65 changes: 65 additions & 0 deletions lib/medical_records/bb_internal/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require 'common/client/configuration/rest'
require 'common/client/middleware/request/camelcase'
require 'common/client/middleware/request/multipart_request'
require 'common/client/middleware/response/json_parser'
require 'common/client/middleware/response/raise_custom_error'
require 'common/client/middleware/response/mhv_errors'
require 'common/client/middleware/response/snakecase'
require 'faraday/multipart'
require 'sm/middleware/response/sm_parser'

module BBInternal
##
# HTTP client configuration for {PHRMgr::Client}
#
class Configuration < Common::Client::Configuration::REST
##
# BB Internal uses the same app token as Rx.
# @return [String] Client token set in `settings.yml` via credstash
#
def app_token
Settings.mhv.rx.app_token
end

##
# BB Internal uses the same domain as Medical Records FHIR.
# @return [String] Base path for dependent URLs
#
def base_path
"#{Settings.mhv.medical_records.host}/mhvapi/v1/"
end

##
# @return [String] Service name to use in breakers and metrics
#
def service_name
'BBInternal'
end

##
# Creates a connection
#
# @return [Faraday::Connection] a Faraday connection instance
#
def connection
Faraday.new(base_path, headers: base_request_headers, request: request_options) do |conn|
conn.use :breakers
conn.request :multipart_request
conn.request :multipart
conn.request :json

# Uncomment this if you want curl 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.response :mhv_errors
conn.response :mhv_xml_html_errors
conn.response :json_parser
conn.adapter Faraday.default_adapter
end
end
end
end
10 changes: 10 additions & 0 deletions modules/my_health/app/controllers/my_health/mr_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'medical_records/phr_mgr/client'

module MyHealth
Expand All @@ -10,6 +11,7 @@ class MrController < ApplicationController
service_tag 'mhv-medical-records'

# skip_before_action :authenticate
before_action :authenticate_bb_client

rescue_from ::MedicalRecords::PatientNotFound do |_exception|
render body: nil, status: :accepted
Expand All @@ -26,6 +28,14 @@ def phrmgr_client
@phrmgr_client ||= PHRMgr::Client.new(current_user.icn)
end

def bb_client
@bb_client ||= BBInternal::Client.new(session: { user_id: current_user.mhv_correlation_id })
end

def authenticate_bb_client
bb_client.authenticate
end

def authorize
raise_access_denied unless current_user.authorize(:mhv_medical_records, :access?)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module MyHealth
module V1
module MedicalRecords
class RadiologyController < MrController
def index
resource = bb_client.list_radiology
render json: resource.to_json
end
end
end
end
end
11 changes: 7 additions & 4 deletions modules/my_health/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
MyHealth::Engine.routes.draw do
namespace :v1 do
scope :medical_records do
resources :session, only: %i[create], controller: 'medical_records/mr_session',
defaults: { format: :json } do
get :status, on: :collection
end
resources :vaccines, only: %i[index show], defaults: { format: :json } do
get :pdf, on: :collection
end
Expand All @@ -17,6 +13,13 @@
resources :conditions, only: %i[index show], defaults: { format: :json }
end

namespace :medical_records do
resources :session, only: %i[create], controller: 'mr_session', defaults: { format: :json } do
get :status, on: :collection
end
resources :radiology, only: %i[index], defaults: { format: :json }
end

scope :messaging do
resources :triage_teams, only: [:index], defaults: { format: :json }, path: 'recipients'
resources :all_triage_teams, only: [:index], defaults: { format: :json }, path: 'allrecipients'
Expand Down
2 changes: 2 additions & 0 deletions modules/my_health/spec/request/v1/allergies_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'support/shared_examples_for_mhv'

RSpec.describe 'Medical Records Integration', type: :request do
Expand All @@ -15,6 +16,7 @@

before do
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(authenticated_client)
sign_in_as(current_user)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'support/shared_examples_for_mhv'

RSpec.describe 'Medical Records Integration', type: :request do
Expand All @@ -15,6 +16,7 @@

before do
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(authenticated_client)
sign_in_as(current_user)
end

Expand Down
2 changes: 2 additions & 0 deletions modules/my_health/spec/request/v1/conditions_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'support/shared_examples_for_mhv'

RSpec.describe 'Medical Records Integration', type: :request do
Expand All @@ -15,6 +16,7 @@

before do
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(authenticated_client)
sign_in_as(current_user)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'support/shared_examples_for_mhv'

RSpec.describe 'Medical Records Integration', type: :request do
Expand All @@ -15,6 +16,7 @@

before do
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(authenticated_client)
sign_in_as(current_user)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'
require 'medical_records/phr_mgr/client'

RSpec.describe 'Medical Records Session', type: :request do
Expand All @@ -15,6 +16,7 @@

before do
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(authenticated_client)
allow(PHRMgr::Client).to receive(:new).and_return(PHRMgr::Client.new(12_345))
sign_in_as(current_user)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

require 'rails_helper'
require 'support/mr_client_helpers'
require 'medical_records/client'
require 'medical_records/bb_internal/client'

RSpec.describe 'Medical Records Session', type: :request do
include MedicalRecords::ClientHelpers
include SchemaMatchers

let(:va_patient) { true }
let(:mhv_account_type) { 'Premium' }
let(:current_user) { build(:user, :mhv, va_patient:, mhv_account_type:) }

before do
bb_internal_client = BBInternal::Client.new(
session: {
user_id: 15_176_497,
patient_id: '15176498',
expires_at: 1.hour.from_now,
token: 'SESSION_TOKEN'
}
)
allow(MedicalRecords::Client).to receive(:new).and_return(authenticated_client)
allow(BBInternal::Client).to receive(:new).and_return(bb_internal_client)
sign_in_as(current_user)
end

it 'responds to GET #index' do
VCR.use_cassette('mr_client/bb_internal/get_radiology') do
get '/my_health/v1/medical_records/radiology'
end

expect(response).to be_successful
expect(response.body).to be_a(String)
end

context 'when the patient ID is not found for a user profile' do
before do
allow_any_instance_of(BBInternal::Client).to receive(:list_radiology)
.and_raise(Common::Exceptions::ServiceError)
end

it 'returns a 500 response for GET #index' do
get '/my_health/v1/medical_records/radiology'
expect(response).to have_http_status(:internal_server_error)
end
end
end
Loading

0 comments on commit 7844ab5

Please sign in to comment.