Module: DaVinciCRDTestKit::GatherResponseGenerationData

Included in:
CustomServiceResponse, HookRequestEndpoint
Defined in:
lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb

Overview

Make requests to client’s FHIR server to use in building responses

Constant Summary collapse

REFERENCES_TO_READ_BY_RESOURCE_TYPE =
{
  Appointment: [
    'basedOn',
    'participant.actor'
  ],
  CommunicationRequest: [
    'requester',
    'sender',
    'recipient'
  ],
  DeviceRequest: [
    'requester',
    'performer'
  ],
  MedicationRequest: [
    'requester',
    'performer',
    'medicationReference',
    'dispenseRequest.performer'
  ],
  NutritionOrder: [
    'orderer'
  ],
  ServiceRequest: [
    'requester',
    'performer',
    'locationReference'
  ],
  VisionPrescription: [
    'prescriber'
  ],
  Encounter: [
    'participant.individual',
    'location.location',
    'serviceProvider'
  ],
  PractitionerRole: [
    'practitioner',
    'organization',
    'location'
  ]
}.freeze

Instance Method Summary collapse

Instance Method Details

#add_location_to_hash(location, location_hash) ⇒ Object



395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 395

def add_location_to_hash(location, location_hash)
  return unless location.is_a?(FHIR::Location) && location.id.present?

  relative_reference = "#{location.resourceType}/#{location.id}"
  return if location_hash.key?(relative_reference)

  location_hash[relative_reference] = location
  return unless location.partOf&.reference.present?

  parent_location = fetch_reference(location.partOf.reference,
                                    additional_tag: PARENT_LOCATION_FETCH_TAG,
                                    parse_as_fhir: true)
  add_location_to_hash(parent_location, location_hash)
end

#add_prefetch_resource_to_resource_hash(prefetched_resource, resource_hash) ⇒ Object



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 325

def add_prefetch_resource_to_resource_hash(prefetched_resource, resource_hash)
  if prefetched_resource['resourceType'] == 'Bundle'
    prefetched_resource['entry']&.each do |entry|
      next unless entry_has_required_details?(entry)

      one_resource = entry['resource']
      key = "#{one_resource['resourceType']}/#{one_resource['id']}"
      resource_hash[key] = one_resource
    end
  elsif resource_has_required_details?(prefetched_resource)

    key = "#{prefetched_resource['resourceType']}/#{prefetched_resource['id']}"
    resource_hash[key] = prefetched_resource
  end
end

#analyzed_resourcesObject



308
309
310
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 308

def analyzed_resources
  @analyzed_resources ||= {}
end

#data_request_headersObject



265
266
267
268
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 265

def data_request_headers
  @data_request_headers ||=
    { 'Authorization' => "Bearer #{request_body['fhirAuthorization']['access_token']}" }
end

#entry_has_required_details?(entry) ⇒ Boolean

Returns:

  • (Boolean)


341
342
343
344
345
346
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 341

def entry_has_required_details?(entry)
  entry.present? &&
    entry.is_a?(Hash) &&
    entry['resource'].present? &&
    resource_has_required_details?(entry['resource'])
end

#execute_request(query) ⇒ Object



270
271
272
273
274
275
276
277
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 270

def execute_request(query)
  fhir_server_connection&.get(query)
rescue Faraday::Error => e
  # Warning: This is a hack. If there is an error with the request such that we never get a response, we have
  #          no clean way to persist that information for the Inferno test to check later. The solution here
  #          is to persist the request anyway with a status of nil, using the error message as response body
  Faraday::Response.new(response_body: e.message, url: fhir_server_connection.url_prefix.to_s)
end

#extract_bundle_entries(bundle) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 133

def extract_bundle_entries(bundle)
  return [] unless bundle.present? && bundle.is_a?(Hash) && bundle['entry'].is_a?(Array)

  bundle['entry'].map do |entry|
    next unless entry.is_a?(Hash) && entry['resource'].is_a?(Hash)

    entry['resource']
  end.compact
