inferno_core 1.4.6 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/inferno/dsl/fhir_resource_navigation.rb +10 -5
- data/lib/inferno/dsl/must_support_assessment.rb +10 -20
- data/lib/inferno/dsl/must_support_strings.rb +62 -0
- data/lib/inferno/dsl/profile_metadata.rb +9 -18
- data/lib/inferno/dsl/value_extractor.rb +22 -4
- data/lib/inferno/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e77b2567061750720f73082094abe8f2906e060f362842640725481824dea6f3
|
|
4
|
+
data.tar.gz: 7b7ba8fa843ab58166f725b7939a74311825f959158b5eea06d954dbf1c9fe50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19f220a5663640cc07eb0182321aad839126f89e0e85b3c7c9d98971c4cde72b591fc5c86013c3cd8db3fc07f01bd08847b5b98ed82213c4819d75ddcf46a1e5
|
|
7
|
+
data.tar.gz: c0b1ba31490d081c3e4d351efa9ac182996952366e20b96fa75ec9e7fb816c01dbdba3c7d857746b45455181a098ee2b78fd2b389b65d1b2d0ec7179504d4482
|
|
@@ -252,16 +252,18 @@ module Inferno
|
|
|
252
252
|
|
|
253
253
|
# @private
|
|
254
254
|
def matching_pattern_codeable_concept_slice?(slice, discriminator)
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
codings = resolve_path(slice, discriminator[:path])
|
|
256
|
+
.flat_map { |codeable_concept| Array.wrap(codeable_concept.coding) }
|
|
257
|
+
codings.any? do |coding|
|
|
257
258
|
coding.code == discriminator[:code] && coding.system == discriminator[:system]
|
|
258
259
|
end
|
|
259
260
|
end
|
|
260
261
|
|
|
261
262
|
# @private
|
|
262
263
|
def matching_pattern_coding_slice?(slice, discriminator)
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
resolve_path(slice, discriminator[:path]).any? do |coding|
|
|
265
|
+
coding.code == discriminator[:code] && coding.system == discriminator[:system]
|
|
266
|
+
end
|
|
265
267
|
end
|
|
266
268
|
|
|
267
269
|
# @private
|
|
@@ -323,7 +325,10 @@ module Inferno
|
|
|
323
325
|
when String
|
|
324
326
|
value == coding.code
|
|
325
327
|
when Hash
|
|
326
|
-
|
|
328
|
+
next false unless value[:system] == coding.system
|
|
329
|
+
|
|
330
|
+
# A value without a code stands for every code in its system
|
|
331
|
+
value[:code].nil? ? coding.code.present? : value[:code] == coding.code
|
|
327
332
|
end
|
|
328
333
|
end
|
|
329
334
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative 'must_support_strings'
|
|
2
|
+
|
|
1
3
|
module Inferno
|
|
2
4
|
module DSL
|
|
3
5
|
# The MustSupportAssessment module contains the logic for tests
|
|
@@ -23,7 +25,7 @@ module Inferno
|
|
|
23
25
|
# test kits can subclass to build these objects, eg from generated YAML, instead of hand-rolling one.
|
|
24
26
|
# @param requirement_extension [String] Extension URL that implies "required" as an alternative to the MS flag
|
|
25
27
|
# @yield [MustSupportMetadataExtractor] Customize the metadata before running the test
|
|
26
|
-
# @return [Array<String>] List of missing elements
|
|
28
|
+
# @return [Array<String>] List of missing elements, sorted alphabetically
|
|
27
29
|
def missing_must_support_elements(resources, profile_url, validator_name: :default, metadata: nil,
|
|
28
30
|
requirement_extension: nil, &)
|
|
29
31
|
debug_metadata = config.options[:debug_must_support_metadata]
|
|
@@ -195,18 +197,17 @@ module Inferno
|
|
|
195
197
|
end
|
|
196
198
|
end
|
|
197
199
|
|
|
200
|
+
# See {MustSupportStrings} for how each missing item is represented. Sorted to match
|
|
201
|
+
# {ProfileMetadata#must_support_strings}.
|
|
198
202
|
def missing_must_support_strings
|
|
199
|
-
missing_elements.map { |element_definition| missing_element_string(element_definition) } +
|
|
200
|
-
missing_slices.map { |slice_definition| slice_definition
|
|
201
|
-
missing_extensions.map { |extension_definition| extension_definition
|
|
203
|
+
(missing_elements.map { |element_definition| missing_element_string(element_definition) } +
|
|
204
|
+
missing_slices.map { |slice_definition| MustSupportStrings.slice_string(slice_definition) } +
|
|
205
|
+
missing_extensions.map { |extension_definition| MustSupportStrings.extension_string(extension_definition) })
|
|
206
|
+
.sort
|
|
202
207
|
end
|
|
203
208
|
|
|
204
209
|
def missing_element_string(element_definition)
|
|
205
|
-
|
|
206
|
-
"#{element_definition[:path]}:#{element_definition[:fixed_value]}"
|
|
207
|
-
else
|
|
208
|
-
element_definition[:path]
|
|
209
|
-
end
|
|
210
|
+
MustSupportStrings.element_string(element_definition)
|
|
210
211
|
end
|
|
211
212
|
|
|
212
213
|
def must_support_extensions
|
|
@@ -452,17 +453,6 @@ module Inferno
|
|
|
452
453
|
def find_slice_by_values(element, value_definitions)
|
|
453
454
|
Array.wrap(element).find { |el| verify_slice_by_values(el, value_definitions) }
|
|
454
455
|
end
|
|
455
|
-
|
|
456
|
-
def required_binding_value_match?(coding, values)
|
|
457
|
-
values.any? do |value|
|
|
458
|
-
case value
|
|
459
|
-
when String
|
|
460
|
-
value == coding.code
|
|
461
|
-
when Hash
|
|
462
|
-
value[:system] == coding.system && value[:code] == coding.code
|
|
463
|
-
end
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
456
|
end
|
|
467
457
|
end
|
|
468
458
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Inferno
|
|
2
|
+
module DSL
|
|
3
|
+
# Builds the strings used to identify Must Support elements, slices, and extensions from their
|
|
4
|
+
# metadata, so that lists of what is expected to be supported (eg {ProfileMetadata#must_support_strings})
|
|
5
|
+
# and lists of what is missing (eg {MustSupportAssessment#missing_must_support_elements}) agree.
|
|
6
|
+
#
|
|
7
|
+
# Each item is represented by its ElementDefinition id (which includes the names of any parent
|
|
8
|
+
# slices) without the leading resource type, eg 'category:us-core' rather than
|
|
9
|
+
# 'Condition.category:us-core'. Elements with a fixed value have it appended, eg 'name.use = old'.
|
|
10
|
+
# @private
|
|
11
|
+
module MustSupportStrings
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# @param element_definition [Hash] Must Support element metadata
|
|
15
|
+
# @return [String]
|
|
16
|
+
def element_string(element_definition)
|
|
17
|
+
path = element_display_path(element_definition)
|
|
18
|
+
|
|
19
|
+
fixed_value = element_definition[:fixed_value]
|
|
20
|
+
if fixed_value.present? || fixed_value == false
|
|
21
|
+
"#{path} = #{fixed_value}"
|
|
22
|
+
else
|
|
23
|
+
path
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param slice_definition [Hash] Must Support slice metadata
|
|
28
|
+
# @return [String]
|
|
29
|
+
def slice_string(slice_definition)
|
|
30
|
+
id_string(slice_definition, :slice_id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @param extension_definition [Hash] Must Support extension metadata
|
|
34
|
+
# @return [String]
|
|
35
|
+
def extension_string(extension_definition)
|
|
36
|
+
id_string(extension_definition, :id)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Elements under a choice type slice (eg value[x]:valueQuantity.value) have their navigation
|
|
40
|
+
# path rewritten so the slice name is mangled (eg valueQuantity:valueQuantity.value), so report
|
|
41
|
+
# the original path instead. Elements where only some types of a choice are Must Support
|
|
42
|
+
# (eg original path effective[x], path effectiveDateTime) keep their path since it names the type.
|
|
43
|
+
def element_display_path(element_definition)
|
|
44
|
+
original_path = element_definition[:original_path]
|
|
45
|
+
original_path&.include?('[x]:') ? original_path : element_definition[:path]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def id_string(definition, id_key)
|
|
49
|
+
id = definition[id_key]
|
|
50
|
+
raise ArgumentError, "Must Support metadata is missing #{id_key.inspect}: #{definition.inspect}" if id.blank?
|
|
51
|
+
|
|
52
|
+
without_resource_type(id)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Resource types are PascalCase while element names are camelCase, so a leading
|
|
56
|
+
# capitalized segment is the resource type prefix of an ElementDefinition id.
|
|
57
|
+
def without_resource_type(id)
|
|
58
|
+
id&.sub(/\A[A-Z][A-Za-z0-9]*\./, '')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative 'must_support_strings'
|
|
2
|
+
|
|
1
3
|
module Inferno
|
|
2
4
|
module DSL
|
|
3
5
|
# A ProfileMetadata object is a small, inheritable data holder for generated metadata about a
|
|
@@ -85,29 +87,18 @@ module Inferno
|
|
|
85
87
|
# check, this includes everything that's expected to be supported, not just what's absent
|
|
86
88
|
# from a particular set of resources.
|
|
87
89
|
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# (eg `"
|
|
90
|
+
# Each item is represented the same way as in the list returned by a missing-elements check:
|
|
91
|
+
# by its ElementDefinition id (including the names of any parent slices) without the leading
|
|
92
|
+
# resource type (eg `"category:us-core"`, `"extension:us-core-race"`), with any matched
|
|
93
|
+
# `fixed_value` appended to elements (eg `"code.coding.code = 45473-6"`). See {MustSupportStrings}.
|
|
92
94
|
# @return [Array<String>]
|
|
93
95
|
def must_support_strings
|
|
94
|
-
element_strings =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
end
|
|
98
|
-
slice_strings =
|
|
99
|
-
Array.wrap(must_supports[:slices]).map { |slice| must_support_string(slice[:path], slice[:slice_name]) }
|
|
100
|
-
extension_strings =
|
|
101
|
-
Array.wrap(must_supports[:extensions]).map { |ext| must_support_string(ext[:path], ext[:slice_name]) }
|
|
96
|
+
element_strings = Array.wrap(must_supports[:elements]).map { |el| MustSupportStrings.element_string(el) }
|
|
97
|
+
slice_strings = Array.wrap(must_supports[:slices]).map { |slice| MustSupportStrings.slice_string(slice) }
|
|
98
|
+
extension_strings = Array.wrap(must_supports[:extensions]).map { |ex| MustSupportStrings.extension_string(ex) }
|
|
102
99
|
|
|
103
100
|
(element_strings + slice_strings + extension_strings).sort
|
|
104
101
|
end
|
|
105
|
-
|
|
106
|
-
private
|
|
107
|
-
|
|
108
|
-
def must_support_string(path, suffix)
|
|
109
|
-
suffix.present? ? "#{path}:#{suffix}" : path
|
|
110
|
-
end
|
|
111
102
|
end
|
|
112
103
|
end
|
|
113
104
|
end
|
|
@@ -60,9 +60,8 @@ module Inferno
|
|
|
60
60
|
def bound_systems_from_valueset_include_element(include_element)
|
|
61
61
|
if include_element.concept.present?
|
|
62
62
|
include_element
|
|
63
|
-
elsif
|
|
64
|
-
|
|
65
|
-
ig_resources.code_system_by_url(include_element.system)
|
|
63
|
+
elsif whole_code_system_include?(include_element)
|
|
64
|
+
whole_code_system(include_element)
|
|
66
65
|
elsif include_element.valueSet.present?
|
|
67
66
|
include_element.valueSet.map do |vs|
|
|
68
67
|
a_value_set = ig_resources.value_set_by_url(vs)
|
|
@@ -71,6 +70,18 @@ module Inferno
|
|
|
71
70
|
end
|
|
72
71
|
end
|
|
73
72
|
|
|
73
|
+
def whole_code_system_include?(include_element)
|
|
74
|
+
include_element.system.present? && include_element.concept.blank? &&
|
|
75
|
+
include_element.filter&.empty? && include_element.valueSet.blank?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# An include of a whole code system lists no codes. Use the code system's concepts when the IG
|
|
79
|
+
# has them, otherwise keep the include so that any code from its system is accepted.
|
|
80
|
+
def whole_code_system(include_element)
|
|
81
|
+
code_system = ig_resources.code_system_by_url(include_element.system)
|
|
82
|
+
code_system&.concept.present? ? code_system : include_element
|
|
83
|
+
end
|
|
84
|
+
|
|
74
85
|
def codes_from_value_set_binding(the_element)
|
|
75
86
|
codes_from_system_code_pair(codings_from_value_set_binding(the_element))
|
|
76
87
|
end
|
|
@@ -99,7 +110,7 @@ module Inferno
|
|
|
99
110
|
bound_systems.flat_map do |bound_system|
|
|
100
111
|
case bound_system
|
|
101
112
|
when FHIR::ValueSet::Compose::Include
|
|
102
|
-
bound_system
|
|
113
|
+
codings_from_include(bound_system)
|
|
103
114
|
when FHIR::CodeSystem
|
|
104
115
|
bound_system.concept.map { |concept| { system: bound_system.url, code: concept.code } }
|
|
105
116
|
else
|
|
@@ -108,6 +119,13 @@ module Inferno
|
|
|
108
119
|
end.uniq
|
|
109
120
|
end
|
|
110
121
|
|
|
122
|
+
# A whole code system include becomes a system-only value
|
|
123
|
+
def codings_from_include(include_element)
|
|
124
|
+
return [{ system: include_element.system }] if include_element.concept.blank?
|
|
125
|
+
|
|
126
|
+
include_element.concept.map { |concept| { system: include_element.system, code: concept.code } }
|
|
127
|
+
end
|
|
128
|
+
|
|
111
129
|
def value_set_expansion_contains(element)
|
|
112
130
|
value_set(element)&.expansion&.contains
|
|
113
131
|
end
|
data/lib/inferno/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inferno_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stephen MacVicar
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-10-
|
|
13
|
+
date: 2026-10-21 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activesupport
|
|
@@ -400,14 +400,14 @@ dependencies:
|
|
|
400
400
|
requirements:
|
|
401
401
|
- - "~>"
|
|
402
402
|
- !ruby/object:Gem::Version
|
|
403
|
-
version:
|
|
403
|
+
version: 3.0.0
|
|
404
404
|
type: :runtime
|
|
405
405
|
prerelease: false
|
|
406
406
|
version_requirements: !ruby/object:Gem::Requirement
|
|
407
407
|
requirements:
|
|
408
408
|
- - "~>"
|
|
409
409
|
- !ruby/object:Gem::Version
|
|
410
|
-
version:
|
|
410
|
+
version: 3.0.0
|
|
411
411
|
- !ruby/object:Gem::Dependency
|
|
412
412
|
name: sequel
|
|
413
413
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -678,6 +678,7 @@ files:
|
|
|
678
678
|
- lib/inferno/dsl/messages.rb
|
|
679
679
|
- lib/inferno/dsl/must_support_assessment.rb
|
|
680
680
|
- lib/inferno/dsl/must_support_metadata_extractor.rb
|
|
681
|
+
- lib/inferno/dsl/must_support_strings.rb
|
|
681
682
|
- lib/inferno/dsl/oauth_credentials.rb
|
|
682
683
|
- lib/inferno/dsl/primitive_type.rb
|
|
683
684
|
- lib/inferno/dsl/profile_metadata.rb
|