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::ClientUnknownContentAttestationTest, 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
Class Method Summary
collapse
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, #filter_logical_model_extension_issues, #local_reference?, #logical_model_extension_issue?, #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
Class Method Details
.humanize_response_type(response_type) ⇒ Object
38
39
40
41
42
43
44
45
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 38
def self.humanize_response_type(response_type)
response_type.split('_')
.map(&:capitalize)
.join(' ')
.sub('Smart', 'SMART')
.sub('Create Update', 'Create/Update')
.sub('Companions Prerequisites', 'Companions/Prerequisites')
end
|
Instance Method Details
#additional_orders_response_type?(card, expected_resource_types: ADDITIONAL_ORDERS_EXPECTED_RESOURCE_TYPES) ⇒ Boolean
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 129
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
313
314
315
316
317
318
319
320
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 313
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
227
228
229
230
231
232
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 227
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
69
70
71
72
73
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 69
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
114
115
116
117
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 114
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 81
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
108
109
110
111
112
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 108
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
100
101
102
103
104
105
106
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 100
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
75
76
77
78
79
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 75
def coverage_info_system_action_type?(action)
return false unless action.respond_to?(:[])
[COVERAGE_INFORMATION_RESPONSE_TYPE, FORM_COMPLETION_RESPONSE_TYPE].include?(identify_action_type(action))
end
|
rubocop:disable Metrics/CyclomaticComplexity
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 141
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
168
169
170
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 168
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
162
163
164
165
166
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 162
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
172
173
174
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 172
def create_questionnaire_action_response_type?(action)
action['type'] == 'create' && action_resource_type_check(action, ['Questionnaire'])
end
|
#disable_coverage_info_configuration!(request_body) ⇒ Object
119
120
121
122
123
124
125
126
127
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 119
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
155
156
157
158
159
160
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 155
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
176
177
178
179
180
181
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 176
def external_reference_response_type?(card)
links = card['links']
return false if links.blank?
links.all? { |link| link['type'] == 'absolute' }
end
|
290
291
292
293
294
295
296
297
298
299
300
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 290
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
|
189
190
191
192
193
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 189
def form_completion_action_response_type?(action)
action['type'] == 'create' &&
action_resource_type_check(action, ['Task']) &&
form_completion_task_questionnaire?(action['resource'])
end
|
183
184
185
186
187
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 183
def form_completion_card_response_type?(card)
card['suggestions']&.all? do |suggestion|
suggestion['actions']&.one? { |action| form_completion_action_response_type?(action) }
end
end
|
195
196
197
198
199
200
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 195
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
326
327
328
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 326
def hook_instances_from_requests(requests)
requests.map { |request| JSON.parse(request.request_body)&.dig('hookInstance') }.compact
end
|
#identify_action_type(action) ⇒ Object
60
61
62
63
64
65
66
67
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 60
def identify_action_type(action)
return nil unless action['type'].present?
return COVERAGE_INFORMATION_RESPONSE_TYPE if coverage_information_response_type?(action)
return CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE if create_or_update_coverage_action_response_type?(action)
return FORM_COMPLETION_RESPONSE_TYPE if form_completion_action_response_type?(action)
nil
end
|
#identify_card_type(card) ⇒ Object
rubocop:disable Metrics/CyclomaticComplexity
47
48
49
50
51
52
53
54
55
56
57
58
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 47
def identify_card_type(card)
return nil if card['type'].present?
return ADDITIONAL_ORDERS_RESPONSE_TYPE if additional_orders_response_type?(card)
return CREATE_OR_UPDATE_COVERAGE_RESPONSE_TYPE if create_or_update_coverage_card_response_type?(card)
return EXTERNAL_REFERENCE_RESPONSE_TYPE if external_reference_response_type?(card)
return FORM_COMPLETION_RESPONSE_TYPE if form_completion_card_response_type?(card)
return INSTRUCTIONS_RESPONSE_TYPE if instructions_response_type?(card)
return LAUNCH_SMART_APP_RESPONSE_TYPE if launch_smart_app_response_type?(card)
return PROPOSE_ALTERNATIVE_REQUEST_RESPONSE_TYPE if propose_alternative_request_response_type?(card)
nil
end
|
#initialize_sorted_cards_hash ⇒ Object
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 269
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
202
203
204
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 202
def instructions_response_type?(card)
card['links'].blank? && card['suggestions'].blank?
end
|
#launch_smart_app_response_type?(card) ⇒ Boolean
206
207
208
209
210
211
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 206
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
234
235
236
237
238
239
240
241
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 234
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
213
214
215
216
217
218
219
220
221
222
223
224
225
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 213
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
258
259
260
261
262
263
264
265
266
267
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 258
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
302
303
304
305
306
307
308
309
310
311
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 302
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
322
323
324
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 322
def sorted_cards_from_cache
scratch['sorted_cards']['data']
end
|
#sorted_cards_from_requests(hooks_requests) ⇒ Object
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
# File 'lib/davinci_crd_test_kit/cross_suite/cards_identification.rb', line 243
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
|