inferno_core 0.5.2 → 0.5.4

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/inferno/apps/cli/evaluate.rb +97 -0
  3. data/lib/inferno/apps/cli/main.rb +38 -0
  4. data/lib/inferno/apps/cli/templates/%library_name%.gemspec.tt +10 -3
  5. data/lib/inferno/apps/cli/templates/Dockerfile.tt +3 -2
  6. data/lib/inferno/apps/cli/templates/lib/%library_name%/metadata.rb.tt +18 -0
  7. data/lib/inferno/apps/cli/templates/lib/%library_name%/suite.rb.tt +59 -0
  8. data/lib/inferno/apps/cli/templates/lib/%library_name%/version.rb.tt +3 -0
  9. data/lib/inferno/apps/cli/templates/lib/%library_name%.rb.tt +1 -58
  10. data/lib/inferno/apps/web/serializers/input.rb +2 -1
  11. data/lib/inferno/apps/web/serializers/markdown_extractor.rb +16 -0
  12. data/lib/inferno/config/boot/presets.rb +18 -1
  13. data/lib/inferno/dsl/fhir_evaluation/config.rb +21 -0
  14. data/lib/inferno/dsl/fhir_evaluation/dataset_loader.rb +33 -0
  15. data/lib/inferno/dsl/fhir_evaluation/evaluation_context.rb +25 -0
  16. data/lib/inferno/dsl/fhir_evaluation/evaluation_result.rb +62 -0
  17. data/lib/inferno/dsl/fhir_evaluation/evaluator.rb +36 -0
  18. data/lib/inferno/dsl/fhir_evaluation/rule.rb +13 -0
  19. data/lib/inferno/dsl/suite_endpoint.rb +58 -58
  20. data/lib/inferno/dsl.rb +2 -0
  21. data/lib/inferno/entities/ig.rb +149 -0
  22. data/lib/inferno/entities/test_kit.rb +4 -2
  23. data/lib/inferno/entities/test_suite.rb +23 -3
  24. data/lib/inferno/entities.rb +2 -0
  25. data/lib/inferno/public/bundle.js +2 -2
  26. data/lib/inferno/repositories/igs.rb +9 -0
  27. data/lib/inferno/repositories/presets.rb +12 -6
  28. data/lib/inferno/result_summarizer.rb +2 -0
  29. data/lib/inferno/utils/ig_downloader.rb +3 -3
  30. data/lib/inferno/utils/named_thor_actions.rb +5 -1
  31. data/lib/inferno/version.rb +1 -1
  32. data/spec/extract_tgz_helper.rb +13 -0
  33. metadata +17 -5
  34. data/lib/inferno/public/175.bundle.js +0 -1
  35. data/lib/inferno/public/217.bundle.js +0 -1
@@ -9,54 +9,54 @@ module Inferno
9
9
  # endpoint](https://github.com/hanami/controller/tree/v2.0.0).
10
10
  #
11
11
  # @example
12
- # class AuthorizedEndpoint < Inferno::DSL::SuiteEndpoint
13
- # # Identify the incoming request based on a bearer token
14
- # def test_run_identifier
15
- # request.header['authorization']&.delete_prefix('Bearer ')
16
- # end
12
+ # class AuthorizedEndpoint < Inferno::DSL::SuiteEndpoint
13
+ # # Identify the incoming request based on a bearer token
14
+ # def test_run_identifier
15
+ # request.header['authorization']&.delete_prefix('Bearer ')
16
+ # end
17
17
  #
18
- # # Return a json FHIR Patient resource
19
- # def make_response
20
- # response.status = 200
21
- # response.body = FHIR::Patient.new(id: 'abcdef').to_json
22
- # response.format = :json
23
- # end
18
+ # # Return a json FHIR Patient resource
19
+ # def make_response
20
+ # response.status = 200
21
+ # response.body = FHIR::Patient.new(id: 'abcdef').to_json
22
+ # response.format = :json
23
+ # end
24
24
  #
25
- # # Update the waiting test to pass when the incoming request is received.
26
- # # This will resume the test run.
27
- # def update_result
28
- # results_repo.update(result.id, result: 'pass')
29
- # end
25
+ # # Update the waiting test to pass when the incoming request is received.
26
+ # # This will resume the test run.
27
+ # def update_result
28
+ # results_repo.update(result.id, result: 'pass')
29
+ # end
30
30
  #
