Module: DaVinciCRDTestKit::CustomServiceResponse

Includes:
FhirpathOnCDSRequest, GatherResponseGenerationData, ReplaceTokens
Included in:
HookRequestEndpoint
Defined in:
lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb

Overview

Build responses using tester-provided template

Constant Summary

Constants included from GatherResponseGenerationData

GatherResponseGenerationData::REFERENCES_TO_READ_BY_RESOURCE_TYPE

Constants included from FhirpathOnCDSRequest

FhirpathOnCDSRequest::SUPPORTED_POST_RESOLVE_FUNCTIONS, FhirpathOnCDSRequest::TODAY_EXPRESSION_PATTERN

Instance Method Summary collapse

Methods included from GatherResponseGenerationData

#add_location_to_hash, #add_prefetch_resource_to_resource_hash, #analyzed_resources, #data_request_headers, #entry_has_required_details?, #execute_request, #extract_bundle_entries, #fetch_reference, #fhir_server_connection, #find_coverage_for_request, #find_references_to_read, #gather_appointment_book_data, #gather_data_for_request, #gather_encounter_discharge_data, #gather_encounter_start_data, #gather_order_dispatch_data, #gather_order_select_data, #gather_order_sign_data, #get_literal_reference_values, #hook_instance_data_fetch_tag, #normalize_reference, #persist_query_request, #prefetched_coverage, #prefetched_coverage_resource, #prefetched_location_bundle, #prefetched_locations_and_parents_hash, #prefetched_resources, #query_for_coverages, #read_and_analyze, #request_additional_fhir_data, #request_coverage, #request_coverage_payer, #request_parent_locations, #resource_has_required_details?

Methods included from ReplaceTokens

#replace_tokens, #replace_tokens_in_string

Methods included from FhirpathOnCDSRequest

#execute_fhirpath_on_cds_request

Instance Method Details

#add_first_default(object, list_element) ⇒ Object



219
220
221
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 219

def add_first_default(object, list_element)
  object[list_element] << get_extension_value(object, default_extension_name(list_element)).first
end

#build_custom_hook_responseObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 35

def build_custom_hook_response
  hook_response = parsed_user_input
  return nil unless hook_response.present?

  # filter cards and actions
  filter_response(hook_response)

  # update cards and actions
  finalize_card_list(hook_response)
  hook_response['systemActions'] = instantiate_actions(hook_response, 'systemActions')

  hook_response
rescue FhirpathServiceError => e
  error_response(
    'FHIRPath service error while generating custom response. ' \
    'Check the FHIRPath expressions and data in your response template and hook request. ' \
    "Details: #{e.message}",
    code: 500,
    outcome_code: 'processing'
  )
  nil
end

#create_instantiated_action_from_target(action, target_resource) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 124

def create_instantiated_action_from_target(action, target_resource)
  instantiated_action = JSON.parse(action.to_json)
  if instantiated_action['type'] == 'delete'
    instantiated_action['resourceId'] = "#{target_resource['resourceType']}/#{target_resource['id']}"
  elsif instantiated_action['resource'].is_a?(Hash)
    # merge action's resource into the target resource.
    # Merge extension lists, keeping duplicate url from action's resource,
    # otherwise replace (top-level elements)
    instantiated_action['resource'] =
      merge_action_resource_into_target(instantiated_action['resource'], target_resource)

    coverage_information_ext = instantiated_action['resource']['extension']&.find do |ext|
      ext['url'] == 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/ext-coverage-information'
    end
    default_coverage_information_elements(coverage_information_ext) if coverage_information_ext.present?
  else
    instantiated_action['resource'] = target_resource
  end

  instantiated_action
end

#custom_response_templateObject



12
13
14
15
16
17
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 12

def custom_response_template
  @custom_response_template ||=
    JSON.parse(result.input_json)
      .find { |input| input['name'].include?('custom_response_template') }
      &.dig('value')
end

#default_coverage_information_assertion_id(coverage_info_ext) ⇒ Object



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

def default_coverage_information_assertion_id(coverage_info_ext)
  existing_assertion_id_ext = coverage_info_ext['extension']&.find { |ext| ext['url'] == 'coverage-assertion-id' }
  assertion_id_ext =
    if existing_assertion_id_ext.blank?
      new_assertion_id_ext = { url: 'coverage-assertion-id' }
      coverage_info_ext['extension'] << new_assertion_id_ext
      new_assertion_id_ext
    else
      existing_assertion_id_ext
    end

  return unless assertion_id_ext['valueString'].blank?

  assertion_id_ext['valueString'] = SecureRandom.hex(32)
end

#default_coverage_information_coverage(coverage_info_ext) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 293

def default_coverage_information_coverage(coverage_info_ext)
  existing_coverage_ext = coverage_info_ext['extension']&.find { |ext| ext['url'] == 'coverage' }
  coverage_ext =
    if existing_coverage_ext.blank?
      new_coverage_ext = { url: 'coverage' }
      coverage_info_ext['extension'] << new_coverage_ext
      new_coverage_ext
    else
      existing_coverage_ext
    end

  return unless coverage_ext['valueReference'].blank? || coverage_ext['valueReference']['reference'].blank?

  coverage_reference = "Coverage/#{request_coverage&.id}"
  coverage_ext['valueReference'] = { reference: coverage_reference }