end

#fetch_reference(reference, additional_tag: nil, parse_as_fhir: false) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 182

def fetch_reference(reference, additional_tag: nil, parse_as_fhir: false)
  response = execute_request(reference)
  return nil unless response.present?

  tags = [DATA_FETCH_TAG, hook_instance_data_fetch_tag]
  tags << additional_tag if additional_tag.present?
  persist_query_request(response, tags)
  return nil unless response.status.to_s.starts_with?('2')

  begin
    parse_as_fhir ? FHIR.from_contents(response.body) : JSON.parse(response.body)
  rescue JSON::ParserError
    nil
  end
end

#fhir_server_connectionObject



255
256
257
258
259
260
261
262
263
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 255

def fhir_server_connection
  @fhir_server_connection ||=
    if request_body['fhirServer'].present? &&
       request_body['fhirAuthorization'].present? &&
       request_body['fhirAuthorization']['access_token'].present?
      Faraday.new(url: request_body['fhirServer'], request: { open_timeout: 10 },
                  headers: data_request_headers)
    end
end

#find_coverage_for_requestObject



242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 242

def find_coverage_for_request
  resource =
    if prefetched_coverage.present?
      prefetched_coverage
    else
      query_for_coverages
    end

  return unless resource.is_a?(FHIR::Bundle)

  resource.entry&.first&.resource
end

#find_references_to_read(resource, to_read_list) ⇒ Object



198
199
200
201
202
203
204
205
206
207
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 198

def find_references_to_read(resource, to_read_list)
  references_to_read = REFERENCES_TO_READ_BY_RESOURCE_TYPE[resource['resourceType']&.to_sym]
  return [] unless references_to_read.present?

  references_to_read.each do |target_path|
    get_literal_reference_values(resource, target_path).each do |reference|
      to_read_list << reference unless to_read_list.include?(reference)
    end
  end
end

#gather_appointment_book_dataObject



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 53

def gather_appointment_book_data
  appointment_book_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  encounter_id = request_body.dig('context', 'encounterId')
  user_id = request_body.dig('context', 'userId')
  appointment_book_to_read << "Patient/#{patient_id}" if patient_id.present?
  appointment_book_to_read << "Encounter/#{encounter_id}" if encounter_id.present?
  appointment_book_to_read << user_id if user_id.present?

  appointment_book_to_analyze = extract_bundle_entries(request_body.dig('context', 'appointments'))

  gather_data_for_request(appointment_book_to_read, appointment_book_to_analyze)
end

#gather_data_for_request(to_read_list, to_analyze_list) ⇒ Object



143
144
145
146
147
148
149
150
151
152
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 143

def gather_data_for_request(to_read_list, to_analyze_list)
  to_analyze_list.each do |resource|
    next unless resource.present?

    find_references_to_read(resource, to_read_list)
    analyzed_resources["#{resource['resourceType']}/#{resource['id']}"] = resource
  end

  read_and_analyze(to_read_list.pop, to_read_list) until to_read_list.empty?
end

#gather_encounter_discharge_dataObject



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 79

def gather_encounter_discharge_data
  encounter_discharge_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  encounter_id = request_body.dig('context', 'encounterId')
  user_id = request_body.dig('context', 'userId')
  encounter_discharge_to_read << "Patient/#{patient_id}" if patient_id.present?
  encounter_discharge_to_read << "Encounter/#{encounter_id}" if encounter_id.present?
  encounter_discharge_to_read << user_id if user_id.present?

  gather_data_for_request(encounter_discharge_to_read, [])
end

#gather_encounter_start_dataObject



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 67

def gather_encounter_start_data
  encounter_start_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  encounter_id = request_body.dig('context', 'encounterId')
  user_id = request_body.dig('context', 'userId')
  encounter_start_to_read << "Patient/#{patient_id}" if patient_id.present?
  encounter_start_to_read << "Encounter/#{encounter_id}" if encounter_id.present?
  encounter_start_to_read << user_id if user_id.present?

  gather_data_for_request(encounter_start_to_read, [])
