inferno_core 0.5.2 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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'