Module: DaVinciCRDTestKit::CardsIdentification
- Includes:
- HookRequestFieldValidation, SuggestionActionsValidation
- Included in:
- HookRequestEndpoint, Jobs::InvokeHook, ResponseLogicalModelValidation, V201::AdditionalOrdersValidationTest, V201::ClientCardDisplayAttest, V201::ClientCardMustSupportCoverageInformationTest, V201::ClientCardMustSupportExternalReferenceTest, V201::ClientCardMustSupportInstructionsTest, V201::CoverageInformationSystemActionReceivedTest, V201::CreateOrUpdateCoverageInfoResponseValidationTest, V201::ExternalReferenceCardValidationTest, V201::FormCompletionResponseValidationTest, V201::InstructionsCardReceivedTest, V201::LaunchSmartAppCardValidationTest, V201::ProposeAlternateRequestCardValidationTest, V221::AdditionalOrdersValidationTest, V221::AllResponsesIncludeCoverageInformationTest, V221::ClientCardDisplayAttest, V221::ClientCardMustSupportCoverageInformationTest, V221::ClientCoverageInfoUpdateTest, V221::ClientHookResponseSupportCoverageInformationTest, V221::CoverageInfoConfigurationTest, V221::CoverageInformationMustSupportTest, V221::CoverageInformationSystemActionReceivedTest, V221::CreateOrUpdateCoverageInfoResponseValidationTest, V221::ExternalReferenceCardValidationTest, V221::FormCompletionResponseValidationTest, V221::InstructionsCardReceivedTest, V221::LaunchSmartAppCardValidationTest, V221::ProposeAlternateRequestCardValidationTest, V221::UnknownCDSHooksElementsTest, V221::UnknownConfigurationTest, V221::UnknownContextTest
- Defined in:
- lib/davinci_crd_test_kit/cross_suite/cards_identification.rb
Constant Summary
collapse
- COVERAGE_INFO_EXT_URL =
'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/ext-coverage-information'.freeze
- ADDITIONAL_ORDERS_RESPONSE_TYPE =
'companions_prerequisites'.freeze
- COVERAGE_INFORMATION_RESPONSE_TYPE =
'coverage_information'.freeze
- CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE =
'create_update_coverage_info'.freeze
- EXTERNAL_REFERENCE_RESPONSE_TYPE =
'external_reference'.freeze
- FORM_COMPLETION_RESPONSE_TYPE =
'request_form_completion'.freeze
- INSTRUCTIONS_RESPONSE_TYPE =
'instructions'.freeze
- LAUNCH_SMART_APP_RESPONSE_TYPE =
'launch_smart_app'.freeze
- PROPOSE_ALTERNATIVE_REQUEST_RESPONSE_TYPE =
'propose_alternate_request'.freeze
- COVERAGE_INFO_CONFIGURATION_CODE =
'coverage-info'.freeze
- ADDITIONAL_ORDERS_EXPECTED_RESOURCE_TYPES =
%w[
CommunicationRequest Device DeviceRequest Medication
MedicationRequest NutritionOrder ServiceRequest
VisionPrescription
].freeze
- PROPOSE_ALTERNATIVE_REQUEST_EXPECTED_RESOURCE_TYPES =
%w[
CommunicationRequest Device DeviceRequest Encounter Medication
MedicationRequest NutritionOrder ServiceRequest
VisionPrescription
].freeze
- COVERAGE_INFO_EXPECTED_RESOURCE_TYPES =
%w[
Appointment CommunicationRequest DeviceRequest Encounter MedicationRequest
NutritionOrder ServiceRequest VisionPrescription
].freeze
ProfilesAndResourceTypes::ORDER_OR_ENCOUNTER_RESOURCE_CLASSES, ProfilesAndResourceTypes::ORDER_RESOURCE_CLASSES, ProfilesAndResourceTypes::ORDER_RESOURCE_TYPES
RequestsLogicalModelValidation::CRD_CDS_HOOK_REQUEST_MODEL_URL, RequestsLogicalModelValidation::PERFORMER_ALLOWED_RESOURCE_TYPES, RequestsLogicalModelValidation::USER_ID_ALLOWED_RESOURCE_TYPES
Instance Method Summary
collapse
#hook_request_context_check, #hook_request_optional_fields_check, #hook_request_prefetch_check, #hook_request_required_fields_check, #json_parse, #no_error_validation
#structure_definition_map, #structure_definition_map_v201, #structure_definition_map_v221
#client_fhir_base_url, #fhir_url, #instance_url, #search_url
Methods included from BaseURLs
#inferno_base_url, #resume_fail_url, #resume_pass_url
#validate_request_against_logical_model
#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?
#action_fields_validation, #action_resource_type_check, #actions_check
Instance Method Details
#additional_orders_response_type?(card, expected_resource_types: ADDITIONAL_ORDERS_EXPECTED_RESOURCE_TYPES) ⇒ Boolean
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 112
def additional_orders_response_type?(card, expected_resource_types: ADDITIONAL_ORDERS_EXPECTED_RESOURCE_TYPES)
return false if card['suggestions'].blank?
card['suggestions'].all? do |suggestion|
actions = suggestion['actions']
actions&.all? do |action|
action['type'] == 'create' &&
(expected_resource_types.blank? || action_resource_type_check(action, expected_resource_types))
end
end
end
|
#cache_sorted_cards(requests, sorted_cards) ⇒ Object
295
296
297
298
299
300
301
302
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 295
def cache_sorted_cards(requests, sorted_cards)
scratch['sorted_cards'] = {
'hook_instances' => hook_instances_from_requests(requests),
'data' => sorted_cards
}
rescue JSON::ParserError
nil
end
|
#check_action_type(actions, action_type, expected_resource_types) ⇒ Object
209
210
211
212
213
214
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 209
def check_action_type(actions, action_type, expected_resource_types)
actions&.any? do |action|
action['type'] == action_type &&
(expected_resource_types.blank? || action_resource_type_check(action, expected_resource_types))
end
end
|
#coverage_info_card_type?(card) ⇒ Boolean
60
61
62
63
64
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 60
def coverage_info_card_type?(card)
return false unless card.respond_to?(:dig)
card.dig('source', 'topic', 'code') == COVERAGE_INFO_CONFIGURATION_CODE
end
|
#coverage_info_configuration_disabled?(request_body) ⇒ Boolean
97
98
99
100
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 97
def coverage_info_configuration_disabled?(request_body)
request_body.is_a?(Hash) &&
request_body.dig('extension', 'davinci-crd.configuration', COVERAGE_INFO_CONFIGURATION_CODE) == false
end
|
#coverage_info_content(response_body) ⇒ Object
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 72
def coverage_info_content(response_body)
return [[], []] unless response_body.is_a?(Hash)
cards =
if response_body['cards'].is_a?(Array)
response_body['cards'].select { |card| coverage_info_card_type?(card) }
else
[]
end
actions =
if response_body['systemActions'].is_a?(Array)
response_body['systemActions'].select { |action| coverage_info_system_action_type?(action) }
else
[]
end
[cards, actions]
end
|
#coverage_info_response?(response_body) ⇒ Boolean
91
92
93
94
95
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 91
def coverage_info_response?(response_body)
cards, actions = coverage_info_content(response_body)
cards.present? || actions.present?
end
|
#coverage_info_system_action_type?(action) ⇒ Boolean
rubocop:disable Metrics/CyclomaticComplexity
124
125
126
127
128
129
130
131
132
133
134
135
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 124
def coverage_information_response_type?(action) return false unless action.respond_to?(:[])
resource = action['resource']&.to_hash
return false unless COVERAGE_INFO_EXPECTED_RESOURCE_TYPES.include? resource&.dig('resourceType')
unless resource&.dig('extension')&.any? { |extension| extension_url(extension) == COVERAGE_INFO_EXT_URL }
return false
end
true
end
|
#create_or_update_coverage_action_response_type?(action) ⇒ Boolean
150
151
152
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 150
def create_or_update_coverage_action_response_type?(action)
['create', 'update'].include?(action['type']) && action_resource_type_check(action, ['Coverage'])
end
|
#create_or_update_coverage_card_response_type?(card) ⇒ Boolean
144
145
146
147
148
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 144
def create_or_update_coverage_card_response_type?(card)
card['suggestions']&.one? && card['suggestions'].first['actions']&.one? do |action|
create_or_update_coverage_action_response_type?(action)
end
end
|
#create_questionnaire_action_response_type?(action) ⇒ Boolean
154
155
156
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 154
def create_questionnaire_action_response_type?(action)
action['type'] == 'create' && action_resource_type_check(action, ['Questionnaire'])
end
|
#disable_coverage_info_configuration!(request_body) ⇒ Object
102
103
104
105
106
107
108
109
110
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 102
def disable_coverage_info_configuration!(request_body)
request_body['extension'] = {} unless request_body['extension'].is_a?(Hash)
unless request_body['extension']['davinci-crd.configuration'].is_a?(Hash)
request_body['extension']['davinci-crd.configuration'] = {}
end
request_body['extension']['davinci-crd.configuration'][COVERAGE_INFO_CONFIGURATION_CODE] = false
request_body
end
|
#extension_url(extension) ⇒ Object
137
138
139
140
141
142
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 137
def extension_url(extension)
return extension.url if extension.respond_to?(:url)
return extension['url'] if extension.respond_to?(:[])
nil
end
|
#external_reference_response_type?(card) ⇒ Boolean
158
159
160
161
162
163
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 158
def external_reference_response_type?(card)
links = card['links']
return false if links.blank?
links.all? { |link| link['type'] == 'absolute' }
end
|
272
273
274
275
276
277
278
279
280
281
282
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 272
def (sorted_cards)
coverage_information_extensions = []
sorted_cards['actions'][COVERAGE_INFORMATION_RESPONSE_TYPE].each do |coverage_information_action|
coverage_information_action['resource']['extension'].each do |extension|
coverage_information_extensions << extension if extension['url'] == COVERAGE_INFO_EXT_URL
end
end
coverage_information_extensions.map { |ext| FHIR::Extension.new(ext) }
end
|
171
172
173
174
175
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 171
def form_completion_action_response_type?(action)
action['type'] == 'create' &&
action_resource_type_check(action, ['Task']) &&
form_completion_task_questionnaire?(action['resource'])
end
|
165
166
167
168
169
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 165
def form_completion_card_response_type?(card)
card['suggestions']&.all? do |suggestion|
suggestion['actions']&.one? { |action| form_completion_action_response_type?(action) }
end
end
|
177
178
179
180
181
182
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 177
def form_completion_task_questionnaire?(task_action)
task_action.dig('code', 'coding')&.any? { |code| code['code'] == 'complete-questionnaire' } &&
task_action['input']&.any? do |input|
input.dig('type', 'text') == 'questionnaire' && valid_url?(input['valueCanonical'])
end
end
|
#hook_instances_from_requests(requests) ⇒ Object
308
309
310
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 308
def hook_instances_from_requests(requests)
requests.map { |request| JSON.parse(request.request_body)&.dig('hookInstance') }.compact
end
|
#identify_action_type(action) ⇒ Object
#identify_card_type(card) ⇒ Object
rubocop:disable Metrics/CyclomaticComplexity
#initialize_sorted_cards_hash ⇒ Object
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 251
def initialize_sorted_cards_hash
{
'cards' => {
ADDITIONAL_ORDERS_RESPONSE_TYPE => [],
CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE => [],
EXTERNAL_REFERENCE_RESPONSE_TYPE => [],
FORM_COMPLETION_RESPONSE_TYPE => [],
INSTRUCTIONS_RESPONSE_TYPE => [],
LAUNCH_SMART_APP_RESPONSE_TYPE => [],
PROPOSE_ALTERNATIVE_REQUEST_RESPONSE_TYPE => [],
nil => [] },
'actions' => {
COVERAGE_INFORMATION_RESPONSE_TYPE => [],
CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE => [],
FORM_COMPLETION_RESPONSE_TYPE => [],
nil => [] }
}
end
|
#instructions_response_type?(card) ⇒ Boolean
184
185
186
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 184
def instructions_response_type?(card)
card['links'].blank? && card['suggestions'].blank?
end
|
#launch_smart_app_response_type?(card) ⇒ Boolean
188
189
190
191
192
193
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 188
def launch_smart_app_response_type?(card)
links = card['links']
return false if links.blank?
links.all? { |link| link['type'] == 'smart' }
end
|
#list_card_types_in_requests(hooks_requests) ⇒ Object
216
217
218
219
220
221
222
223
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 216
def list_card_types_in_requests(hooks_requests)
sorted_cards = sorted_cards_from_requests(hooks_requests)
present_card_types = sorted_cards['cards'].select { |key, value| key.present? && value.present? }.keys
present_action_types = sorted_cards['actions'].select { |key, value| key.present? && value.present? }.keys
present_card_types.map { |type| "#{type}_card" } + present_action_types.map { |type| "#{type}_action" }
end
|
#propose_alternative_request_response_type?(card, expected_resource_types: PROPOSE_ALTERNATIVE_REQUEST_EXPECTED_RESOURCE_TYPES) ⇒ Boolean
195
196
197
198
199
200
201
202
203
204
205
206
207
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 195
def propose_alternative_request_response_type?(
card,
expected_resource_types: PROPOSE_ALTERNATIVE_REQUEST_EXPECTED_RESOURCE_TYPES
)
card['suggestions']&.any? do |suggestion|
actions = suggestion['actions']
has_update = check_action_type(actions, 'update', expected_resource_types)
has_delete = check_action_type(actions, 'delete', expected_resource_types)
has_create = check_action_type(actions, 'create', expected_resource_types)
has_update || (has_delete && has_create)
end
end
|
#sort_card_types_from_request(request, sorted_cards) ⇒ Object
240
241
242
243
244
245
246
247
248
249
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 240
def sort_card_types_from_request(request, sorted_cards)
response_body = JSON.parse(request.response_body)
response_body['cards']&.each do |card|
sorted_cards['cards'][identify_card_type(card)] << card
end
response_body['systemActions']&.each do |action|
sorted_cards['actions'][identify_action_type(action)] << action
end
end
|
#sorted_cards_cached?(requests) ⇒ Boolean
284
285
286
287
288
289
290
291
292
293
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 284
def sorted_cards_cached?(requests)
return false unless scratch['sorted_cards'].present?
return false unless scratch['sorted_cards']['hook_instances'].length == requests.length
requests.all? do |request|
scratch['sorted_cards']['hook_instances'].include?(JSON.parse(request.request_body)&.dig('hookInstance'))
rescue JSON::ParserError
false
end
end
|
#sorted_cards_from_cache ⇒ Object
304
305
306
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 304
def sorted_cards_from_cache
scratch['sorted_cards']['data']
end
|
#sorted_cards_from_requests(hooks_requests) ⇒ Object
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 225
def sorted_cards_from_requests(hooks_requests)
return sorted_cards_from_cache if sorted_cards_cached?(hooks_requests)
sorted_cards = initialize_sorted_cards_hash
hooks_requests.each do |request|
sort_card_types_from_request(request, sorted_cards)
rescue JSON::ParserError
next
end
cache_sorted_cards(hooks_requests, sorted_cards)
sorted_cards
end
|