end

#default_coverage_information_date(coverage_info_ext) ⇒ Object



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

def default_coverage_information_date(coverage_info_ext)
  existing_date_ext = coverage_info_ext['extension']&.find { |ext| ext['url'] == 'date' }
  date_ext =
    if existing_date_ext.blank?
      new_date_ext = { url: 'date' }
      coverage_info_ext['extension'] << new_date_ext
      new_date_ext
    else
      existing_date_ext
    end

  return unless date_ext['valueDate'].blank?

  date_ext['valueDate'] = Time.now.utc.strftime('%Y-%m-%d')
end

#default_coverage_information_elements(coverage_info_ext) ⇒ Object



287
288
289
290
291
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 287

def default_coverage_information_elements(coverage_info_ext)
  default_coverage_information_coverage(coverage_info_ext)
  default_coverage_information_date(coverage_info_ext)
  default_coverage_information_assertion_id(coverage_info_ext)
end

#default_extension_name(list_element) ⇒ Object



203
204
205
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 203

def default_extension_name(list_element)
  "com.inferno.internal.default#{list_element.capitalize}"
end

#defaults_extension?(object, list_element) ⇒ Boolean

Returns:

  • (Boolean)


207
208
209
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 207

def defaults_extension?(object, list_element)
  get_defaults_extension_value(object, list_element).present?
end

#define_extension(parent, extension, value) ⇒ Object



269
270
271
272
273
274
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 269

def define_extension(parent, extension, value)
  parent['extension'] = {} if parent['extension'].nil?
  return if parent['extension'][extension].present?

  parent['extension'][extension] = value
end

#enabled_on_service?(object) ⇒ Boolean

Returns:

  • (Boolean)


192
193
194
195
196
197
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 192

def enabled_on_service?(object)
  services = get_extension_value(object, 'com.inferno.includeForServices')
  return true unless services.present?

  services.split(',').map(&:strip).any? { |url| request.env['PATH_INFO'].include?(url) }
end

#evaluate_inclusion_for_request(inclusion_criteria) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 251

def evaluate_inclusion_for_request(inclusion_criteria)
  return false if inclusion_criteria == 'default'

  result = execute_fhirpath_on_cds_request(request_body, inclusion_criteria)

  if result.empty? || result.length > 1
    false
  elsif [true, false].include?(result[0])
    result[0]
  else
    result[0] != 'false' # single non-false entry is always true for fhirpath
  end
end

#filter_and_separate_defaults(parent, list_element) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 166

def filter_and_separate_defaults(parent, list_element)
  defaults_ext = default_extension_name(list_element)
  define_extension(parent, defaults_ext, [])
  parent[list_element]&.select! do |object|
    next false unless enabled_on_service?(object)

    remove_extension(object, 'com.inferno.includeForServices')

    if object_has_inclusion_criteria?(object)
      if object_is_a_default?(object)
        remove_inclusion_criteria(object)
        parent['extension'][defaults_ext] << object

        false
      elsif object_included_for_this_request?(object)
        remove_inclusion_criteria(object)
        true
      else
        false
      end
    else
      true
    end
  end
end

#filter_response(hook_response) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 158

def filter_response(hook_response)
  filter_and_separate_defaults(hook_response, 'cards')
  hook_response['cards']&.each do |card|
    card['suggestions']&.each { |suggestion| filter_and_separate_defaults(suggestion, 'actions') }
  end
  filter_and_separate_defaults(hook_response, 'systemActions')
end

#finalize_card_list(hook_response) ⇒ Object



58
59
60
61
62
63
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 58

def finalize_card_list(hook_response)
  add_first_default(hook_response, 'cards') if hook_response['cards'].blank? && defaults_extension?(hook_response,
                                                                                                    'cards')
  remove_defaults_extension(hook_response, 'cards')
  hook_response['cards']&.each { |card| update_card(card) }
end

#get_defaults_extension_value(object, list_element) ⇒ Object



215
216
217
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 215

def get_defaults_extension_value(object, list_element)
  get_extension_value(object, default_extension_name(list_element))
end

#get_extension_value(parent, extension) ⇒ Object



283
284
285
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 283

def get_extension_value(parent, extension)
  parent.dig('extension', extension)
end

#get_object_inclusion_criteria_extension_value(object) ⇒ Object



227
228
229
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 227

def get_object_inclusion_criteria_extension_value(object)
  get_extension_value(object, 'com.inferno.inclusionCriteria')
end

#get_object_resource_selection_criteria_extension_value(object) ⇒ Object



239
240
241
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 239

def get_object_resource_selection_criteria_extension_value(object)
  get_extension_value(object, 'com.inferno.resourceSelectionCriteria')
end

#get_target_resources_for_request(resource_selection_criteria) ⇒ Object



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