31
- # # Apply the 'authorized' tag to the incoming request so that it may be
32
- # # used by later tests.
33
- # def tags
34
- # ['authorized']
35
- # end
36
- # end
31
+ # # Apply the 'authorized' tag to the incoming request so that it may be
32
+ # # used by later tests.
33
+ # def tags
34
+ # ['authorized']
35
+ # end
36
+ # end
37
37
  #
38
- # class AuthorizedRequestSuite < Inferno::TestSuite
39
- # id :authorized_suite
40
- # suite_endpoint :get, '/authorized_endpoint', AuthorizedEndpoint
38
+ # class AuthorizedRequestSuite < Inferno::TestSuite
39
+ # id :authorized_suite
40
+ # suite_endpoint :get, '/authorized_endpoint', AuthorizedEndpoint
41
41
  #
42
- # group do
43
- # title 'Authorized Request Group'
42
+ # group do
43
+ # title 'Authorized Request Group'
44
44
  #
45
- # test do
46
- # title 'Wait for authorized request'
45
+ # test do
46
+ # title 'Wait for authorized request'
47
47
  #
48
- # input :bearer_token
48
+ # input :bearer_token
49
49
  #
50
- # run do
51
- # wait(
52
- # identifier: bearer_token,
53
- # message: "Waiting to receive a request with bearer_token: #{bearer_token}" \
54
- # "at `#{Inferno::Application['base_url']}/custom/authorized_suite/authorized_endpoint`"
55
- # )
50
+ # run do
51
+ # wait(
52
+ # identifier: bearer_token,
53
+ # message: "Waiting to receive a request with bearer_token: #{bearer_token}" \
54
+ # "at `#{Inferno::Application['base_url']}/custom/authorized_suite/authorized_endpoint`"
55
+ # )
56
+ # end
57
+ # end
56
58
  # end
57
59
  # end
58
- # end
59
- # end
60
60
  class SuiteEndpoint < Hanami::Action
61
61
  attr_reader :req, :res
62
62
 
@@ -69,11 +69,11 @@ module Inferno
69
69
  # @return [String]
70
70
  #
71
71
  # @example
72
- # def test_run_identifier
73
- # # Identify the test session of an incoming request based on the bearer
74
- # # token
75
- # request.headers['authorization']&.delete_prefix('Bearer ')
76
- # end
72
+ # def test_run_identifier
73
+ # # Identify the test session of an incoming request based on the bearer
74
+ # # token
75
+ # request.headers['authorization']&.delete_prefix('Bearer ')
76
+ # end
77
77
  def test_run_identifier
78
78
  nil
79
79
  end
@@ -83,11 +83,11 @@ module Inferno
83
83
  # @return [Void]
84
84
  #
85
85
  # @example
86
- # def make_response
87
- # response.status = 200
88
- # response.body = { abc: 123 }.to_json
89
- # response.format = :json
90
- # end
86
+ # def make_response
87
+ # response.status = 200
88
+ # response.body = { abc: 123 }.to_json
89
+ # response.format = :json
90
+ # end
91
91
  def make_response
92
92
  nil
93
93
  end
@@ -113,9 +113,9 @@ module Inferno
113
113
  # @return [Void]
114
114
  #
115
115
  # @example
116
- # def update_result
117
- # results_repo.update(result.id, result: 'pass')
118
- # end
116
+ # def update_result
117
+ # results_repo.update(result.id, result: 'pass')
118
+ # end
119
119
  def update_result
120
120
  nil
121
121
  end
@@ -165,9 +165,9 @@ module Inferno
165
165
  # @return [Hanami::Action::Request]
166
166
  #
167
167
  # @example
168
- # request.params # Get url/query params
169
- # request.body.read # Get body
170
- # request.headers['accept'] # Get Accept header
168
+ # request.params # Get url/query params
169
+ # request.body.read # Get body
170
+ # request.headers['accept'] # Get Accept header
171
171
  def request
172
172
  req
173
173
  end
@@ -178,10 +178,10 @@ module Inferno
178
178
  # @return [Hanami::Action::Response]
179
179
  #
180
180
  # @example
