Module: Common::Client::Concerns::MhvFhirSessionClient
- Extended by:
- ActiveSupport::Concern
- Includes:
- MHVJwtSessionClient
- Included in:
- MedicalRecords::Client
- Defined in:
- lib/common/client/concerns/mhv_fhir_session_client.rb
Overview
Module mixin for overriding session logic when making MHV JWT/FHIR-based client connections.
All refrences to “session” in this module refer to the upstream MHV/FHIR session.
Constant Summary collapse
- LOCK_RETRY_DELAY =
Number of seconds to wait between attempts to acquire a session lock
1
- RETRY_ATTEMPTS =
How many times to attempt to acquire a session lock
10
Instance Attribute Summary collapse
-
#session ⇒ Hash
readonly
A hash containing session information.
Instance Method Summary collapse
-
#fetch_patient_by_subject_id(fhir_client, subject_id) ⇒ FHIR::Patient
private
Fetch a Patient FHIR record by subjectId.
-
#get_patient_fhir_id(jwt_token) ⇒ String
private
Decodes a JWT to get the patient’s subjectId, then uses that to fetch their FHIR ID.
-
#get_session ⇒ MedicalRecords::ClientSession
protected
Creates and saves an MHV/FHIR session for a patient.
- #incomplete?(session) ⇒ Boolean protected
- #invalid?(session) ⇒ Boolean protected
-
#perform_phr_refresh ⇒ DateTime
private
Checks to see if a PHR refresh is necessary, performs the refresh, and updates the refresh timestamp.
-
#save_session ⇒ MedicalRecords::ClientSession
private
Takes information from the session variable and saves a new session instance in redis.
Methods included from MHVJwtSessionClient
#auth_body, #auth_headers, #decode_jwt_token, #extract_token_expiration, #get_jwt_from_headers, #get_session_tagged, #jwt_bearer_token, #patient_fhir_id, #session_config_key, #user_key, #validate_session_params
Methods included from MhvLockedSessionClient
#authenticate, #initialize, #lock_and_get_session, #obtain_redis_lock, #refresh_session, #release_redis_lock
Methods included from SentryLogging
#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata
Instance Attribute Details
#session ⇒ Hash (readonly)
Returns a hash containing session information.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 19 module MhvFhirSessionClient extend ActiveSupport::Concern include MHVJwtSessionClient protected LOCK_RETRY_DELAY = 1 # Number of seconds to wait between attempts to acquire a session lock RETRY_ATTEMPTS = 10 # How many times to attempt to acquire a session lock def incomplete?(session) session.icn.blank? || session.patient_fhir_id.blank? || session.token.blank? || session.expires_at.blank? end def invalid?(session) session.expired? || incomplete?(session) end ## # Creates and saves an MHV/FHIR session for a patient. If any step along the way fails, save # the partial session before raising the exception. # # @return [MedicalRecords::ClientSession] if a MR (Medical Records) client session # def get_session exception = nil perform_phr_refresh begin # Call MHVJwtSessionClient's get_session method for JWT-session creation. jwt_session = super session.token = jwt_session.token session.expires_at = jwt_session.expires_at rescue => e exception ||= e end begin get_patient_fhir_id(session.token) if session.token && patient_fhir_id.nil? rescue MedicalRecords::PatientNotFound # Don't raise this exception, as it will bubble up to a shared class where we don't # want to handle it. Instead we simply don't set the Patient FHIR ID and raise an # exception later in medical_records/client.rb where it will be easier to handle. rescue => e exception ||= e end new_session = save_session raise exception if exception new_session end private ## # Checks to see if a PHR refresh is necessary, performs the refresh, and updates the refresh timestamp. # # @return [DateTime] the refresh timestamp # def perform_phr_refresh return unless session.refresh_time.nil? # Perform an async PHR refresh for the user. This job will not raise any errors, it only logs them. MHV::PhrUpdateJob.perform_async(session.icn, session.user_id) # Record that the refresh has happened for this session. Don't run this more than once per session duration. session.refresh_time = DateTime.now end ## # Decodes a JWT to get the patient's subjectId, then uses that to fetch their FHIR ID # # @param jwt_token [String] a JWT token # @return [String] the patient's FHIR ID # def get_patient_fhir_id(jwt_token) decoded_token = decode_jwt_token(jwt_token) session.patient_fhir_id = fetch_patient_by_subject_id(sessionless_fhir_client(jwt_token), decoded_token[0]['subjectId']) end ## # Fetch a Patient FHIR record by subjectId # # @param fhir_client [FHIR::Client] a FHIR client with which to get the record # @param subject_id [String] the patient's subjectId from the JWT # @return [FHIR::Patient] the patient's FHIR record # def fetch_patient_by_subject_id(fhir_client, subject_id) raise Common::Exceptions::Unauthorized, detail: 'JWT token does not contain subjectId' if subject_id.nil? # Get the patient's FHIR ID patient = get_patient_by_identifier(fhir_client, subject_id) if patient.nil? || patient.entry.empty? || !patient.entry[0].resource.respond_to?(:id) raise Common::Exceptions::Unauthorized, detail: 'Patient record not found or does not contain a valid FHIR ID' end session.patient_fhir_id = patient.entry[0].resource.id end ## # Takes information from the session variable and saves a new session instance in redis. # # @return [MedicalRecords::ClientSession] the updated session # def save_session new_session = @session.class.new(user_id: session.user_id.to_s, patient_fhir_id: session.patient_fhir_id, icn: session.icn, expires_at: session.expires_at, token: session.token, refresh_time: session.refresh_time) new_session.save new_session end end |
Instance Method Details
#fetch_patient_by_subject_id(fhir_client, subject_id) ⇒ FHIR::Patient (private)
Fetch a Patient FHIR record by subjectId
107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 107 def fetch_patient_by_subject_id(fhir_client, subject_id) raise Common::Exceptions::Unauthorized, detail: 'JWT token does not contain subjectId' if subject_id.nil? # Get the patient's FHIR ID patient = get_patient_by_identifier(fhir_client, subject_id) if patient.nil? || patient.entry.empty? || !patient.entry[0].resource.respond_to?(:id) raise Common::Exceptions::Unauthorized, detail: 'Patient record not found or does not contain a valid FHIR ID' end session.patient_fhir_id = patient.entry[0].resource.id end |
#get_patient_fhir_id(jwt_token) ⇒ String (private)
Decodes a JWT to get the patient’s subjectId, then uses that to fetch their FHIR ID
94 95 96 97 98 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 94 def get_patient_fhir_id(jwt_token) decoded_token = decode_jwt_token(jwt_token) session.patient_fhir_id = fetch_patient_by_subject_id(sessionless_fhir_client(jwt_token), decoded_token[0]['subjectId']) end |
#get_session ⇒ MedicalRecords::ClientSession (protected)
Creates and saves an MHV/FHIR session for a patient. If any step along the way fails, save the partial session before raising the exception.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 42 def get_session exception = nil perform_phr_refresh begin # Call MHVJwtSessionClient's get_session method for JWT-session creation. jwt_session = super session.token = jwt_session.token session.expires_at = jwt_session.expires_at rescue => e exception ||= e end begin get_patient_fhir_id(session.token) if session.token && patient_fhir_id.nil? rescue MedicalRecords::PatientNotFound # Don't raise this exception, as it will bubble up to a shared class where we don't # want to handle it. Instead we simply don't set the Patient FHIR ID and raise an # exception later in medical_records/client.rb where it will be easier to handle. rescue => e exception ||= e end new_session = save_session raise exception if exception new_session end |
#incomplete?(session) ⇒ Boolean (protected)
28 29 30 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 28 def incomplete?(session) session.icn.blank? || session.patient_fhir_id.blank? || session.token.blank? || session.expires_at.blank? end |
#invalid?(session) ⇒ Boolean (protected)
32 33 34 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 32 def invalid?(session) session.expired? || incomplete?(session) end |
#perform_phr_refresh ⇒ DateTime (private)
Checks to see if a PHR refresh is necessary, performs the refresh, and updates the refresh timestamp.
79 80 81 82 83 84 85 86 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 79 def perform_phr_refresh return unless session.refresh_time.nil? # Perform an async PHR refresh for the user. This job will not raise any errors, it only logs them. MHV::PhrUpdateJob.perform_async(session.icn, session.user_id) # Record that the refresh has happened for this session. Don't run this more than once per session duration. session.refresh_time = DateTime.now end |
#save_session ⇒ MedicalRecords::ClientSession (private)
Takes information from the session variable and saves a new session instance in redis.
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/common/client/concerns/mhv_fhir_session_client.rb', line 124 def save_session new_session = @session.class.new(user_id: session.user_id.to_s, patient_fhir_id: session.patient_fhir_id, icn: session.icn, expires_at: session.expires_at, token: session.token, refresh_time: session.refresh_time) new_session.save new_session end |