Class: DaVinciCRDTestKit::Jobs::InvokeHook

Inherits:
Object
  • Object
show all
Includes:
CardsIdentification, ServerBaseURLs, Sidekiq::Job
Defined in:
lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb

Constant Summary

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

Instance Method Summary collapse

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

Instance Method Details

#add_unknown_configuration(request_body) ⇒ Object



181
182
183
184
185
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 181

def add_unknown_configuration(request_body)
  request_body['extension'] ||= {}
  request_body['extension']['davinci-crd.configuration'] ||= {}
  request_body['extension']['davinci-crd.configuration'][random_key] = true
end

#add_unknown_context(request_body) ⇒ Object



187
188
189
190
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 187

def add_unknown_context(request_body)
  request_body['context'] ||= {}
  request_body['context'][random_key] ||= random_key
end

#add_unknown_element(request_body) ⇒ Object



192
193
194
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 192

def add_unknown_element(request_body)
  request_body[random_key] ||= random_key
end

#await_test_waitingObject



81
82
83
84
85
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 81

def await_test_waiting
  sleep 0.5 until test_waiting? || test_done?

  @result_id = results_repo.find_waiting_result(test_run_id:)&.id
end

#invoke_hook(request_body, headers) ⇒ Object



202
203
204
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 202

def invoke_hook(request_body, headers)
  service_connection.post('', request_body, headers)
end

#parsed_response_body(response) ⇒ Object



196
197
198
199
200
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 196

def parsed_response_body(response)
  JSON.parse(response.env.response_body.to_s)
rescue JSON::ParserError, TypeError
  nil
end

#perform(test_session_id, request_bodies, service_endpoint, inferno_base_url, jwks_kid, encryption_method, request_tag, continuation_url, failure_url, acknowledge_before_continuing, coverage_info_configuration_supported) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 18

def perform(test_session_id, request_bodies, service_endpoint, inferno_base_url, jwks_kid,
            encryption_method, request_tag, continuation_url, failure_url, acknowledge_before_continuing,
            coverage_info_configuration_supported)
  @test_session_id = test_session_id
  @service_endpoint = service_endpoint
  @inferno_base_url = inferno_base_url
  @jwks_kid = jwks_kid
  @encryption_method = encryption_method
  @request_tag = request_tag
  @continuation_url = continuation_url
  @failure_url = failure_url
  @acknowledge_before_continuing = acknowledge_before_continuing
  @coverage_info_configuration_supported = coverage_info_configuration_supported

  perform_hook_invocations(request_bodies)
end

#perform_hook_invocations(request_bodies) ⇒ Object



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

def perform_hook_invocations(request_bodies)
  await_test_waiting # let Inferno start waiting so it can respond to FHIR requests

  request_bodies.each do |request|
    break unless test_waiting?

    request_body = prepare_hook_request(request)
    response = send_hook_invocation(request_body.to_json)
    send_coverage_info_configuration_invocation(request_body, response)
    send_unknown_configuration_invocation(request_body, response)
    send_unknown_context_invocation(request_body, response)
    send_unknown_cds_hooks_element_invocation(request_body, response)
  end

  return unless test_waiting?

  # end the wait to continue the tests
  Faraday.get(@continuation_url) unless @acknowledge_before_continuing
rescue StandardError => e
  Faraday.get(@failure_url, { message: "Hook invocation failed: #{e.message}" })
end

#persist_hook_request(response, tags, headers) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 206

def persist_hook_request(response, tags, headers)
  inferno_request_headers = headers.map { |name, value| { name:, value: } }
  inferno_response_headers = response.headers&.map { |name, value| { name:, value: } }
  requests_repo.create(
    verb: 'POST',
    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_session_id,
    result_id: @result_id,
    request_headers: inferno_request_headers,
    response_headers: inferno_response_headers,
    tags:
  )
end

#prepare_hook_request(parsed_request) ⇒ Object



91
92
93
94
95
96
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 91

def prepare_hook_request(parsed_request)
  parsed_request['hookInstance'] = SecureRandom.uuid
  parsed_request['fhirServer'] = fhir_url
  update_simulated_server_token(parsed_request)
  parsed_request
end

#random_keyObject



177
178
179
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 177

def random_key
  ('a'..'z').to_a.sample(16).join
end

#requests_repoObject



65
66
67
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 65

def requests_repo
  @requests_repo ||= Inferno::Repositories::Requests.new
