inferno_core 0.3.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 884936b347ee4892198605b7ae408f3889e74bed0a4e33b6ec2a5ee7ed3adcd9
4
- data.tar.gz: b0e30f11313a6a5b690e96a08fdb083ba9d85b94b512ec105fce75631802f0cb
3
+ metadata.gz: 5c905fd9a450198c42daa6a7f3119277d1eeb1e39acb788856d90c3817719d02
4
+ data.tar.gz: e11d6fcfa8acb3c487c62d512b9210db18f5d7bba5c9148063587de8a3e78420
5
5
  SHA512:
6
- metadata.gz: 13b145c224d9b762b2ae8019048d6775dea2bd0ce47abcb3450959e3148224a523914eaf06b480a25d1a065c449e32b9c64ed83056118b611e8be2c4d9640755
7
- data.tar.gz: 78a7bd5437739a2e1e7384dc500d46a99c8795f03638023839fdf4c24131fcc9a6ba8ca1ec507cd0b0bd559d8c9741e07ce2c6e3b04954b72bf6ba4ce7158805
6
+ metadata.gz: d62c7ebc7b9f9ac2024bfb933254cd54010b869fde72d15c77e935e7228096e2ba45844e3f8ba2f7b28f71315120d7a194b63d7e95675600e26efd1bbe822cc1
7
+ data.tar.gz: ed6b0d392219155cd2cd6865e01d9ba838c067f36bfdd86ac10e80e923b51ddf688cff4f8e8a67e62e53a1d28c151f1c76d5e791166e920ff9832676fa1239c0
@@ -11,7 +11,7 @@ Inferno::Application.boot(:db) do
11
11
 
12
12
  config_path = File.expand_path('database.yml', File.join(Dir.pwd, 'config'))
13
13
  config_contents = ERB.new(File.read(config_path)).result
14
- config = YAML.safe_load(config_contents)[ENV['APP_ENV']]
14
+ config = YAML.safe_load(config_contents)[ENV.fetch('APP_ENV', nil)]
15
15
  .merge(logger: Inferno::Application['logger'])
16
16
  connection_attempts_remaining = ENV.fetch('MAX_DB_CONNECTION_ATTEMPTS', '10').to_i
17
17
  connection_retry_delay = ENV.fetch('DB_CONNECTION_RETRY_DELAY', '5').to_i
@@ -4,4 +4,4 @@ ENV['APP_ENV'] ||= 'development'
4
4
 
5
5
  root_path = Dir.pwd
6
6
 
7
- Dotenv.load(File.join(root_path, '.env'), File.join(root_path, ".env.#{ENV['APP_ENV']}"))
7
+ Dotenv.load(File.join(root_path, '.env'), File.join(root_path, ".env.#{ENV.fetch('APP_ENV', nil)}"))
@@ -16,7 +16,7 @@ module Inferno
16
16
 
17
17
  @config.apply(new_configuration)
18
18
 
19
- children.each { |child| child.config(new_configuration) }
19
+ all_children.each { |child| child.config(new_configuration) }
20
20
 
21
21
  @config
22
22
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'request_storage'
2
+ require_relative 'tcp_exception_handler'
2
3
 
3
4
  module Inferno
4
5
  module DSL
@@ -40,6 +41,7 @@ module Inferno
40
41
  klass.extend ClassMethods
41
42
  klass.extend Forwardable
42
43
  klass.include RequestStorage
44
+ klass.include TCPExceptionHandler
43
45
 
44
46
  klass.def_delegators 'self.class', :profile_url, :validator_url
45
47
  end
@@ -73,11 +75,13 @@ module Inferno
73
75
  # @return [Inferno::Entities::Request]
74
76
  def fhir_operation(path, body: nil, client: :default, name: nil, headers: {})
75
77
  store_request_and_refresh_token(fhir_client(client), name) do
76
- operation_headers = fhir_client(client).fhir_headers
77
- operation_headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
78
- operation_headers.merge!(headers) if headers.present?
78
+ tcp_exception_handler do
79
+ operation_headers = fhir_client(client).fhir_headers
80
+ operation_headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
81
+ operation_headers.merge!(headers) if headers.present?
79
82
 
