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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/NOTICE.md +12 -0
  3. data/config/presets/{localhost_preset.json → inferno_reference_server_preset.json.erb} +2 -2
  4. data/lib/us_quality_core_test_kit/client/generator/example_client_generator.rb +0 -1
  5. data/lib/us_quality_core_test_kit/client/generator/tags_generator.rb +0 -1
  6. data/lib/us_quality_core_test_kit/client/server_proxy.rb +2 -6
  7. data/lib/us_quality_core_test_kit/date_search_validation.rb +122 -0
  8. data/lib/us_quality_core_test_kit/fhir_resource_navigation.rb +9 -0
  9. data/lib/us_quality_core_test_kit/generated/v0.5.0/devicenotrequested/devicenotrequested_validation_test.rb +3 -0
  10. data/lib/us_quality_core_test_kit/generated/v0.5.0/devicerequest/devicerequest_validation_test.rb +3 -0
  11. data/lib/us_quality_core_test_kit/generated/v0.5.0/patient/patient_validation_test.rb +4 -0
  12. data/lib/us_quality_core_test_kit/generated/v0.5.0/us_quality_core_test_suite.rb +1 -2
  13. data/lib/us_quality_core_test_kit/generator/group_generator.rb +69 -3
  14. data/lib/us_quality_core_test_kit/generator/group_metadata.rb +100 -4
  15. data/lib/us_quality_core_test_kit/generator/group_metadata_extractor.rb +184 -8
  16. data/lib/us_quality_core_test_kit/generator/ig_loader.rb +47 -3
  17. data/lib/us_quality_core_test_kit/generator/ig_metadata.rb +51 -2
  18. data/lib/us_quality_core_test_kit/generator/ig_metadata_extractor.rb +25 -6
  19. data/lib/us_quality_core_test_kit/generator/ig_resources.rb +27 -3
  20. data/lib/us_quality_core_test_kit/generator/must_support_metadata_extractor.rb +366 -9
  21. data/lib/us_quality_core_test_kit/generator/must_support_test_generator.rb +54 -3
  22. data/lib/us_quality_core_test_kit/generator/provenance_revinclude_search_test_generator.rb +149 -3
  23. data/lib/us_quality_core_test_kit/generator/read_test_generator.rb +54 -3
  24. data/lib/us_quality_core_test_kit/generator/reference_resolution_test_generator.rb +57 -3
  25. data/lib/us_quality_core_test_kit/generator/search_definition_metadata_extractor.rb +181 -5
  26. data/lib/us_quality_core_test_kit/generator/search_metadata_extractor.rb +61 -3
  27. data/lib/us_quality_core_test_kit/generator/search_test_generator.rb +186 -3
  28. data/lib/us_quality_core_test_kit/generator/suite_generator.rb +35 -8
  29. data/lib/us_quality_core_test_kit/generator/terminology_binding_metadata_extractor.rb +107 -3
  30. data/lib/us_quality_core_test_kit/generator/validation_test_generator.rb +83 -4
  31. data/lib/us_quality_core_test_kit/generator/value_extractor.rb +147 -3
  32. data/lib/us_quality_core_test_kit/must_support_test.rb +15 -3
  33. data/lib/us_quality_core_test_kit/primitive_type.rb +7 -0
  34. data/lib/us_quality_core_test_kit/read_test.rb +99 -4
  35. data/lib/us_quality_core_test_kit/reference_resolution_test.rb +164 -3
  36. data/lib/us_quality_core_test_kit/resource_search_param_checker.rb +138 -0
  37. data/lib/us_quality_core_test_kit/search_test.rb +681 -46
  38. data/lib/us_quality_core_test_kit/search_test_properties.rb +53 -3
  39. data/lib/us_quality_core_test_kit/validation_test.rb +51 -4
  40. data/lib/us_quality_core_test_kit/version.rb +2 -2
  41. data/lib/us_quality_core_test_kit/well_known_code_systems.rb +23 -0
  42. 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 'special_cases'
7
- require_relative 'naming'
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 'must_support_metadata_extractor'
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 < USCoreTestKit::Generator::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 'us_core_test_kit/generator/ig_loader'
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 < USCoreTestKit::Generator::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 < USCoreTestKit::Generator::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 < USCoreTestKit::Generator::IGMetadataExtractor
12
- def initialize(ig_resources)
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 add_missing_supported_profiles; end
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 < USCoreTestKit::Generator::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