us_quality_core_test_kit 0.1.0 → 0.1.2
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/NOTICE.md +12 -0
- data/config/presets/{localhost_preset.json → inferno_reference_server_preset.json.erb} +2 -2
- data/lib/us_quality_core_test_kit/client/generator/example_client_generator.rb +0 -1
- data/lib/us_quality_core_test_kit/client/generator/tags_generator.rb +0 -1
- data/lib/us_quality_core_test_kit/client/server_proxy.rb +2 -6
- data/lib/us_quality_core_test_kit/date_search_validation.rb +122 -0
- data/lib/us_quality_core_test_kit/fhir_resource_navigation.rb +9 -0
- data/lib/us_quality_core_test_kit/generated/v0.5.0/devicenotrequested/devicenotrequested_validation_test.rb +3 -0
- data/lib/us_quality_core_test_kit/generated/v0.5.0/devicerequest/devicerequest_validation_test.rb +3 -0
- data/lib/us_quality_core_test_kit/generated/v0.5.0/patient/patient_validation_test.rb +4 -0
- data/lib/us_quality_core_test_kit/generated/v0.5.0/us_quality_core_test_suite.rb +1 -2
- data/lib/us_quality_core_test_kit/generator/group_generator.rb +69 -3
- data/lib/us_quality_core_test_kit/generator/group_metadata.rb +100 -4
- data/lib/us_quality_core_test_kit/generator/group_metadata_extractor.rb +184 -8
- data/lib/us_quality_core_test_kit/generator/ig_loader.rb +47 -3
- data/lib/us_quality_core_test_kit/generator/ig_metadata.rb +51 -2
- data/lib/us_quality_core_test_kit/generator/ig_metadata_extractor.rb +25 -6
- data/lib/us_quality_core_test_kit/generator/ig_resources.rb +27 -3
- data/lib/us_quality_core_test_kit/generator/must_support_metadata_extractor.rb +366 -9
- data/lib/us_quality_core_test_kit/generator/must_support_test_generator.rb +54 -3
- data/lib/us_quality_core_test_kit/generator/provenance_revinclude_search_test_generator.rb +149 -3
- data/lib/us_quality_core_test_kit/generator/read_test_generator.rb +54 -3
- data/lib/us_quality_core_test_kit/generator/reference_resolution_test_generator.rb +57 -3
- data/lib/us_quality_core_test_kit/generator/search_definition_metadata_extractor.rb +181 -5
- data/lib/us_quality_core_test_kit/generator/search_metadata_extractor.rb +61 -3
- data/lib/us_quality_core_test_kit/generator/search_test_generator.rb +186 -3
- data/lib/us_quality_core_test_kit/generator/suite_generator.rb +35 -8
- data/lib/us_quality_core_test_kit/generator/terminology_binding_metadata_extractor.rb +107 -3
- data/lib/us_quality_core_test_kit/generator/validation_test_generator.rb +83 -4
- data/lib/us_quality_core_test_kit/generator/value_extractor.rb +147 -3
- data/lib/us_quality_core_test_kit/must_support_test.rb +15 -3
- data/lib/us_quality_core_test_kit/primitive_type.rb +7 -0
- data/lib/us_quality_core_test_kit/read_test.rb +99 -4
- data/lib/us_quality_core_test_kit/reference_resolution_test.rb +164 -3
- data/lib/us_quality_core_test_kit/resource_search_param_checker.rb +138 -0
- data/lib/us_quality_core_test_kit/search_test.rb +681 -46
- data/lib/us_quality_core_test_kit/search_test_properties.rb +53 -3
- data/lib/us_quality_core_test_kit/validation_test.rb +51 -4
- data/lib/us_quality_core_test_kit/version.rb +2 -2
- data/lib/us_quality_core_test_kit/well_known_code_systems.rb +23 -0
- metadata +8 -17
|
@@ -1,19 +1,195 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'us_core_test_kit/generator/group_metadata_extractor'
|
|
4
|
-
|
|
5
3
|
require_relative 'group_metadata'
|
|
6
|
-
require_relative '
|
|
7
|
-
require_relative '
|
|
4
|
+
require_relative 'ig_metadata'
|
|
5
|
+
require_relative 'must_support_metadata_extractor'
|
|
8
6
|
require_relative 'search_metadata_extractor'
|
|
9
7
|
require_relative 'terminology_binding_metadata_extractor'
|
|
10
|
-
require_relative '
|
|
11
|
-
|
|
12
|
-
require_relative 'ig_metadata'
|
|
8
|
+
require_relative 'naming'
|
|
9
|
+
require_relative 'special_cases'
|
|
13
10
|
|
|
14
11
|
module USQualityCoreTestKit
|
|
15
12
|
class Generator
|
|
16
|
-
class GroupMetadataExtractor
|
|
13
|
+
class GroupMetadataExtractor
|
|
14
|
+
attr_accessor :resource_capabilities, :profile_url, :ig_metadata, :ig_resources
|
|
15
|
+
|
|
16
|
+
def initialize(resource_capabilities, profile_url, ig_metadata, ig_resources)
|
|
17
|
+
self.resource_capabilities = resource_capabilities
|
|
18
|
+
self.profile_url = profile_url
|
|
19
|
+
self.ig_metadata = ig_metadata
|
|
20
|
+
self.ig_resources = ig_resources
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def group_metadata_hash
|
|
24
|
+
@group_metadata_hash ||=
|
|
25
|
+
{
|
|
26
|
+
name: name,
|
|
27
|
+
class_name: class_name,
|
|
28
|
+
version: version,
|
|
29
|
+
reformatted_version: reformatted_version,
|
|
30
|
+
resource: resource,
|
|
31
|
+
profile_url: profile_url,
|
|
32
|
+
profile_name: profile_name,
|
|
33
|
+
profile_version: profile_version,
|
|
34
|
+
title: title,
|
|
35
|
+
short_description: short_description,
|
|
36
|
+
interactions: interactions,
|
|
37
|
+
operations: operations,
|
|
38
|
+
searches: searches,
|
|
39
|
+
search_definitions: search_definitions,
|
|
40
|
+
include_params: include_params,
|
|
41
|
+
revincludes: revincludes,
|
|
42
|
+
required_concepts: required_concepts,
|
|
43
|
+
must_supports: must_supports,
|
|
44
|
+
mandatory_elements: mandatory_elements,
|
|
45
|
+
bindings: bindings,
|
|
46
|
+
references: references,
|
|
47
|
+
resource_conformance_expectation: resource_conformance_expectation
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
mark_mandatory_and_must_support_searches
|
|
51
|
+
handle_special_cases
|
|
52
|
+
|
|
53
|
+
@group_metadata_hash
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
### BEGIN SPECIAL CASES ###
|
|
57
|
+
|
|
58
|
+
def handle_special_cases
|
|
59
|
+
set_first_search
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def set_first_search
|
|
63
|
+
search = searches.find { |param| param[:names] == first_search_params }
|
|
64
|
+
return if search.nil?
|
|
65
|
+
|
|
66
|
+
searches.delete(search)
|
|
67
|
+
searches.unshift(search)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
### END SPECIAL CASES ###
|
|
71
|
+
|
|
72
|
+
def profile
|
|
73
|
+
@profile ||= ig_resources.profile_by_url(profile_url)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def profile_elements
|
|
77
|
+
@profile_elements ||= profile.snapshot.element
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def base_name
|
|
81
|
+
profile_url.split('StructureDefinition/').last
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def name
|
|
85
|
+
base_name.tr('-', '_')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def version
|
|
89
|
+
ig_metadata.ig_version
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def reformatted_version
|
|
93
|
+
ig_metadata.reformatted_version
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def resource
|
|
97
|
+
resource_capabilities.type
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def profile_name
|
|
101
|
+
profile.title.gsub(' ', ' ')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def profile_version
|
|
105
|
+
profile.version
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def interactions
|
|
109
|
+
@interactions ||=
|
|
110
|
+
resource_capabilities.interaction.map do |interaction|
|
|
111
|
+
{
|
|
112
|
+
code: interaction.code,
|
|
113
|
+
expectation: interaction.extension.first.valueCode # TODO: fix expectation extension finding
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def operations
|
|
119
|
+
@operations ||=
|
|
120
|
+
resource_capabilities.operation.map do |operation|
|
|
121
|
+
{
|
|
122
|
+
code: operation.name,
|
|
123
|
+
expectation: operation.extension.first.valueCode # TODO: fix expectation extension finding
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def searches
|
|
129
|
+
@searches ||= search_metadata_extractor.searches
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def search_definitions
|
|
133
|
+
@search_definitions ||= search_metadata_extractor.search_definitions
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def include_params
|
|
137
|
+
resource_capabilities.searchInclude || []
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def revincludes
|
|
141
|
+
resource_capabilities.searchRevInclude || []
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def required_concepts
|
|
145
|
+
# The base FHIR vital signs profile has a required binding that isn't
|
|
146
|
+
# relevant for any of its child profiles
|
|
147
|
+
return [] if resource == 'Observation'
|
|
148
|
+
|
|
149
|
+
profile_elements
|
|
150
|
+
.select { |element| element.type&.any? { |type| type.code == 'CodeableConcept' } }
|
|
151
|
+
.select { |element| element.binding&.strength == 'required' }
|
|
152
|
+
.map { |element| element.path.gsub("#{resource}.", '').gsub('[x]', 'CodeableConcept') }
|
|
153
|
+
.uniq
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def bindings
|
|
157
|
+
@bindings ||=
|
|
158
|
+
terminology_binding_metadata_extractor.terminology_bindings
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def must_supports
|
|
162
|
+
@must_supports ||=
|
|
163
|
+
must_support_metadata_extractor.must_supports
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def mandatory_elements
|
|
167
|
+
@mandatory_elements ||=
|
|
168
|
+
profile_elements
|
|
169
|
+
.select { |element| element.min.positive? }
|
|
170
|
+
.map(&:path)
|
|
171
|
+
.uniq
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def references
|
|
175
|
+
@references ||=
|
|
176
|
+
profile_elements
|
|
177
|
+
.select { |element| element.type&.first&.code == 'Reference' }
|
|
178
|
+
.map do |reference_definition|
|
|
179
|
+
{
|
|
180
|
+
path: reference_definition.path,
|
|
181
|
+
profiles: reference_definition.type.first.targetProfile
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def resource_conformance_expectation
|
|
187
|
+
@resource_conformance_expectation ||=
|
|
188
|
+
resource_capabilities.extension.find do |extension|
|
|
189
|
+
extension.url == 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation'
|
|
190
|
+
end&.valueCode
|
|
191
|
+
end
|
|
192
|
+
|
|
17
193
|
def group_metadata
|
|
18
194
|
@group_metadata ||=
|
|
19
195
|
GroupMetadata.new(group_metadata_hash)
|
|
@@ -1,12 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
|
|
3
|
+
require 'active_support/all'
|
|
4
|
+
require 'fhir_models'
|
|
5
|
+
require 'pathname'
|
|
6
|
+
require 'rubygems/package'
|
|
7
|
+
require 'zlib'
|
|
5
8
|
require_relative 'ig_resources'
|
|
6
9
|
|
|
7
10
|
module USQualityCoreTestKit
|
|
8
11
|
class Generator
|
|
9
|
-
class IGLoader
|
|
12
|
+
class IGLoader
|
|
13
|
+
attr_accessor :ig_file_name
|
|
14
|
+
|
|
15
|
+
def initialize(ig_file_name)
|
|
16
|
+
self.ig_file_name = ig_file_name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def load
|
|
20
|
+
load_ig
|
|
21
|
+
load_standalone_resources
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def load_ig
|
|
25
|
+
tar = Gem::Package::TarReader.new(
|
|
26
|
+
Zlib::GzipReader.open(ig_file_name)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
tar.each do |entry|
|
|
30
|
+
next if entry.directory?
|
|
31
|
+
|
|
32
|
+
file_name = entry.full_name.split('/').last
|
|
33
|
+
|
|
34
|
+
next if file_name.end_with? 'openapi.json'
|
|
35
|
+
|
|
36
|
+
next unless file_name.end_with? '.json'
|
|
37
|
+
|
|
38
|
+
next unless entry.full_name.start_with? 'package/'
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
resource = FHIR.from_contents(entry.read)
|
|
42
|
+
next if resource.nil?
|
|
43
|
+
rescue StandardError
|
|
44
|
+
puts "#{file_name} does not appear to be a FHIR resource."
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
ig_resources.add(resource)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
ig_resources
|
|
52
|
+
end
|
|
53
|
+
|
|
10
54
|
def ig_resources
|
|
11
55
|
@ig_resources ||= IGResources.new
|
|
12
56
|
end
|
|
@@ -1,11 +1,60 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'us_core_test_kit/generator/ig_metadata'
|
|
4
3
|
require_relative 'special_cases'
|
|
5
4
|
|
|
6
5
|
module USQualityCoreTestKit
|
|
7
6
|
class Generator
|
|
8
|
-
class IGMetadata
|
|
7
|
+
class IGMetadata
|
|
8
|
+
attr_accessor :ig_version, :groups
|
|
9
|
+
|
|
10
|
+
def reformatted_version
|
|
11
|
+
@reformatted_version ||= ig_version.delete('.').gsub('-', '_')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def ordered_groups
|
|
15
|
+
@ordered_groups ||=
|
|
16
|
+
[patient_group] + non_delayed_groups + delayed_groups
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def patient_group
|
|
20
|
+
@patient_group ||=
|
|
21
|
+
groups.find { |group| group.resource == 'Patient' }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delayed_groups
|
|
25
|
+
@delayed_groups ||=
|
|
26
|
+
groups.select(&:delayed?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def non_delayed_groups
|
|
30
|
+
@non_delayed_groups ||=
|
|
31
|
+
groups.reject(&:delayed?) - [patient_group]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def postprocess_groups(ig_resources)
|
|
35
|
+
groups.each do |group|
|
|
36
|
+
group.add_delayed_references(delayed_profiles, ig_resources)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def granular_scope_resource_type_groups
|
|
41
|
+
@granular_scope_resource_type_groups ||=
|
|
42
|
+
Hash.new { |hash, key| hash[key] = [] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def granular_scope_groups
|
|
46
|
+
@granular_scope_groups ||= []
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_hash
|
|
50
|
+
{
|
|
51
|
+
ig_version:,
|
|
52
|
+
groups: groups.map(&:to_hash),
|
|
53
|
+
granular_scope_resource_type_groups:,
|
|
54
|
+
granular_scope_groups:
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
9
58
|
def delayed_profiles
|
|
10
59
|
@delayed_profiles ||=
|
|
11
60
|
delayed_groups.reject { |group| SpecialCases.exclude_group?(group) }.map(&:profile_url)
|
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'us_core_test_kit/generator/ig_metadata_extractor'
|
|
4
|
-
|
|
5
3
|
require_relative 'ig_metadata'
|
|
6
4
|
require_relative 'group_metadata_extractor'
|
|
7
5
|
require_relative 'special_cases'
|
|
8
6
|
|
|
9
7
|
module USQualityCoreTestKit
|
|
10
8
|
class Generator
|
|
11
|
-
class IGMetadataExtractor
|
|
12
|
-
|
|
13
|
-
super
|
|
9
|
+
class IGMetadataExtractor
|
|
10
|
+
attr_accessor :ig_resources, :metadata
|
|
14
11
|
|
|
12
|
+
def initialize(ig_resources)
|
|
13
|
+
self.ig_resources = ig_resources
|
|
14
|
+
remove_version_from_supported_profiles
|
|
15
|
+
remove_extra_supported_profiles
|
|
15
16
|
self.metadata = IGMetadata.new
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
def
|
|
19
|
+
def extract
|
|
20
|
+
add_metadata_from_ig
|
|
21
|
+
add_metadata_from_resources
|
|
22
|
+
metadata
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_metadata_from_ig
|
|
26
|
+
metadata.ig_version = "v#{ig_resources.ig.version}".delete('-ballot')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def resources_in_capability_statement
|
|
30
|
+
ig_resources.capability_statement.rest.first.resource
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def remove_version_from_supported_profiles
|
|
34
|
+
resources_in_capability_statement.each do |resource|
|
|
35
|
+
resource.supportedProfile.map! { |profile_url| profile_url.split('|').first }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
19
38
|
|
|
20
39
|
def remove_extra_supported_profiles
|
|
21
40
|
resources_in_capability_statement.each do |resource|
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'us_core_test_kit/generator/ig_resources'
|
|
4
|
-
|
|
5
3
|
module USQualityCoreTestKit
|
|
6
4
|
class Generator
|
|
7
|
-
class IGResources
|
|
5
|
+
class IGResources
|
|
6
|
+
def add(resource)
|
|
7
|
+
resources_by_type[resource.resourceType] << resource
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ig
|
|
11
|
+
resources_by_type['ImplementationGuide'].first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def inspect
|
|
15
|
+
'IGResources'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def value_set_by_url(url)
|
|
19
|
+
resources_by_type['ValueSet'].find { |profile| profile.url == url }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def code_system_by_url(url)
|
|
23
|
+
resources_by_type['CodeSystem'].find { |system| system.url == url }
|
|
24
|
+
end
|
|
25
|
+
|
|
8
26
|
def capability_statement(mode = 'server')
|
|
9
27
|
resources_by_type['CapabilityStatement'].reverse.find do |capability_statement_resource|
|
|
10
28
|
capability_statement_resource.rest.any? { |r| r.mode == mode }
|
|
@@ -56,6 +74,12 @@ module USQualityCoreTestKit
|
|
|
56
74
|
|
|
57
75
|
candidates.first
|
|
58
76
|
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def resources_by_type
|
|
81
|
+
@resources_by_type ||= Hash.new { |hash, key| hash[key] = [] }
|
|
82
|
+
end
|
|
59
83
|
end
|
|
60
84
|
end
|
|
61
85
|
end
|