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::CoverageInformationCardAbsenceTest, V221::CoverageInformationMustSupportTest, V221::CoverageInformationSystemActionValidationTest, 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
120
121
122
123
124
125
126
127
128
129
130
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 120
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
304
305
306
307
308
309
310
311
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 304
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
218
219
220
221
222
223
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 218
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
105
106
107
108
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 105
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
99
100
101
102
103
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 99
def coverage_info_response?(response_body)
cards, actions = coverage_info_content(response_body)
cards.present? || actions.present?
end
|
#coverage_info_system_action_response?(response_body) ⇒ Boolean
91
92
93
94
95
96
97
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 91
def coverage_info_system_action_response?(response_body)
return false unless response_body.is_a?(Hash)
return false unless response_body['systemActions'].is_a?(Array)
response_body['systemActions'].any? { |action| coverage_information_response_type?(action) }
end
|
#coverage_info_system_action_type?(action) ⇒ Boolean
rubocop:disable Metrics/CyclomaticComplexity
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 132
def coverage_information_response_type?(action) return false unless action.respond_to?(:[])
return false unless action['type'] == 'update'
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
159
160
161
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 159
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
153
154
155
156
157
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 153
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
163
164
165
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 163
def create_questionnaire_action_response_type?(action)
action['type'] == 'create' && action_resource_type_check(action, ['Questionnaire'])
end
|
#disable_coverage_info_configuration!(request_body) ⇒ Object
110
111
112
113
114
115
116
117
118
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 110
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
146
147
148
149
150
151
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 146
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
167
168
169
170
171
172
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 167
def external_reference_response_type?(card)
links = card['links']
return false if links.blank?
links.all? { |link| link['type'] == 'absolute' }
end
|
281
282
283
284
285
286
287
288
289
290
291
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 281
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
|
180
181
182
183
184
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 180
def form_completion_action_response_type?(action)
action['type'] == 'create' &&
action_resource_type_check(action, ['Task']) &&
form_completion_task_questionnaire?(action['resource'])
end
|
174
175
176
177
178
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 174
def form_completion_card_response_type?(card)
card['suggestions']&.all? do |suggestion|
suggestion['actions']&.one? { |action| form_completion_action_response_type?(action) }
end
end
|
186
187
188
189
190
191
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 186
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
317
318
319
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 317
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 260
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
193
194
195
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 193
def instructions_response_type?(card)
card['links'].blank? && card['suggestions'].blank?
end
|
#launch_smart_app_response_type?(card) ⇒ Boolean
197
198
199
200
201
202
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 197
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
225
226
227
228
229
230
231
232
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 225
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
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 204
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
249
250
251
252
253
254
255
256
257
258
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 249
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
293
294
295
296
297
298
299
300
301
302
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 293
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
313
314
315
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 313
def sorted_cards_from_cache
scratch['sorted_cards']['data']
end
|
#sorted_cards_from_requests(hooks_requests) ⇒ Object
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 234
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
|