Class: DaVinciCRDTestKit::HookRequestEndpoint

Inherits:
Inferno::DSL::SuiteEndpoint
  • Object
show all
Includes:
CardsIdentification, CustomServiceResponse, GatherResponseGenerationData, MockServiceResponse
Defined in:
lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb

Constant Summary collapse

AVAILABLE_HOOKS =
[
  'appointment-book',
  'encounter-start',
  'encounter-discharge',
  'order-select',
  'order-sign',
  'order-dispatch'
].freeze

Constants included from CardsIdentification

CardsIdentification::ADDITIONAL_ORDERS_EXPECTED_RESOURCE_TYPES, CardsIdentification::ADDITIONAL_ORDERS_RESPONSE_TYPE, CardsIdentification::COVERAGE_INFORMATION_RESPONSE_TYPE, CardsIdentification::COVERAGE_INFO_CONFIGURATION_CODE, CardsIdentification::COVERAGE_INFO_EXPECTED_RESOURCE_TYPES, CardsIdentification::COVERAGE_INFO_EXT_URL, CardsIdentification::CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE, CardsIdentification::EXTERNAL_REFERENCE_RESPONSE_TYPE, CardsIdentification::FORM_COMPLETION_RESPONSE_TYPE, CardsIdentification::INSTRUCTIONS_RESPONSE_TYPE, CardsIdentification::LAUNCH_SMART_APP_RESPONSE_TYPE, CardsIdentification::PROPOSE_ALTERNATIVE_REQUEST_EXPECTED_RESOURCE_TYPES, CardsIdentification::PROPOSE_ALTERNATIVE_REQUEST_RESPONSE_TYPE

Constants included from ProfilesAndResourceTypes

ProfilesAndResourceTypes::ORDER_OR_ENCOUNTER_RESOURCE_CLASSES, ProfilesAndResourceTypes::ORDER_RESOURCE_CLASSES, ProfilesAndResourceTypes::ORDER_RESOURCE_TYPES

Constants included from RequestsLogicalModelValidation

RequestsLogicalModelValidation::CRD_CDS_HOOK_REQUEST_MODEL_URL, RequestsLogicalModelValidation::PERFORMER_ALLOWED_RESOURCE_TYPES, RequestsLogicalModelValidation::USER_ID_ALLOWED_RESOURCE_TYPES

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 CardsIdentification

#additional_orders_response_type?, #cache_sorted_cards, #check_action_type, #coverage_info_card_type?, #coverage_info_configuration_disabled?, #coverage_info_content, #coverage_info_response?, #coverage_info_system_action_type?, #coverage_information_response_type?, #create_or_update_coverage_action_response_type?, #create_or_update_coverage_card_response_type?, #create_questionnaire_action_response_type?, #disable_coverage_info_configuration!, #extension_url, #external_reference_response_type?, #extract_coverage_information_extensions, #form_completion_action_response_type?, #form_completion_card_response_type?, #form_completion_task_questionnaire?, #hook_instances_from_requests, #identify_action_type, #identify_card_type, #initialize_sorted_cards_hash, #instructions_response_type?, #launch_smart_app_response_type?, #list_card_types_in_requests, #propose_alternative_request_response_type?, #sort_card_types_from_request, #sorted_cards_cached?, #sorted_cards_from_cache, #sorted_cards_from_requests

Methods included from HookRequestFieldValidation

#hook_request_context_check, #hook_request_optional_fields_check, #hook_request_prefetch_check, #hook_request_required_fields_check, #json_parse, #no_error_validation

Methods included from ProfilesAndResourceTypes

#structure_definition_map, #structure_definition_map_v201, #structure_definition_map_v221

Methods included from ServerBaseURLs

#client_fhir_base_url, #fhir_url, #instance_url, #search_url

Methods included from BaseURLs

#inferno_base_url, #resume_fail_url, #resume_pass_url

Methods included from RequestsLogicalModelValidation

#validate_request_against_logical_model

Methods included from LogicalModelsOverrideHelper

#allowed_resource_type?, #check_appointment_conformance, #check_order_like_resource_conformance, #check_resource_conformance_to_coverage_profile, #check_resource_conformance_to_order_or_encounter_profile, #check_resource_conformance_to_order_profile, #check_resource_conformance_to_questionnaire_task_profile, #check_resource_type_and_validate, #local_reference?, #manually_check_appointment_validation_errors, #parse_action_resource, #primary_performer_type?, #referenced_resource_present_in_bundle?, #reject_resource_issues, #resolved_participant_patient_slice_issue?, #resolved_participant_primary_performer_slice_issue?

Methods included from SuggestionActionsValidation