end

#gather_order_dispatch_dataObject



121
122
123
124
125
126
127
128
129
130
131
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 121

def gather_order_dispatch_data
  order_dispatch_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  order_id = request_body.dig('context', 'order')
  performer_id = request_body.dig('context', 'performer')
  order_dispatch_to_read << "Patient/#{patient_id}" if patient_id.present?
  order_dispatch_to_read << order_id if order_id.present?
  order_dispatch_to_read << performer_id if performer_id.present?

  gather_data_for_request(order_dispatch_to_read, [request_body.dig('context', 'task')].compact)
end

#gather_order_select_dataObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 91

def gather_order_select_data
  order_sign_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  encounter_id = request_body.dig('context', 'encounterId')
  user_id = request_body.dig('context', 'userId')
  order_sign_to_read << "Patient/#{patient_id}" if patient_id.present?
  order_sign_to_read << "Encounter/#{encounter_id}" if encounter_id.present?
  order_sign_to_read << user_id if user_id.present?

  order_sign_to_analyze = extract_bundle_entries(request_body.dig('context', 'draftOrders')).select do |resource|
    request_body.dig('context', 'selections').include?("#{resource['resourceType']}/#{resource['id']}")
  end

  gather_data_for_request(order_sign_to_read, order_sign_to_analyze)
end

#gather_order_sign_dataObject



107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 107

def gather_order_sign_data
  order_sign_to_read = []
  patient_id = request_body.dig('context', 'patientId')
  encounter_id = request_body.dig('context', 'encounterId')
  user_id = request_body.dig('context', 'userId')
  order_sign_to_read << "Patient/#{patient_id}" if patient_id.present?
  order_sign_to_read << "Encounter/#{encounter_id}" if encounter_id.present?
  order_sign_to_read << user_id if user_id.present?

  order_sign_to_analyze = extract_bundle_entries(request_body.dig('context', 'draftOrders'))

  gather_data_for_request(order_sign_to_read, order_sign_to_analyze)
end

#get_literal_reference_values(resource, path) ⇒ Object

Precondition: at most one level of nesting in the path



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 210

def get_literal_reference_values(resource, path)
  if path.include?('.')
    first_element, path = path.split('.')
    resource = resource[first_element]
  end

  reference_objects =
    case resource
    when Hash
      [resource[path]].flatten
    when Array
      resource.map { |entry| entry[path] }
    else
      []
    end

  reference_objects.map { |entry| entry['reference'] if entry.present? }.compact
end

#hook_instance_data_fetch_tagObject



49
50
51
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 49

def hook_instance_data_fetch_tag
  @hook_instance_data_fetch_tag ||= TagMethods.hook_instance_data_fetch_tag(request_body['hookInstance'])
end

#normalize_reference(reference) ⇒ Object

turn absolute references into relative if for the fhir server indicated in the request



172
173
174
175
176
177
178
179
180
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 172

def normalize_reference(reference)
  server = "#{request_body['fhirServer']&.chomp('/')}/"

  if request_body['fhirServer'].present? && reference.starts_with?(server)
    reference[server.length..]
  else
    reference
  end
end

#persist_query_request(response, tags) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 279

def persist_query_request(response, tags)
  inferno_request_headers = data_request_headers.map { |name, value| { name:, value: } }
  inferno_response_headers = response.headers&.map { |name, value| { name:, value: } }
  requests_repo.create(
    verb: 'GET',
    url: response.env.url.to_s,
    direction: 'outgoing',
    status: response.status,
    request_body: response.env.request_body,
    response_body: response.env.response_body,
    test_session_id: test_run.test_session_id,
    result_id: result.id,
    request_headers: inferno_request_headers,
    response_headers: inferno_response_headers,
    tags:
  )
end

#prefetched_coverageObject



233
234
235
236
237
238
239
240
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 233