def get_target_resources_for_request(resource_selection_criteria)
  execute_fhirpath_on_cds_request(request_body, resource_selection_criteria)
end

#instantiate_action_using_targets(action, targets, instantiated_action_list, instantiated_resources_list, default_action) ⇒ Object



113
114
115
116
117
118
119
120
121
122
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 113

def instantiate_action_using_targets(action, targets, instantiated_action_list, instantiated_resources_list,
                                     default_action)
  targets.each do |target|
    target_reference = "#{target['resourceType']}/#{target['id']}"
    next if default_action && instantiated_resources_list.include?(target_reference)

    instantiated_resources_list << target_reference unless instantiated_resources_list.include?(target_reference)
    instantiated_action_list << create_instantiated_action_from_target(action, target)
  end
end

#instantiate_actions(parent, list_element) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 81

def instantiate_actions(parent, list_element)
  actions = parent[list_element]
  default_actions = get_defaults_extension_value(parent, list_element)
  remove_defaults_extension(parent, list_element)

  return [] unless actions.present? || default_actions.present?

  instantiated_action_list = []
  instantiated_resources_list = []
  actions.each { |action| instantiate_an_action(action, instantiated_action_list, instantiated_resources_list) }
  default_actions.each do |action|
    instantiate_an_action(action, instantiated_action_list, instantiated_resources_list,
                          default_action: true)
  end

  instantiated_action_list
end

#instantiate_an_action(action, instantiated_action_list, instantiated_resources_list, default_action: false) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 99

def instantiate_an_action(action, instantiated_action_list, instantiated_resources_list,
                          default_action: false)
  replace_tokens(action, request_body)

  if object_has_resource_selection_criteria?(action)
    targets = get_target_resources_for_request(get_object_resource_selection_criteria_extension_value(action))
    remove_resource_selection_criteria(action)
    instantiate_action_using_targets(action, targets, instantiated_action_list, instantiated_resources_list,
                                     default_action)
  elsif !default_action || instantiated_action_list.blank?
    instantiated_action_list << action
  end
end

#merge_action_resource_into_target(action_resource, target_resource) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 146

def merge_action_resource_into_target(action_resource, target_resource)
  target_resource.merge(action_resource) do |key, old_value, new_value|
    if key == 'extension'
      old_value.select do |existing_extention|
        new_value.find { |incoming_extension| existing_extention['url'] == incoming_extension['url'] }.blank?
      end + new_value
    else
      new_value
    end
  end
end

#object_has_inclusion_criteria?(object) ⇒ Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 223

def object_has_inclusion_criteria?(object)
  get_object_inclusion_criteria_extension_value(object).present?
end

#object_has_resource_selection_criteria?(object) ⇒ Boolean

Returns:

  • (Boolean)


235
236
237
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 235

def object_has_resource_selection_criteria?(object)
  get_object_resource_selection_criteria_extension_value(object).present?
end

#object_included_for_this_request?(object) ⇒ Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 247

def object_included_for_this_request?(object)
  evaluate_inclusion_for_request(get_object_inclusion_criteria_extension_value(object))
end

#object_is_a_default?(object) ⇒ Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 199

def object_is_a_default?(object)
  get_object_inclusion_criteria_extension_value(object) == 'default'
end

#parsed_user_inputObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 19

def parsed_user_input
  if custom_response_template.blank?
    error_response('No custom template provided for custom Inferno CRD response',
                   code: 500,
                   outcome_code: 'processing')
    nil
  else
    JSON.parse(custom_response_template)
  end
rescue JSON::ParserError
  error_response('Invalid template provided for custom Inferno CRD response: invalid JSON',
                 code: 500,
                 outcome_code: 'processing')
  nil
end

#remove_defaults_extension(object, list_element) ⇒ Object



211
212
213
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 211

def remove_defaults_extension(object, list_element)
  remove_extension(object, default_extension_name(list_element))
end

#remove_extension(parent, extension) ⇒ Object



276
277
278
279
280
281
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 276

def remove_extension(parent, extension)
  parent['extension']&.delete(extension)
  return unless parent['extension'].blank?

  parent.delete('extension')
end

#remove_inclusion_criteria(object) ⇒ Object



231
232
233
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 231

def remove_inclusion_criteria(object)
  remove_extension(object, 'com.inferno.inclusionCriteria')
end

#remove_resource_selection_criteria(object) ⇒ Object



243
244
245
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 243

def remove_resource_selection_criteria(object)
  remove_extension(object, 'com.inferno.resourceSelectionCriteria')
end

#update_card(card) ⇒ Object



65
66
67
68
69
70
71
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 65

def update_card(card)
  card['uuid'] = SecureRandom.uuid if card['uuid'].present?

  return unless card['suggestions'].is_a?(Array)

  card['suggestions'].each { |suggestion| update_suggestion(suggestion) }
end

#update_suggestion(suggestion) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb', line 73

def update_suggestion(suggestion)
  suggestion['uuid'] = SecureRandom.uuid if suggestion['uuid'].present?

  return unless suggestion['actions'].is_a?(Array)

  suggestion['actions'] = instantiate_actions(suggestion, 'actions')
end