181
- # response.status = 200 # Set the status
182
- # response.body = 'Ok' # Set the body
183
- # # Set headers
184
- # response.headers.merge!('X-Custom-Header' => 'CUSTOM_HEADER_VALUE')
181
+ # response.status = 200 # Set the status
182
+ # response.body = 'Ok' # Set the body
183
+ # # Set headers
184
+ # response.headers.merge!('X-Custom-Header' => 'CUSTOM_HEADER_VALUE')
185
185
  def response
186
186
  res
187
187
  end
data/lib/inferno/dsl.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require_relative 'dsl/assertions'
2
2
  require_relative 'dsl/fhir_client'
3
3
  require_relative 'dsl/fhir_validation'
4
+ require_relative 'dsl/fhir_evaluation/evaluator'
4
5
  require_relative 'dsl/fhir_resource_validation'
5
6
  require_relative 'dsl/fhirpath_evaluation'
6
7
  require_relative 'dsl/http_client'
@@ -18,6 +19,7 @@ module Inferno
18
19
  HTTPClient,
19
20
  Results,
20
21
  FHIRValidation,
22
+ FHIREvaluation,
21
23
  FHIRResourceValidation,
22
24
  FhirpathEvaluation,
23
25
  Messages
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fhir_models'
4
+ require 'pathname'
5
+ require 'rubygems/package'
6
+ require 'zlib'
7
+
8
+ require_relative '../repositories/igs'
9
+
10
+ module Inferno
11
+ module Entities
12
+ # IG is a wrapper class around the relevant concepts inside an IG.
13
+ # Not everything within an IG is currently used by Inferno.
14
+ class IG < Entity
15
+ ATTRIBUTES = [
16
+ :id,
17
+ :profiles,
18
+ :extensions,
19
+ :value_sets,
20
+ :search_params,
21
+ :examples
22
+ ].freeze
23
+
24
+ include Inferno::Entities::Attributes
25
+
26
+ def initialize(**params)
27
+ super(params, ATTRIBUTES)
28
+
29
+ @profiles = []
30
+ @extensions = []
31
+ @value_sets = []
32
+ @examples = []
33
+ @search_params = []
34
+ end
35
+
36
+ def self.from_file(ig_path)
37
+ raise "#{ig_path} does not exist" unless File.exist?(ig_path)
38
+
39
+ # fhir_models by default logs the entire content of non-FHIR files
40
+ # which could be things like a package.json
41
+ original_logger = FHIR.logger
42
+ FHIR.logger = Logger.new('/dev/null')
43
+
44
+ if File.directory?(ig_path)
45
+ from_directory(ig_path)
46
+ elsif ig_path.end_with? '.tgz'
47
+ from_tgz(ig_path)
48
+ else
49
+ raise "Unable to load #{ig_path} as it does not appear to be a directory or a .tgz file"
50
+ end
51
+ ensure
52
+ FHIR.logger = original_logger if defined? original_logger
53
+ end
54
+
55
+ def self.from_tgz(ig_path)
56
+ tar = Gem::Package::TarReader.new(
57
+ Zlib::GzipReader.open(ig_path)
58
+ )
59
+
60
+ ig = IG.new
61
+
62
+ tar.each do |entry|
63
+ next if skip_item?(entry.full_name, entry.directory?)
64
+
65
+ begin
66
+ resource = FHIR::Json.from_json(entry.read)
67
+ next if resource.nil?
68
+
69
+ ig.handle_resource(resource, entry.full_name)
70
+ rescue StandardError
71
+ next
72
+ end
73
+ end
74
+ ig
75
+ end
76
+
77
+ def self.from_directory(ig_directory)
78
+ ig = IG.new
79
+
80
+ ig_path = Pathname.new(ig_directory)
81
+ Dir.glob("#{ig_path}/**/*") do |f|
82
+ relative_path = Pathname.new(f).relative_path_from(ig_path).to_s
83
+ next if skip_item?(relative_path, File.directory?(f))
84
+
85
+ begin
86
+ resource = FHIR::Json.from_json(File.read(f))
87
+ next if resource.nil?
88
+
89
+ ig.handle_resource(resource, relative_path)
90
+ rescue StandardError
91
+ next
92
+ end
93
+ end
94
+ ig
95
+ end
96
+
97
+ # These files aren't FHIR resources
98
+ FILES_TO_SKIP = ['package.json', 'validation-summary.json'].freeze
99
+
100
+ def self.skip_item?(relative_path, is_directory)
101
+ return true if is_directory
102
+
103
+ file_name = relative_path.split('/').last
104
+
105
+ return true unless file_name.end_with? '.json'
106
+ return true unless relative_path.start_with? 'package/'
107
+
108
+ return true if file_name.start_with? '.' # ignore hidden files
109
+ return true if file_name.end_with? '.openapi.json'
110
+ return true if FILES_TO_SKIP.include? file_name
111
+
112
+ false
113
+ end
114
+
115
+ def handle_resource(resource, relative_path)
116
+ case resource.resourceType
117
+ when 'StructureDefinition'
118
+ if resource.type == 'Extension'
119
+ extensions.push resource
120
+ else
121
+ profiles.push resource
122
+ end
123
+ when 'ValueSet'
124
+ value_sets.push resource
125
+ when 'SearchParameter'
126
+ search_params.push resource
127
+ when 'ImplementationGuide'
128
+ @id = extract_package_id(resource)
129
+ else
130
+ examples.push(resource) if relative_path.start_with? 'package/example'
131
+ end
132
+ end
133
+
134
+ def extract_package_id(ig_resource)
135
+ "#{ig_resource.id}##{ig_resource.version || 'current'}"
136
+ end
137
+
138
+ # @private
139
+ def add_self_to_repository
140
+ repository.insert(self)
141
+ end
142
+
143
+ # @private
144
+ def repository
145
+ Inferno::Repositories::IGs.new
146
+ end
147
+ end
148
+ end
149
+ end
@@ -6,7 +6,7 @@ module Inferno
6
6
  # @example
