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.
- checksums.yaml +4 -4
- data/lib/inferno/apps/cli/evaluate.rb +97 -0
- data/lib/inferno/apps/cli/main.rb +38 -0
- data/lib/inferno/apps/cli/templates/%library_name%.gemspec.tt +10 -3
- data/lib/inferno/apps/cli/templates/Dockerfile.tt +3 -2
- data/lib/inferno/apps/cli/templates/lib/%library_name%/metadata.rb.tt +18 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/suite.rb.tt +59 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/version.rb.tt +3 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%.rb.tt +1 -58
- data/lib/inferno/apps/web/serializers/input.rb +2 -1
- data/lib/inferno/apps/web/serializers/markdown_extractor.rb +16 -0
- data/lib/inferno/config/boot/presets.rb +18 -1
- data/lib/inferno/dsl/fhir_evaluation/config.rb +21 -0
- data/lib/inferno/dsl/fhir_evaluation/dataset_loader.rb +33 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluation_context.rb +25 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluation_result.rb +62 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluator.rb +36 -0
- data/lib/inferno/dsl/fhir_evaluation/rule.rb +13 -0
- data/lib/inferno/dsl/suite_endpoint.rb +58 -58
- data/lib/inferno/dsl.rb +2 -0
- data/lib/inferno/entities/ig.rb +149 -0
- data/lib/inferno/entities/test_kit.rb +4 -2
- data/lib/inferno/entities/test_suite.rb +23 -3
- data/lib/inferno/entities.rb +2 -0
- data/lib/inferno/public/bundle.js +2 -2
- data/lib/inferno/repositories/igs.rb +9 -0
- data/lib/inferno/repositories/presets.rb +12 -6
- data/lib/inferno/result_summarizer.rb +2 -0
- data/lib/inferno/utils/ig_downloader.rb +3 -3
- data/lib/inferno/utils/named_thor_actions.rb +5 -1
- data/lib/inferno/version.rb +1 -1
- data/spec/extract_tgz_helper.rb +13 -0
- metadata +17 -5
- data/lib/inferno/public/175.bundle.js +0 -1
- 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
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
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
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
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
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
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
|
-
#
|
39
|
-
#
|
40
|
-
#
|
38
|
+
# class AuthorizedRequestSuite < Inferno::TestSuite
|
39
|
+
# id :authorized_suite
|
40
|
+
# suite_endpoint :get, '/authorized_endpoint', AuthorizedEndpoint
|
41
41
|
#
|
42
|
-
#
|
43
|
-
#
|
42
|
+
# group do
|
43
|
+
# title 'Authorized Request Group'
|
44
44
|
#
|
45
|
-
#
|
46
|
-
#
|
45
|
+
# test do
|
46
|
+
# title 'Wait for authorized request'
|
47
47
|
#
|
48
|
-
#
|
48
|
+
# input :bearer_token
|
49
49
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
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
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
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
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
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
|
-
#
|
117
|
-
#
|
118
|
-
#
|
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
|
-
#
|
169
|
-
#
|
170
|
-
#
|
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
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
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
|
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
|
-
|
94
|
+
@version = version if version.present?
|
94
95
|
|
95
|
-
@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
|
data/lib/inferno/entities.rb
CHANGED
@@ -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'
|