80
- fhir_client(client).send(:post, path, body, operation_headers)
83
+ fhir_client(client).send(:post, path, body, operation_headers)
84
+ end
81
85
  end
82
86
  end
83
87
 
@@ -89,8 +93,10 @@ module Inferno
89
93
  # @return [Inferno::Entities::Request]
90
94
  def fhir_get_capability_statement(client: :default, name: nil)
91
95
  store_request_and_refresh_token(fhir_client(client), name) do
92
- fhir_client(client).conformance_statement
93
- fhir_client(client).reply
96
+ tcp_exception_handler do
97
+ fhir_client(client).conformance_statement
98
+ fhir_client(client).reply
99
+ end
94
100
  end
95
101
  end
96
102
 
@@ -104,7 +110,9 @@ module Inferno
104
110
  # @return [Inferno::Entities::Request]
105
111
  def fhir_read(resource_type, id, client: :default, name: nil)
106
112
  store_request_and_refresh_token(fhir_client(client), name) do
107
- fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
113
+ tcp_exception_handler do
114
+ fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
115
+ end
108
116
  end
109
117
  end
110
118
 
@@ -126,8 +134,10 @@ module Inferno
126
134
  end
127
135
 
128
136
  store_request_and_refresh_token(fhir_client(client), name) do
129
- fhir_client(client)
130
- .search(fhir_class_from_resource_type(resource_type), { search: search })
137
+ tcp_exception_handler do
138
+ fhir_client(client)
139
+ .search(fhir_class_from_resource_type(resource_type), { search: search })
140
+ end
131
141
  end
132
142
  end
133
143
 
@@ -141,7 +151,9 @@ module Inferno
141
151
  # @return [Inferno::Entities::Request]
142
152
  def fhir_delete(resource_type, id, client: :default, name: nil)
143
153
  store_request('outgoing', name) do
144
- fhir_client(client).destroy(fhir_class_from_resource_type(resource_type), id)
154
+ tcp_exception_handler do
155
+ fhir_client(client).destroy(fhir_class_from_resource_type(resource_type), id)
156
+ end
145
157
  end
146
158
  end
147
159
 
@@ -1,5 +1,4 @@
1
1
  require_relative '../ext/fhir_models'
2
-
3
2
  module Inferno
4
3
  module DSL
5
4
  # This module contains the methods needed to configure a validator to
@@ -39,13 +38,16 @@ module Inferno
39
38
  # Find a particular validator. Looks through a runnable's parents up to
40
39
  # the suite to find a validator with a particular name
41
40
  def find_validator(validator_name)
42
- self.class.find_validator(validator_name)
41
+ self.class.find_validator(validator_name, suite_options)
43
42
  end
44
43
 
45
44
  class Validator
45
+ attr_reader :requirements
46
+
46
47
  # @private
47
- def initialize(&block)
48
+ def initialize(requirements = nil, &block)
48
49
  instance_eval(&block)
50
+ @requirements = requirements
49
51
  end
50
52
 
51
53
  # @private
@@ -115,7 +117,7 @@ module Inferno
115
117
 
116
118
  outcome = FHIR::OperationOutcome.new(JSON.parse(validate(resource, profile_url)))
117
119
 
118
- message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue) } || []
120
+ message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue, resource) } || []
119
121
 
120
122
  message_hashes.concat(additional_validation_messages(resource, profile_url))
121
123
 
@@ -132,10 +134,10 @@ module Inferno
132
134
  end
133
135
 
134
136
  # @private
135
- def message_hash_from_issue(issue)
137
+ def message_hash_from_issue(issue, resource)
136
138
  {
137
139
  type: issue_severity(issue),
138
- message: issue_message(issue)
140
+ message: issue_message(issue, resource)
139
141
  }
140
142
  end
141
143
 
@@ -152,14 +154,16 @@ module Inferno
152
154
  end
153
155
 
154
156
  # @private