#action_fields_validation, #action_resource_type_check, #actions_check

Methods included from CustomServiceResponse

#add_first_default, #build_custom_hook_response, #create_instantiated_action_from_target, #custom_response_template, #default_coverage_information_assertion_id, #default_coverage_information_coverage, #default_coverage_information_date, #default_coverage_information_elements, #default_extension_name, #defaults_extension?, #define_extension, #enabled_on_service?, #evaluate_inclusion_for_request, #filter_and_separate_defaults, #filter_response, #finalize_card_list, #get_defaults_extension_value, #get_extension_value, #get_object_inclusion_criteria_extension_value, #get_object_resource_selection_criteria_extension_value, #get_target_resources_for_request, #instantiate_action_using_targets, #instantiate_actions, #instantiate_an_action, #merge_action_resource_into_target, #object_has_inclusion_criteria?, #object_has_resource_selection_criteria?, #object_included_for_this_request?, #object_is_a_default?, #parsed_user_input, #remove_defaults_extension, #remove_extension, #remove_inclusion_criteria, #remove_resource_selection_criteria, #update_card, #update_suggestion

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

Methods included from MockServiceResponse

#add_basic_cards, #add_coverage_cards, #add_coverage_cards?, #add_coverage_extension, #add_order_hook_cards, #build_mock_hook_response, #context, #coverage_information_required?, #coverage_information_required_hooks, #create_alternate_request_card, #create_card_response, #create_cards_and_system_actions, #create_companions_prerequisites_card, #create_coverage_extension_system_actions, #create_coverage_resource, #create_form_completion_card, #create_or_update_coverage, #create_system_actions, #create_warning_messages, #current_time, #extract_prefetched_coverage, #find_entry_in_prefetch_bundle, #find_one_prefetched_order_dispatch_order, #find_prefetched_order_dispatch_orders, #format_missing_response_types, #get_context_resource, #get_missing_response_types, #get_patient_coverage, #identify_alternate_order_resource, #identify_resources_for_system_actions, #load_json_file, #make_resource_request, #missing_response_type_filter, #patient_coverage, #payer_reference_from_coverage, #prefetch_key_for_system_actions, #resource_to_update_field_name, #resource_type_to_update, #selected_response_types, #update_one_card_info, #update_service_request, #update_specific_hook_card_info

Instance Method Details

#apply_hook_configuration(response_body) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 139

def apply_hook_configuration(response_body)
  return response_body unless response_body.present? && coverage_info_disabled?

  cards = response_body['cards']
  response_body['cards'] = cards.reject { |card| coverage_info_card_type?(card) } if cards.is_a?(Array)

  system_actions = response_body['systemActions']
  if system_actions.is_a?(Array)
    response_body['systemActions'] = system_actions.reject { |action| coverage_info_system_action_type?(action) }
  end

  response_body
end

#coverage_info_disabled?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 153

def coverage_info_disabled?
  request_body.dig('extension', 'davinci-crd.configuration', COVERAGE_INFO_CONFIGURATION_CODE) == false
end

#error_operation_outcome(code, text) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 191

def error_operation_outcome(code, text)
  {
    resourceType: 'OperationOutcome',
    issue: [
      {
        severity: 'error',
        code:,
        details: {
          text:
        }
      }
    ]
  }
end

#error_response(error_message, code: 400, outcome_code: 'invalid') ⇒ Object



184
185
186
187
188
189
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 184