7
7
  #
8
8
  # module USCoreTestKit
9
- # class TestKit < Inferno::Entities::TestKit
9
+ # class Metadata < Inferno::Entities::TestKit
10
10
  # id :us_core
11
11
  # title 'US Core Test Kit'
12
12
  # description <<~DESCRIPTION
@@ -156,7 +156,7 @@ module Inferno
156
156
 
157
157
  # @private
158
158
  def repository
159
- @repository ||= Inferno::Repositories::TestKits
159
+ @repository ||= Inferno::Repositories::TestKits.new
160
160
  end
161
161
 
162
162
  # @private
@@ -168,4 +168,6 @@ module Inferno
168
168
  end
169
169
  end
170
170
  end
171
+
172
+ TestKit = Entities::TestKit
171
173
  end
@@ -84,15 +84,16 @@ module Inferno
84
84
  }
85
85
  end
86
86
 
87
- # Set/get the version of this test suite.
87
+ # Set/get the version of this test suite. Defaults to the TestKit
88
+ # version.
88
89
  #
89
90
  # @param version [String]
90
91
  #
91
92
  # @return [String, nil]
92
93
  def version(version = nil)
93
- return @version if version.nil?
94
+ @version = version if version.present?
94
95
 
95
- @version = version
96
+ @version || test_kit&.version
96
97
  end
97
98
 
98
99
  # @private
@@ -186,6 +187,25 @@ module Inferno
186
187
 
187
188
  @suite_summary = format_markdown(suite_summary)
188
189
  end
190
+
191
+ # Get the TestKit this suite belongs to
192
+ #
193
+ # @return [Inferno::Entities::TestKit]
194
+ def test_kit
195
+ return @test_kit if @test_kit
196
+
197
+ module_name = name
198
+
199
+ while module_name.present? && @test_kit.nil?
200
+ module_name = module_name.deconstantize
201
+
202
+ next unless const_defined?("#{module_name}::Metadata")
203
+
204
+ @test_kit = const_get("#{module_name}::Metadata")
205
+ end
206
+
207
+ @test_kit
208
+ end
189
209
  end
190
210
  end
191
211
  end
@@ -2,12 +2,14 @@ require_relative 'entities/attributes'
2
2
  require_relative 'entities/has_runnable'
3
3
  require_relative 'entities/entity'
4
4
  require_relative 'entities/header'
5
+ require_relative 'entities/ig'
5
6
  require_relative 'entities/message'
6
7
  require_relative 'entities/request'
7
8
  require_relative 'entities/result'
8
9
  require_relative 'entities/session_data'
9
10
  require_relative 'entities/test'
10
11
  require_relative 'entities/test_group'
12
+ require_relative 'entities/test_kit'
11
13
  require_relative 'entities/test_run'
12
14
  require_relative 'entities/test_session'
13
15
  require_relative 'entities/test_suite'