155
- def issue_message(issue)
157
+ def issue_message(issue, resource)
156
158
  location = if issue.respond_to?(:expression)
157
159
  issue.expression&.join(', ')
158
160
  else
159
161
  issue.location&.join(', ')
160
162
  end
161
163
 
162
- "#{location}: #{issue&.details&.text}"
164
+ location_prefix = resource.id ? "#{resource.resourceType}/#{resource.id}" : resource.resourceType
165
+
166
+ "#{location_prefix}: #{location}: #{issue&.details&.text}"
163
167
  end
164
168
 
165
169
  # Post a resource to the validation service for validating.
@@ -198,14 +202,33 @@ module Inferno
198
202
  #
199
203
  # @param name [Symbol] the name of the validator, only needed if you are
200
204
  # using multiple validators
201
- def validator(name = :default, &block)
202
- fhir_validators[name] = Inferno::DSL::FHIRValidation::Validator.new(&block)
205
+ # @param required_suite_options [Hash] suite options that must be
206
+ # selected in order to use this validator
207
+ def validator(name = :default, required_suite_options: nil, &block)
208
+ current_validators = fhir_validators[name] || []
209
+
210
+ new_validator = Inferno::DSL::FHIRValidation::Validator.new(required_suite_options, &block)
211
+
212
+ current_validators.reject! { |validator| validator.requirements == required_suite_options }
213
+ current_validators << new_validator
214
+
215
+ fhir_validators[name] = current_validators
203
216
  end
204
217
 
205
218
  # Find a particular validator. Looks through a runnable's parents up to
206
219
  # the suite to find a validator with a particular name
207
- def find_validator(validator_name)
208
- validator = fhir_validators[validator_name] || parent&.find_validator(validator_name)
220
+ def find_validator(validator_name, selected_suite_options = nil)
221
+ validators = fhir_validators[validator_name] ||
222
+ Array.wrap(parent&.find_validator(validator_name, selected_suite_options))
223
+
224
+ validator =
225
+ if selected_suite_options.present?
226
+ validators.find do |possible_validator|
227
+ possible_validator.requirements.nil? || selected_suite_options >= possible_validator.requirements
228
+ end
229
+ else
230
+ validators.first
231
+ end
209
232
 
210
233
  raise Exceptions::ValidatorNotFoundException, validator_name if validator.nil?
211
234
 
@@ -1,4 +1,5 @@
1
1
  require_relative 'request_storage'
2
+ require_relative 'tcp_exception_handler'
2
3
 
3
4
  module Inferno
4
5
  module DSL
@@ -31,6 +32,7 @@ module Inferno
31
32
  def self.included(klass)
32
33
  klass.extend ClassMethods
33
34
  klass.include RequestStorage
35
+ klass.include TCPExceptionHandler
34
36
  end
35
37
 
36
38
  # Return a previously defined HTTP client
@@ -44,7 +46,9 @@ module Inferno
44
46
  definition = self.class.http_client_definitions[client]
45
47
  return nil if definition.nil?
46
48
 
47
- http_clients[client] = HTTPClientBuilder.new.build(self, definition)
49
+ tcp_exception_handler do
50
+ http_clients[client] = HTTPClientBuilder.new.build(self, definition)
51
+ end
48
52
  end
49
53
 
50
54
  # @private
@@ -65,14 +69,16 @@ module Inferno
65
69
  # @return [Inferno::Entities::Request]
66
70
  def get(url = '', client: :default, name: nil, **options)
67
71
  store_request('outgoing', name) do