def error_response(error_message, code: 400, outcome_code: 'invalid')
  response.status = code
  response.body = error_operation_outcome(outcome_code, error_message).to_json
  response.headers.merge!({ 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' })
  response.format = :json
end

#hook_instance_already_used?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 157

def hook_instance_already_used?
  requests_repo.tagged_requests(test_run.test_session_id, [hook_instance_tag]).present?
end

#hook_instance_tagObject



172
173
174
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 172

def hook_instance_tag
  TagMethods.hook_instance_tag(request_body['hookInstance'])
end

#hook_or_group_tagObject



176
177
178
179
180
181
182
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 176

def hook_or_group_tag
  if test.config.options[:crd_test_group].present?
    test.config.options[:crd_test_group]
  else
    DaVinciCRDTestKit.const_get(:"#{name.upcase}_TAG")
  end
end

#hook_responseObject



128
129
130
131
132
133
134
135
136
137
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 128

def hook_response
  if response_approach == 'custom'
    build_custom_hook_response
  else
    build_mock_hook_response
  end
rescue StandardError => e
  error_response("Inferno failed to generate a response: #{e.message} at #{e.backtrace.first}", code: 500)
  nil
end

#ig_versionObject



23
24
25
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 23

def ig_version
  @ig_version ||= requested_version || request.env['PATH_INFO'].match(/(v\d+)/)&.[](1) || 'v201'
end

#invoked_hookObject

from the url



65
66
67
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 65

def invoked_hook
  @invoked_hook ||= request.env['PATH_INFO'].match(%r{/([^/]+)-(?:service|subset)$})&.[](1)
end

#issObject



45
46
47
48
49
50
51
52
53
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 45

def iss
  @iss ||=
    begin
      payload, = JWT.decode(token, nil, false)
      payload['iss']
    rescue JWT::DecodeError
      nil
    end
end

#long_running_group?Boolean


Long Running Group handling


Returns:

  • (Boolean)


214
215
216
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 214

def long_running_group?
  test.config.options[:crd_test_group] == LONG_RUNNING_GROUP_TAG
end

#long_running_pause_timeObject



218
219
220
221
222
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 218

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

#make_responseObject



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
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 78

def make_response
  if invoked_hook != requested_hook
    error_response("#{request.env['PATH_INFO']} serves the #{invoked_hook}, but the client " \
                   "requested the #{requested_hook} hook.",
                   code: 400,
                   outcome_code: 'value')
  elsif wrong_hook_for_test?
    error_response("Hook '#{requested_hook}' is not being tested in the current session. " \
                   "This session is currently testing the '#{tested_hook}' hook.",
                   code: 422,
                   outcome_code: 'value')
  elsif hook_instance_already_used?
    error_response(
      "Invalid Request: Hook instance `#{request_body['hookInstance']}` has already been used in this session.",
      outcome_code: 'value'
    )
  elsif AVAILABLE_HOOKS.include?(requested_hook)
    process_valid_hook
  else
    error_response("Invalid Request: hook `#{requested_hook}` is not supported by this server.",
                   outcome_code: 'value')
  end
rescue StandardError => e
  error_response("Inferno failed to generate a response: #{e.message} at #{e.backtrace.first}",
                 code: 500,
                 outcome_code: 'exception')
end

#nameObject



206
207
208
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 206

def name
  requested_hook.gsub('-', '_')
end

#process_valid_hookObject



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

def process_valid_hook
  if ig_version == 'v201'
    send(:"gather_#{requested_hook.gsub('-', '_')}_data")
    request_coverage
  elsif ig_version == 'v221'
    request_additional_fhir_data
  end
  response_body = apply_hook_configuration(hook_response)
  return unless response_body.present?

  response.body = response_body.to_json
  response.headers.merge!({ 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' })
  response.status = 200
  response.format = :json
end

#request_bodyObject

JSON round-trip normalizes the Hanami params object to a plain string-keyed Hash



28
29
30
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 28

def request_body
  @request_body ||= JSON.parse(request.params.to_json)
end

#requested_hookObject

from the hook body



60
61
62
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 60

def requested_hook
  @requested_hook ||= request_body['hook']
end

#requested_versionObject



32
33
34
35
36
37
38
39
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 32

def requested_version
  requested = request_body.dig('extension', 'davinci-crd.requestedVersion').to_s
  if requested == '2.2'
    'v221'
  elsif requested == '2.0'
    'v201'
  end
end

#response_approachObject



122
123
124
125
126
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 122

def response_approach
  JSON.parse(result.input_json)
    .find { |input| input['name'].ends_with?('_response_approach') }
    &.dig('value')
end

#tagsObject



161
162
163
164
165
166
167
168
169
170
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 161

def tags
  return [LONG_RUNNING_GROUP_TAG] if long_running_group?
  return [DUPLICATED_HOOK_INSTANCE_TAG] if hook_instance_already_used?

  return [] if invoked_hook != requested_hook ||
               wrong_hook_for_test? ||
               !AVAILABLE_HOOKS.include?(requested_hook)

  [hook_instance_tag, hook_or_group_tag]
end

#test_run_identifierObject



41
42
43
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 41

def test_run_identifier
  iss
end

#tested_hookObject

from the waiting test



70
71
72
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 70

def tested_hook
  @tested_hook ||= test.config.options[:hook_name]
end

#tokenObject



55
56
57
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 55

def token
  @token ||= request.headers['authorization']&.delete_prefix('Bearer ')
end

#update_resultObject

end the wait immediately after the long-running request returns pause here because update_result runs before response generation



226
227
228
229
230
231
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 226

def update_result
  return unless long_running_group?

  sleep long_running_pause_time
  results_repo.update(result.id, result: 'pass', result_message: '')
end

#wrong_hook_for_test?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 74

def wrong_hook_for_test?
  tested_hook.present? && tested_hook != ANY_HOOK_TAG && requested_hook != tested_hook
end