Class: Inferno::DSL::MustSupportAssessment::InternalMustSupportLogic
- Inherits:
-
Object
- Object
- Inferno::DSL::MustSupportAssessment::InternalMustSupportLogic
show all
- Includes:
- FHIRResourceNavigation
- Defined in:
- lib/inferno/dsl/must_support_assessment.rb
Constant Summary
FHIRResourceNavigation::DAR_EXTENSION_URL, FHIRResourceNavigation::PRIMITIVE_DATA_TYPES
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#any_choice_supported?(choices) ⇒ Boolean
-
#any_elements_choice_supported?(choices) ⇒ Boolean
-
#any_extension_ids_choice_supported?(choices) ⇒ Boolean
-
#any_path_choice_supported?(choices) ⇒ Boolean
-
#any_slice_names_choice_supported?(choices) ⇒ Boolean
-
#extension_definition_candidates(path_matching_extensions) ⇒ Object
-
#extension_urls(value) ⇒ Object
-
#extract_metadata(profile, ig, requirement_extension: nil) ⇒ Object
-
#find_missing_elements(resources, must_support_elements) ⇒ Object
-
#find_pattern_codeable_concept_slice(element, discriminator) ⇒ Object
-
#find_pattern_coding_slice(element, discriminator) ⇒ Object
-
#find_pattern_identifier_slice(element, discriminator) ⇒ Object
-
#find_required_binding_slice(element, discriminator) ⇒ Object
-
#find_slice(resource, path, discriminator) ⇒ Object
-
#find_slice_by_values(element, value_definitions) ⇒ Object
-
#find_type_slice(element, discriminator) ⇒ Object
-
#find_value_slice(element, discriminator) ⇒ Object
-
#handle_must_support_choices ⇒ Object
-
#handle_must_support_element_choices ⇒ Object
-
#handle_must_support_extension_choices ⇒ Object
-
#handle_must_support_slice_choices ⇒ Object
-
#initialize(metadata = nil) ⇒ InternalMustSupportLogic
constructor
A new instance of InternalMustSupportLogic.
-
#matches_fixed_value?(value, fixed_value) ⇒ Boolean
-
#matching_without_extensions?(value, ms_extension_urls, fixed_value) ⇒ Boolean
-
#missing_element_string(element_definition) ⇒ Object
-
#missing_elements(resources = []) ⇒ Object
-
#missing_extensions(resources = []) ⇒ Object
-
#missing_must_support_strings ⇒ Object
-
#missing_slices(resources = []) ⇒ Object
-
#must_support_elements ⇒ Object
-
#must_support_extension_definition(extension_path, extension_type, extension_name) ⇒ Object
-
#must_support_extension_present?(value, ms_extension_urls) ⇒ Boolean
-
#must_support_extensions ⇒ Object
-
#must_support_slices ⇒ Object
-
#navigation_compatible_must_support_path(path) ⇒ Object
-
#normalized_extension_url(url) ⇒ Object
-
#normalized_extension_urls(urls) ⇒ Object
-
#normalized_must_support_path_segment(segment, logical_segments) ⇒ Object
-
#perform_must_support_test_with_metadata(resources, profile_metadata, debug_metadata: false) ⇒ Array<String>
perform_must_support_test_with_metadata is invoked from check and perform_must_support_test, with the metadata to be used as the basis for the test.
-
#perform_test(resources) ⇒ Object
-
#process_must_support_element_in_extension(resource, path) ⇒ Object
-
#required_binding_value_match?(coding, values) ⇒ Boolean
-
#resource_populates_element?(resource, element_definition) ⇒ Boolean
-
#value_without_extensions?(value) ⇒ Boolean
-
#write_metadata_for_debugging ⇒ Object
#append_path_character, #choice_path?, #current_and_child_values_match?, #extension_filter_value, #field_value, #find_a_value_at, #find_in_elements, #find_slice_via_discriminator, #flatten_bundles, #get_next_value, #get_primitive_type_value, #local_field_name, #matching_pattern_codeable_concept_slice?, #matching_pattern_coding_slice?, #matching_pattern_identifier_slice?, #matching_required_binding_slice?, #matching_slice?, #matching_type_slice?, #matching_value_slice?, #path_segments, #populated_choice_value, #required_binding_codings, #resolve_path, #slice_path?, #sliced_choice_path?, #sliced_choice_value, #split_path_segment_or_append, #update_path_segment_state, #value_at_path_matches?, #value_not_empty?, #verify_slice_by_values
Constructor Details
Returns a new instance of InternalMustSupportLogic.
68
69
70
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 68
def initialize(metadata = nil)
@metadata = metadata
end
|
Instance Attribute Details
Returns the value of attribute metadata.
66
67
68
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 66
def metadata
@metadata
end
|
Instance Method Details
#any_choice_supported?(choices) ⇒ Boolean
145
146
147
148
149
150
151
152
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 145
def any_choice_supported?(choices)
return false unless choices.present?
any_path_choice_supported?(choices) ||
any_extension_ids_choice_supported?(choices) ||
any_slice_names_choice_supported?(choices) ||
any_elements_choice_supported?(choices)
end
|
#any_elements_choice_supported?(choices) ⇒ Boolean
174
175
176
177
178
179
180
181
182
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 174
def any_elements_choice_supported?(choices)
return false unless choices[:elements].present?
choices[:elements].any? do |choice|
missing_elements.none? do |element|
element[:path] == choice[:path] && element[:fixed_value] == choice[:fixed_value]
end
end
end
|
#any_extension_ids_choice_supported?(choices) ⇒ Boolean
160
161
162
163
164
165
166
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 160
def any_extension_ids_choice_supported?(choices)
return false unless choices[:extension_ids].present?
choices[:extension_ids].any? do |extension_id|
missing_extensions.none? { |extension| extension[:id] == extension_id }
end
end
|
#any_path_choice_supported?(choices) ⇒ Boolean
154
155
156
157
158
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 154
def any_path_choice_supported?(choices)
return false unless choices[:paths].present?
choices[:paths].any? { |path| missing_elements.none? { |element| element[:path] == path } }
end
|
#any_slice_names_choice_supported?(choices) ⇒ Boolean
168
169
170
171
172
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 168
def any_slice_names_choice_supported?(choices)
return false unless choices[:slice_names].present?
choices[:slice_names].any? { |slice_name| missing_slices.none? { |slice| slice[:name] == slice_name } }
end
|
#extension_definition_candidates(path_matching_extensions) ⇒ Object
287
288
289
290
291
292
293
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 287
def extension_definition_candidates(path_matching_extensions)
[
[path_matching_extensions, :end_with?],
[path_matching_extensions, :include?],
[must_support_extensions, :end_with?]
]
end
|
#extension_urls(value) ⇒ Object
333
334
335
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 333
def extension_urls(value)
Array.wrap(value.extension).map { |extension| normalized_extension_url(extension.url) }
end
|
89
90
91
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 89
def (profile, ig, requirement_extension: nil)
MustSupportMetadataExtractor.new(profile.snapshot.element, profile, profile.type, ig, requirement_extension)
end
|
#find_missing_elements(resources, must_support_elements) ⇒ Object
233
234
235
236
237
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 233
def find_missing_elements(resources, must_support_elements)
must_support_elements.select do |element_definition|
resources.none? { |resource| resource_populates_element?(resource, element_definition) }
end
end
|
#find_pattern_codeable_concept_slice(element, discriminator) ⇒ Object
390
391
392
393
394
395
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 390
def find_pattern_codeable_concept_slice(element, discriminator)
coding_path = discriminator[:path].present? ? "#{discriminator[:path]}.coding" : 'coding'
find_a_value_at(element, coding_path) do |coding|
coding.code == discriminator[:code] && coding.system == discriminator[:system]
end
end
|
#find_pattern_coding_slice(element, discriminator) ⇒ Object
397
398
399
400
401
402
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 397
def find_pattern_coding_slice(element, discriminator)
coding_path = discriminator[:path].present? ? discriminator[:path] : ''
find_a_value_at(element, coding_path) do |coding|
coding.code == discriminator[:code] && coding.system == discriminator[:system]
end
end
|
#find_pattern_identifier_slice(element, discriminator) ⇒ Object
404
405
406
407
408
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 404
def find_pattern_identifier_slice(element, discriminator)
find_a_value_at(element, discriminator[:path]) do |identifier|
identifier.system == discriminator[:system]
end
end
|
#find_required_binding_slice(element, discriminator) ⇒ Object
441
442
443
444
445
446
447
448
449
450
451
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 441
def find_required_binding_slice(element, discriminator)
if element.is_a?(FHIR::Coding) && required_binding_value_match?(element, discriminator[:values])
return element
end
coding_path = discriminator[:path].present? ? "#{discriminator[:path]}.coding" : 'coding'
find_a_value_at(element, coding_path) do |coding|
required_binding_value_match?(coding, discriminator[:values])
end
end
|
#find_slice(resource, path, discriminator) ⇒ Object
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 368
def find_slice(resource, path, discriminator)
find_a_value_at(resource, path) do |element|
case discriminator[:type]
when 'patternCodeableConcept'
find_pattern_codeable_concept_slice(element, discriminator)
when 'patternCoding'
find_pattern_coding_slice(element, discriminator)
when 'patternIdentifier'
find_pattern_identifier_slice(element, discriminator)
when 'value'
find_value_slice(element, discriminator)
when 'type'
find_type_slice(element, discriminator)
when 'requiredBinding'
find_required_binding_slice(element, discriminator)
end
end
end
|
#find_slice_by_values(element, value_definitions) ⇒ Object
453
454
455
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 453
def find_slice_by_values(element, value_definitions)
Array.wrap(element).find { |el| verify_slice_by_values(el, value_definitions) }
end
|
#find_type_slice(element, discriminator) ⇒ Object
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 415
def find_type_slice(element, discriminator)
case discriminator[:code]
when 'Date'
begin
Date.parse(element)
rescue ArgumentError, TypeError
false
end
when 'DateTime'
begin
DateTime.parse(element)
rescue ArgumentError, TypeError
false
end
when 'String'
element.is_a? String
else
if element.is_a? FHIR::Bundle::Entry
element = element.resource
end
element.is_a? FHIR.const_get(discriminator[:code])
end
end
|
#find_value_slice(element, discriminator) ⇒ Object
410
411
412
413
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 410
def find_value_slice(element, discriminator)
values = discriminator[:values].map { |value| value.merge(path: path_segments(value[:path])) }
find_slice_by_values(element, values)
end
|
#handle_must_support_choices ⇒ Object
113
114
115
116
117
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 113
def handle_must_support_choices
handle_must_support_element_choices
handle_must_support_extension_choices
handle_must_support_slice_choices
end
|
#handle_must_support_element_choices ⇒ Object
119
120
121
122
123
124
125
126
127
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 119
def handle_must_support_element_choices
missing_elements.delete_if do |element|
choices = metadata.must_supports[:choices].find do |choice|
choice[:paths]&.include?(element[:path]) ||
choice[:elements]&.any? { |ms_element| ms_element[:path] == element[:path] }
end
any_choice_supported?(choices)
end
end
|
#handle_must_support_extension_choices ⇒ Object
129
130
131
132
133
134
135
136
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 129
def handle_must_support_extension_choices
missing_extensions.delete_if do |extension|
choices = metadata.must_supports[:choices].find do |choice|
choice[:extension_ids]&.include?(extension[:id])
end
any_choice_supported?(choices)
end
end
|
#handle_must_support_slice_choices ⇒ Object
138
139
140
141
142
143
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 138
def handle_must_support_slice_choices
missing_slices.delete_if do |slice|
choices = metadata.must_supports[:choices].find { |choice| choice[:slice_names]&.include?(slice[:name]) }
any_choice_supported?(choices)
end
end
|
#matches_fixed_value?(value, fixed_value) ⇒ Boolean
345
346
347
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 345
def matches_fixed_value?(value, fixed_value)
fixed_value.blank? || value == fixed_value
end
|
#matching_without_extensions?(value, ms_extension_urls, fixed_value) ⇒ Boolean
317
318
319
320
321
322
323
324
325
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 317
def matching_without_extensions?(value, ms_extension_urls, fixed_value)
has_ms_extension = must_support_extension_present?(value, ms_extension_urls)
value = value.value if value.instance_of?(Inferno::DSL::PrimitiveType)
return false unless has_ms_extension || value_without_extensions?(value)
matches_fixed_value?(value, fixed_value)
end
|
#missing_element_string(element_definition) ⇒ Object
190
191
192
193
194
195
196
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 190
def missing_element_string(element_definition)
if element_definition[:fixed_value].present?
"#{element_definition[:path]}:#{element_definition[:fixed_value]}"
else
element_definition[:path]
end
end
|
#missing_elements(resources = []) ⇒ Object
229
230
231
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 229
def missing_elements(resources = [])
@missing_elements ||= find_missing_elements(resources, must_support_elements)
end
|
#missing_extensions(resources = []) ⇒ Object
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 202
def missing_extensions(resources = [])
@missing_extensions ||=
must_support_extensions.select do |extension_definition|
expected_url = normalized_extension_url(extension_definition[:url])
resources.none? do |resource|
path = extension_definition[:path]
if path == 'extension'
Array.wrap(resource.extension).any? do |extension|
normalized_extension_url(extension.url) == expected_url
end
else
extension = find_a_value_at(resource, path) do |el|
normalized_extension_url(el.url) == expected_url
end
extension.present?
end
end
end
end
|
#missing_must_support_strings ⇒ Object
184
185
186
187
188
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 184
def missing_must_support_strings
missing_elements.map { |element_definition| missing_element_string(element_definition) } +
missing_slices.map { |slice_definition| slice_definition[:slice_id] } +
missing_extensions.map { |extension_definition| extension_definition[:id] }
end
|
#missing_slices(resources = []) ⇒ Object
358
359
360
361
362
363
364
365
366
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 358
def missing_slices(resources = [])
@missing_slices ||=
must_support_slices.select do |slice|
resources.none? do |resource|
path = slice[:path]
find_slice(resource, path, slice[:discriminator]).present?
end
end
end
|
#must_support_elements ⇒ Object
225
226
227
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 225
def must_support_elements
metadata.must_supports[:elements]
end
|
#must_support_extension_definition(extension_path, extension_type, extension_name) ⇒ Object
275
276
277
278
279
280
281
282
283
284
285
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 275
def must_support_extension_definition(extension_path, extension_type, extension_name)
suffix = "#{extension_type}:#{extension_name}"
path_matching_extensions = must_support_extensions.select { |definition| definition[:path] == extension_path }
extension_definition_candidates(path_matching_extensions).each do |definitions, matcher|
match = definitions.find { |definition| definition[:id].public_send(matcher, suffix) }
return match if match.present?
end
nil
end
|
#must_support_extension_present?(value, ms_extension_urls) ⇒ Boolean
327
328
329
330
331
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 327
def must_support_extension_present?(value, ms_extension_urls)
return false unless value.respond_to?(:extension)
(extension_urls(value) & normalized_extension_urls(ms_extension_urls)).present?
end
|
#must_support_extensions ⇒ Object
198
199
200
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 198
def must_support_extensions
metadata.must_supports[:extensions]
end
|
#must_support_slices ⇒ Object
354
355
356
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 354
def must_support_slices
metadata.must_supports[:slices]
end
|
#navigation_compatible_must_support_path(path) ⇒ Object
254
255
256
257
258
259
260
261
262
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 254
def navigation_compatible_must_support_path(path)
logical_segments = []
path_segments(path).map do |segment|
normalized_segment = normalized_must_support_path_segment(segment, logical_segments)
logical_segments << segment.split(':').first
normalized_segment
end.join('.')
end
|
#normalized_extension_url(url) ⇒ Object
341
342
343
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 341
def normalized_extension_url(url)
url&.split('|')&.first
end
|
#normalized_extension_urls(urls) ⇒ Object
337
338
339
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 337
def normalized_extension_urls(urls)
Array.wrap(urls).map { |url| normalized_extension_url(url) }
end
|
#normalized_must_support_path_segment(segment, logical_segments) ⇒ Object
264
265
266
267
268
269
270
271
272
273
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 264
def normalized_must_support_path_segment(segment, logical_segments)
extension_type, extension_name = segment.match(/\A(modifierExtension|extension):(.+)\z/)&.captures
return segment if extension_type.blank?
extension_path = [logical_segments.join('.'), extension_type].reject(&:blank?).join('.')
extension_definition = must_support_extension_definition(extension_path, extension_type, extension_name)
return segment if extension_definition.blank?
"#{extension_type}.where(url='#{normalized_extension_url(extension_definition[:url])}')"
end
|
perform_must_support_test_with_metadata is invoked from check and perform_must_support_test, with the metadata to be used as the basis for the test. It may also be invoked directly from a test if you want to completely overwrite the metadata.
79
80
81
82
83
84
85
86
87
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 79
def perform_must_support_test_with_metadata(resources, profile_metadata, debug_metadata: false)
return if resources.blank?
@metadata = profile_metadata
write_metadata_for_debugging if debug_metadata
perform_test(resources)
end
|
103
104
105
106
107
108
109
110
111
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 103
def perform_test(resources)
missing_elements(resources)
missing_slices(resources)
missing_extensions(resources)
handle_must_support_choices if metadata.must_supports[:choices].present?
missing_must_support_strings
end
|
#process_must_support_element_in_extension(resource, path) ⇒ Object
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 295
def process_must_support_element_in_extension(resource, path)
return [resource, path] unless path.start_with?('extension:')
path_without_prefix = path.delete_prefix('extension:')
extension_split = path_without_prefix.split('.')
extension_name = extension_split.first
extension_path = extension_split.last
found_extension_url =
normalized_extension_url(must_support_extensions.find { |ex| ex[:id].include?(extension_name) }[:url])
ms_element_extension = resource.extension.find do |extension|
normalized_extension_url(extension.url) == found_extension_url
end
if ms_element_extension.present?
resource = ms_element_extension
path = extension_path
end
[resource, path]
end
|
#required_binding_value_match?(coding, values) ⇒ Boolean
457
458
459
460
461
462
463
464
465
466
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 457
def required_binding_value_match?(coding, values)
values.any? do |value|
case value
when String
value == coding.code
when Hash
value[:system] == coding.system && value[:code] == coding.code
end
end
end
|
#resource_populates_element?(resource, element_definition) ⇒ Boolean
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 239
def resource_populates_element?(resource, element_definition)
raw_path = element_definition[:path]
path = navigation_compatible_must_support_path(raw_path)
ms_extension_urls = must_support_extensions.select { |ex| ex[:path] == "#{raw_path}.extension" }
.map { |ex| ex[:url] }
value_found = find_a_value_at(resource, path) do |potential_value|
matching_without_extensions?(potential_value, ms_extension_urls, element_definition[:fixed_value])
end
value_found.present? || value_found == false
end
|
#value_without_extensions?(value) ⇒ Boolean
349
350
351
352
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 349
def value_without_extensions?(value)
value_without_extensions = value.respond_to?(:to_hash) ? value.to_hash.except('extension') : value
value_without_extensions.present? || value_without_extensions == false
end
|
93
94
95
96
97
98
99
100
101
|
# File 'lib/inferno/dsl/must_support_assessment.rb', line 93
def write_metadata_for_debugging
outfile = "#{metadata.profile&.id}-#{SecureRandom.uuid}.yml"
File.open(File.join(Dir.tmpdir, outfile), 'w') do |f|
writable_metadata = { must_supports: @metadata.must_supports.to_hash }
f.write(YAML.dump(writable_metadata))
puts "Wrote MustSupport metadata to #{f.path}"
end
end
|