def prefetched_coverage
  @prefetched_coverage ||=
    if request_body.dig('prefetch', 'coverage').present?
      FHIR.from_contents(request_body.dig('prefetch', 'coverage').to_json)
    elsif request_body.dig('prefetch', 'cov').present?
      FHIR.from_contents(request_body.dig('prefetch', 'cov').to_json)
    end
end

#prefetched_coverage_resourceObject



359
360
361
362
363
364
365
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 359

def prefetched_coverage_resource
  if prefetched_coverage.is_a?(FHIR::Bundle)
    prefetched_coverage.entry.first&.resource
  else
    prefetched_coverage
  end
end

#prefetched_location_bundleObject



380
381
382
383
384
385
386
387
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 380

def prefetched_location_bundle
  locations_data = request_body.dig('prefetch', 'locations') || request_body.dig('prefetch', 'locs')
  locations = FHIR.from_contents(locations_data.to_json) if locations_data.present?
  return locations if locations.is_a?(FHIR::Bundle)
  return nil unless locations.is_a?(FHIR::Location)

  FHIR::Bundle.new({ entry: [FHIR::Bundle::Entry.new({ resource: locations })] })
end

#prefetched_locations_and_parents_hashObject



389
390
391
392
393
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 389

def prefetched_locations_and_parents_hash
  prefetched_location_bundle&.entry&.each_with_object({}) do |entry, hash|
    add_location_to_hash(entry.resource, hash)
  end
end

#prefetched_resourcesObject



312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 312

def prefetched_resources
  @prefetched_resources ||=
    if request_body['prefetch'].blank? || !request_body['prefetch'].is_a?(Hash)
      {}
    else
      request_body['prefetch'].values.each_with_object({}) do |prefetched_resource, resource_hash|
        next unless prefetched_resource.is_a?(Hash)

        add_prefetch_resource_to_resource_hash(prefetched_resource, resource_hash)
      end
    end
end

#query_for_coveragesObject



297
298
299
300
301
302
303
304
305
306
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 297

def query_for_coverages
  query = "Coverage?patient=#{request_body.dig('context', 'patientId')}&status=active"
  response = execute_request(query)
  return nil unless response.present?

  persist_query_request(response, [DATA_FETCH_TAG, hook_instance_data_fetch_tag])
  return nil unless response.status.to_s.starts_with?('2')

  FHIR.from_contents(response.body)
end

#read_and_analyze(reference, to_read_list) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 154

def read_and_analyze(reference, to_read_list)
  normalized_reference = normalize_reference(reference)
  return if analyzed_resources.key?(normalized_reference)

  resource =
    if prefetched_resources.key?(normalized_reference)
      prefetched_resources[normalized_reference]
    else
      fetch_reference(normalized_reference)
    end
  analyzed_resources[normalized_reference] = resource
  return unless resource.present?

  find_references_to_read(resource, to_read_list)
end

#request_additional_fhir_dataObject

gather additional fhir resources not requested via prefetch not used for response generation, but verified later



354
355
356
357
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 354

def request_additional_fhir_data
  request_coverage_payer
  request_parent_locations
end

#request_coverageObject



229
230
231
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 229

def request_coverage
  @request_coverage ||= find_coverage_for_request
end

#request_coverage_payerObject



367
368
369
370
371
372
373
374
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 367

def request_coverage_payer
  return unless prefetched_coverage.present?

  coverage_resource = prefetched_coverage_resource
  return unless coverage_resource.present? && coverage_resource.payor.first&.reference.present?

  fetch_reference(coverage_resource.payor.first.reference, additional_tag: PAYER_ORG_FETCH_TAG)
end

#request_parent_locationsObject



376
377
378
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 376

def request_parent_locations
  prefetched_locations_and_parents_hash
end

#resource_has_required_details?(resource) ⇒ Boolean

Returns:

  • (Boolean)


348
349
350
# File 'lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb', line 348

def resource_has_required_details?(resource)
  resource.is_a?(Hash) && resource['resourceType'].present? && resource['id'].present?
end