end

#results_repoObject



69
70
71
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 69

def results_repo
  @results_repo ||= Inferno::Repositories::Results.new
end

#send_coverage_info_configuration_invocation(request_body, response) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 121

def send_coverage_info_configuration_invocation(request_body, response)
  return unless @coverage_info_configuration_supported
  return if @coverage_info_configuration_invoked
  return unless response.status == 200
  return unless coverage_info_response?(parsed_response_body(response))
  return unless test_waiting?

  configured_request_body = JSON.parse(request_body.to_json)
  prepare_hook_request(configured_request_body)
  disable_coverage_info_configuration!(configured_request_body)
  send_hook_invocation(configured_request_body.to_json, [COVERAGE_INFO_DISABLED_TAG])
  @coverage_info_configuration_invoked = true
end

#send_hook_invocation(request_body, extra_tags = []) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 107

def send_hook_invocation(request_body, extra_tags = [])
  token = JwtHelper.build(
    aud: @service_endpoint,
    iss: @inferno_base_url,
    jku: "#{@inferno_base_url}/jwks.json",
    kid: @jwks_kid,
    encryption_method: @encryption_method
  )
  headers = { 'Content-type' => 'application/json', 'Authorization' => "Bearer #{token}" }
  response = invoke_hook(request_body, headers)
  persist_hook_request(response, [@request_tag] + extra_tags, headers)
  response
end

#send_unknown_cds_hooks_element_invocation(request_body, response) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 163

def send_unknown_cds_hooks_element_invocation(request_body, response)
  return if @unknown_cds_hooks_element_invoked
  return unless response.status == 200
  return unless coverage_info_response?(parsed_response_body(response))
  return unless test_waiting?

  request_body = JSON.parse(request_body.to_json)
  prepare_hook_request(request_body)
  add_unknown_element(request_body)
  send_hook_invocation(request_body.to_json, [UNKNOWN_ELEMENT_TAG])

  @unknown_cds_hooks_element_invoked = true
end

#send_unknown_configuration_invocation(request_body, response) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 135

def send_unknown_configuration_invocation(request_body, response)
  return if @unknown_configuration_invoked
  return unless response.status == 200
  return unless coverage_info_response?(parsed_response_body(response))
  return unless test_waiting?

  request_body = JSON.parse(request_body.to_json)
  prepare_hook_request(request_body)
  add_unknown_configuration(request_body)
  send_hook_invocation(request_body.to_json, [UNKNOWN_CONFIGURATION_TAG])

  @unknown_configuration_invoked = true
end

#send_unknown_context_invocation(request_body, response) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 149

def send_unknown_context_invocation(request_body, response)
  return if @unknown_context_invoked
  return unless response.status == 200
  return unless coverage_info_response?(parsed_response_body(response))
  return unless test_waiting?

  request_body = JSON.parse(request_body.to_json)
  prepare_hook_request(request_body)
  add_unknown_context(request_body)
  send_hook_invocation(request_body.to_json, [UNKNOWN_CONTEXT_TAG])

  @unknown_context_invoked = true
end

#service_connectionObject



73
74
75
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 73

def service_connection
  @service_connection ||= Faraday.new(url: @service_endpoint, request: { open_timeout: 30 })
end

#test_done?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 77

def test_done?
  test_runs_repo.status_for_test_run(test_run_id) == 'done'
end

#test_run_idObject



57
58
59
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 57

def test_run_id
  @test_run_id ||= test_runs_repo.last_test_run(@test_session_id).id
end

#test_runs_repoObject



61
62
63
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 61

def test_runs_repo
  @test_runs_repo ||= Inferno::Repositories::TestRuns.new
end

#test_waiting?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 87

def test_waiting?
  results_repo.find_waiting_result(test_run_id:).present?
end

#update_simulated_server_token(parsed_request) ⇒ Object



98
99
100
101
102
103
104
105
# File 'lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb', line 98

def update_simulated_server_token(parsed_request)
  parsed_request['fhirAuthorization'] = {} if parsed_request['fhirAuthorization'].nil?
  fhir_authorization = parsed_request['fhirAuthorization']

  fhir_authorization['expires_in'] = 300 unless fhir_authorization['expires_in'].present?
  fhir_authorization['access_token'] =
    MockEHR::FHIRRequestHandler.session_id_to_token(@test_session_id, fhir_authorization['expires_in'].to_i / 60)
end