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_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

rubocop:disable Metrics/CyclomaticComplexity



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 143

def apply_hook_configuration(response_body) # rubocop:disable Metrics/CyclomaticComplexity
  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.delete 'systemActions' if response_body['systemActions'].blank?

  response_body
end

#coverage_info_disabled?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 159

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

#error_operation_outcome(code, text) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 197

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



190
191
192
193
194
195
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 190

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)


163
164
165
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 163

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

#hook_instance_tagObject



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

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

#hook_or_group_tagObject



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

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

#hook_responseObject



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

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)


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

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

#long_running_pause_timeObject



224
225
226
227
228
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 224

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

def make_response
  if requested_hook.blank?
    error_response("No hook requested - populate the 'hook' element in the request.",
                   code: 400,
                   outcome_code: 'value')
  elsif 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



212
213
214
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 212

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

#process_valid_hookObject



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 110

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



126
127
128
129
130
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 126

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

#tagsObject



167
168
169
170
171
172
173
174
175
176
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 167

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].compact
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



232
233
234
235
236
237
# File 'lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb', line 232

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