68
- client = http_client(client)
69
-
70
- if client
71
- client.get(url, nil, options[:headers])
72
- elsif url.match?(%r{\Ahttps?://})
73
- Faraday.get(url, nil, options[:headers])
74
- else
75
- raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
72
+ tcp_exception_handler do
73
+ client = http_client(client)
74
+
75
+ if client
76
+ client.get(url, nil, options[:headers])
77
+ elsif url.match?(%r{\Ahttps?://})
78
+ Faraday.get(url, nil, options[:headers])
79
+ else
80
+ raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
81
+ end
76
82
  end
77
83
  end
78
84
  end
@@ -91,14 +97,16 @@ module Inferno
91
97
  # @return [Inferno::Entities::Request]
92
98
  def post(url = '', body: nil, client: :default, name: nil, **options)
93
99
  store_request('outgoing', name) do
94
- client = http_client(client)
95
-
96
- if client
97
- client.post(url, body, options[:headers])
98
- elsif url.match?(%r{\Ahttps?://})
99
- Faraday.post(url, body, options[:headers])
100
- else
101
- raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
100
+ tcp_exception_handler do
101
+ client = http_client(client)
102
+
103
+ if client
104
+ client.post(url, body, options[:headers])
105
+ elsif url.match?(%r{\Ahttps?://})
106
+ Faraday.post(url, body, options[:headers])
107
+ else
108
+ raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
109
+ end
102
110
  end
103
111
  end
104
112
  end
@@ -114,14 +122,16 @@ module Inferno
114
122
  # @return [Inferno::Entities::Request]
115
123
  def delete(url = '', client: :default, name: :nil, **options)
116
124
  store_request('outgoing', name) do
117
- client = http_client(client)
118
-
119
- if client
120
- client.delete(url, nil, options[:headers])
121
- elsif url.match?(%r{\Ahttps?://})
122
- Faraday.delete(url, nil, options[:headers])
123
- else
124
- raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
125
+ tcp_exception_handler do
126
+ client = http_client(client)
127
+
128
+ if client
129
+ client.delete(url, nil, options[:headers])
130
+ elsif url.match?(%r{\Ahttps?://})
131
+ Faraday.delete(url, nil, options[:headers])
132
+ else
133
+ raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
134
+ end
125
135
  end
126
136
  end
127
137
  end
@@ -151,17 +161,19 @@ module Inferno
151
161
  end
152
162
 
153
163
  store_request('outgoing', name) do
154
- client = http_client(client)
155
-
156
- if client
157
- response = client.get(url, nil, options[:headers]) { |req| req.options.on_data = collector }
158
- elsif url.match?(%r{\Ahttps?://})
159
- response = Faraday.get(url, nil, options[:headers]) { |req| req.options.on_data = collector }
160
- else
161
- raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
164
+ tcp_exception_handler do
165
+ client = http_client(client)
166
+
167
+ if client
168
+ response = client.get(url, nil, options[:headers]) { |req| req.options.on_data = collector }
169
+ elsif url.match?(%r{\Ahttps?://})
170
+ response = Faraday.get(url, nil, options[:headers]) { |req| req.options.on_data = collector }
171
+ else
172
+ raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
173
+ end
174
+ response.env.body = streamed.join
175
+ response
162
176
  end
163
- response.env.body = streamed.join
164
- response
165
177
  end
166
178
  end
167
179
 
@@ -125,7 +125,7 @@ module Inferno
125
125
  def all_outputs
126
126
  outputs
127
127
  .map { |output_identifier| config.output_name(output_identifier) }
128
- .concat(children.flat_map(&:all_outputs))
128
+ .concat(all_children.flat_map(&:all_outputs))
129
129
  .uniq
130
130
  end
131
131
 
@@ -137,7 +137,7 @@ module Inferno
137
137
  @children_available_inputs ||=
138
138
  begin
139
139
  child_outputs = []
140
- children.each_with_object({}) do |child, definitions|
140
+ all_children.each_with_object({}) do |child, definitions|
141
141
  new_definitions = child.available_inputs.map(&:dup)
142
142
  new_definitions.each do |input, new_definition|
143
143
  existing_definition = definitions[input]
@@ -9,6 +9,7 @@ module Inferno
9
9
  # definition framework.
10
10
  module Runnable
11
11
  attr_accessor :parent
12
+ attr_reader :suite_option_requirements
12
13
 
13
14
  include Inferno::Utils::MarkdownFormatter
14
15
 
@@ -48,7 +49,7 @@ module Inferno
48
49
  VARIABLES_NOT_TO_COPY = [
49
50
  :@id, # New runnable will have a different id
50
51
  :@parent, # New runnable unlikely to have the same parent
51
- :@children, # New subclasses have to be made for each child
52
+ :@all_children, # New subclasses have to be made for each child
52
53
  :@test_count, # Needs to be recalculated
53
54
  :@config, # Needs to be set by calling .config, which does extra work
54
55
  :@available_inputs, # Needs to be recalculated
@@ -63,13 +64,13 @@ module Inferno
63
64
 
64
65
  subclass.config(config)
65
66
 
66
- new_children = children.map do |child|
67
+ new_children = all_children.map do |child|
67
68
  Class.new(child).tap do |subclass_child|
68
69
  subclass_child.parent = subclass
69
70
  end
70
71
  end
71
72
 
72
- subclass.instance_variable_set(:@children, new_children)
73
+ subclass.instance_variable_set(:@all_children, new_children)
73
74
  end
74
75
 
75
76
  # @private
@@ -94,7 +95,7 @@ module Inferno
94
95
 
95
96
  klass.parent = self
96
97
 
97
- children << klass
98
+ all_children << klass
98
99
 
99
100
  configure_child_class(klass, hash_args)
100
101
 
@@ -172,7 +173,7 @@ module Inferno
172
173
 
173
174
  klass.config(config)
174
175
 
175
- klass.children.select!(&:required?) if hash_args.delete(:exclude_optional)
176
+ klass.all_children.select!(&:required?) if hash_args.delete(:exclude_optional)
176
177
 
177
178
  hash_args.each do |key, value|
178
179
  if value.is_a? Array
@@ -182,7 +183,7 @@ module Inferno
182
183
  end
183
184
  end
184
185
 
185
- klass.children.each do |child_class|
186
+ klass.all_children.each do |child_class|
186
187
  klass.configure_child_class(child_class, {})
187
188
  child_class.add_self_to_repository
188
189
  end
@@ -302,8 +303,8 @@ module Inferno
302
303
  end
303
304
 
304
305
  # @private
305
- def children
306
- @children ||= []
306
+ def all_children
307
+ @all_children ||= []
307
308
  end
308
309
 
309
310
  def validator_url(url = nil)
@@ -378,8 +379,12 @@ module Inferno
378
379
  end
379
380
 
380
381
  # @private
381
- def test_count
382
- @test_count ||= children&.reduce(0) { |sum, child| sum + child.test_count } || 0
382
+ def test_count(selected_suite_options = {})
383
+ @test_counts ||= {}
384
+
385
+ @test_counts[selected_suite_options] ||=
386
+ children(selected_suite_options)
387
+ &.reduce(0) { |sum, child| sum + child.test_count(selected_suite_options) } || 0
383
388
  end
384
389
 
385
390
  # @private
@@ -388,6 +393,21 @@ module Inferno
388
393
  !parent.respond_to?(:run_as_group?) ||
389
394
  (parent.user_runnable? && !parent.run_as_group?)
390
395
  end
396
+
397
+ def required_suite_options(suite_option_requirements)
398
+ @suite_option_requirements = suite_option_requirements
399
+ end
400
+
401
+ def children(selected_suite_options = nil)
402
+ return all_children if selected_suite_options.blank?
403
+
404
+ all_children.select do |child|
405
+ requirements = child.suite_option_requirements || {}
406
+
407
+ # requirements are a subset of selected options or equal to selected options
408
+ selected_suite_options >= requirements
409
+ end
410
+ end
391
411
  end
392
412
  end
393
413
  end
@@ -0,0 +1,11 @@
1
+ module Inferno
2
+ module DSL
3
+ module TCPExceptionHandler
4
+ def tcp_exception_handler(&block)
5
+ block.call
6
+ rescue Faraday::ConnectionFailed, SocketError => e
7
+ e.message.include?('Failed to open TCP') ? raise(Exceptions::AssertionException, e.message) : raise(e)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -13,13 +13,14 @@ module Inferno
13
13
  def_delegators 'self.class', :title, :id, :block, :inputs, :outputs
14
14
 
15
15
  attr_accessor :result_message
16
- attr_reader :test_session_id, :scratch
16
+ attr_reader :test_session_id, :scratch, :suite_options
17
17
 
18
18
  # @private
19
19
  def initialize(**params)
20
20
  params[:inputs]&.each { |key, value| instance_variable_set("@#{key}", value) }
21
21
  @scratch = params[:scratch]
22
22
  @test_session_id = params[:test_session_id]
23
+ @suite_options = params[:suite_options].presence || {}
23
24
  end
24
25
 
25
26
  # @private
@@ -195,7 +196,8 @@ module Inferno
195
196
  end
196
197
 
197
198
  # @private
198
- def test_count
199
+ # Has an unused argument to match the method signature of Runnable#test_count
200
+ def test_count(_ = nil)
199
201
  1
200
202
  end
201
203
 
@@ -32,11 +32,11 @@ module Inferno
32
32
  end
33
33
 
34
34
  def groups
35
- children.select { |child| child < Inferno::Entities::TestGroup }
35
+ all_children.select { |child| child < Inferno::Entities::TestGroup }
36
36
  end
37
37
 
38
38
  def tests
39
- children.select { |child| child < Inferno::Entities::Test }
39
+ all_children.select { |child| child < Inferno::Entities::Test }
40
40
  end
41
41
 
42
42
  # Methods to configure Inferno::DSL::Runnable
@@ -19,8 +19,19 @@ module Inferno
19
19
  # @!attribute results
20
20
  # @return [Array<Inferno::Entities::TestResult>] the `TestResults`
21
21
  # associated with this session
22
+ # @!attribute suite_options
23
+ # @return [Hash] the suite options associated with this session
22
24
  class TestSession < Entity
23
- ATTRIBUTES = [:id, :created_at, :updated_at, :test_suite_id, :test_suite, :test_runs, :results].freeze
25
+ ATTRIBUTES = [
26
+ :id,
27
+ :created_at,
28
+ :updated_at,
29
+ :test_suite_id,
30
+ :test_suite,
31
+ :test_runs,
32
+ :results,
33
+ :suite_options
34
+ ].freeze
24
35
 
25
36
  include Inferno::Entities::Attributes
26
37
 
@@ -22,7 +22,7 @@ module Inferno
22
22
  return @default_group if @default_group
23
23
 
24
24
  @default_group = Class.new(TestGroup)
25
- children << @default_group
25
+ all_children << @default_group
26
26
  @default_group
27
27
  end
28
28
 
@@ -31,7 +31,7 @@ module Inferno
31
31
  end
32
32
 
33
33
  def groups
34
- children.select { |child| child < Inferno::Entities::TestGroup }
34
+ all_children.select { |child| child < Inferno::Entities::TestGroup }
35
35
  end
36
36
 
37
37
  # Methods to configure Inferno::DSL::Runnable
@@ -60,17 +60,6 @@ module Inferno
60
60
  @version = version
61
61
  end
62
62
 
63
- def find_validator(validator_name)
64
- validator = fhir_validators[validator_name]
65
-
66
- return validator if validator
67
-
68
- raise Exceptions::ValidatorNotFoundException, validator_name unless validator_name == :default
69
-
70
- fhir_validators[:default] =
71
- Inferno::DSL::FHIRValidation::Validator.new { |v| v.url default_validator_url }
72
- end
73
-
74
63
  def configuration_messages(new_messages = nil, force_recheck: false)
75
64
  return @configuration_messages = new_messages unless new_messages.nil?
76
65
 
@@ -95,6 +84,14 @@ module Inferno
95
84
  def presets
96
85
  @presets ||= Repositories::Presets.new.presets_for_suite(id)
97
86
  end
87
+
88
+ def suite_option(identifier, **input_params)
89
+ suite_options[identifier] = input_params
90
+ end
91
+
92
+ def suite_options
93
+ @suite_options ||= {}
94
+ end
98
95
  end
99
96
  end
100
97
  end