inferno_core 0.0.6 → 0.0.8

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/inferno +7 -0
  3. data/lib/inferno/apps/cli/console.rb +12 -0
  4. data/lib/inferno/apps/cli/main.rb +18 -0
  5. data/lib/inferno/apps/cli/migration.rb +14 -0
  6. data/lib/inferno/apps/cli.rb +8 -0
  7. data/lib/inferno/apps/web/index.html.erb +1 -0
  8. data/lib/inferno/config/application.rb +3 -0
  9. data/lib/inferno/config/boot/db.rb +1 -9
  10. data/lib/inferno/config/boot/logging.rb +2 -0
  11. data/lib/inferno/db/migrations/001_create_initial_structure.rb +1 -1
  12. data/lib/inferno/db/schema.rb +1 -1
  13. data/lib/inferno/dsl/assertions.rb +66 -1
  14. data/lib/inferno/dsl/configurable.rb +1 -1
  15. data/lib/inferno/dsl/fhir_client.rb +22 -18
  16. data/lib/inferno/dsl/fhir_client_builder.rb +3 -3
  17. data/lib/inferno/dsl/fhir_validation.rb +105 -1
  18. data/lib/inferno/dsl/http_client.rb +4 -4
  19. data/lib/inferno/dsl/http_client_builder.rb +3 -3
  20. data/lib/inferno/dsl/request_storage.rb +8 -8
  21. data/lib/inferno/dsl/results.rb +1 -1
  22. data/lib/inferno/dsl/resume_test_route.rb +9 -9
  23. data/lib/inferno/dsl/runnable.rb +36 -18
  24. data/lib/inferno/entities/header.rb +14 -7
  25. data/lib/inferno/entities/message.rb +16 -8
  26. data/lib/inferno/entities/request.rb +32 -19
  27. data/lib/inferno/entities/result.rb +36 -29
  28. data/lib/inferno/entities/session_data.rb +12 -6
  29. data/lib/inferno/entities/test.rb +13 -0
  30. data/lib/inferno/entities/test_run.rb +29 -6
  31. data/lib/inferno/entities/test_session.rb +16 -10
  32. data/lib/inferno/public/217.bundle.js +1 -1
  33. data/lib/inferno/public/bundle.js +154 -1
  34. data/lib/inferno/public/bundle.js.LICENSE.txt +15 -0
  35. data/lib/inferno/repositories/in_memory_repository.rb +1 -1
  36. data/lib/inferno/repositories/results.rb +1 -1
  37. data/lib/inferno/spec_support.rb +1 -1
  38. data/lib/inferno/test_runner.rb +1 -1
  39. data/lib/inferno/utils/markdown_formatter.rb +1 -1
  40. data/lib/inferno/utils/middleware/request_logger.rb +9 -3
  41. data/lib/inferno/utils/migration.rb +17 -0
  42. data/lib/inferno/version.rb +1 -1
  43. data/lib/inferno.rb +0 -4
  44. metadata +41 -8
  45. data/bin/inferno-console +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40769c54fd3854730eb080d1bb9e0755ff1551b00761968155d463dd3a6ce3e0
4
- data.tar.gz: 8bbf5aaa2b09d7d5ff54e56a3ed33ce4e1f4df29fc53e3737f18e9972dc34f1e
3
+ metadata.gz: ef5b26a5633a33054fd378a523bc3a768e1a77ef572180db26ea792899cfc86a
4
+ data.tar.gz: b08e8c22c830ab2547d02e89d08e97bfc8248ce1fd8705d95b25d25df1925565
5
5
  SHA512:
6
- metadata.gz: 35fe2518d3856f01b3358da68d862318784f05ab067b579d3ac62053dc1deed2be2895e3cfb5ff8efbc97e71d2495a11ebf66eadf23c5b8b4edb502707334838
7
- data.tar.gz: 04e37af9daf40c01f0d4182f055e4ddf187d72f0e9d8a43e8858af5c549a4a60900d71f034a8e027e20f53a56f6b5e835bce1fca3a0b98870b07e0681793a2d9
6
+ metadata.gz: a22e65c16f1b80abec1c1e182a91ffa44cdb4e78cfad7ec6233c07cbcbfcdf221a5444aa7bec88c4a6bdb99ad95623926d88f6ed60b3888fa0b7bfc1b5a32793
7
+ data.tar.gz: d9717b4a08e8650071bb679ff964f85efd9d771a8f361a4b0a0781d7e9e91ea9ff764e6c8760a34f063e97944fedfb06b86349cd95cb29561844dc20a37401c1
data/bin/inferno ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pry'
4
+ require_relative '../lib/inferno/config/application'
5
+ require_relative '../lib/inferno/apps/cli'
6
+
7
+ Inferno::CLI::Main.start
@@ -0,0 +1,12 @@
1
+ module Inferno
2
+ module CLI
3
+ class Console
4
+ def run
5
+ require_relative '../../../inferno'
6
+
7
+ Inferno::Application.finalize!
8
+ Pry.start
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'console'
2
+ require_relative 'migration'
3
+
4
+ module Inferno
5
+ module CLI
6
+ class Main < Thor
7
+ desc 'console', 'Start an interactive console session with Inferno'
8
+ def console
9
+ Console.new.run
10
+ end
11
+
12
+ desc 'migrate', 'Run database migrations'
13
+ def migrate
14
+ Migration.new.run
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../../utils/migration'
2
+
3
+ module Inferno
4
+ module CLI
5
+ class Migration
6
+ def run
7
+ Inferno::Application.start(:logging)
8
+ Inferno::Application['logger'].level = Logger::DEBUG
9
+
10
+ Utils::Migration.new.run
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ require 'thor'
2
+
3
+ require_relative 'cli/main'
4
+
5
+ module Inferno
6
+ module CLI
7
+ end
8
+ end
@@ -1,6 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
+ <base href="/">
4
5
  <meta charset="utf-8" />
5
6
  <link rel="icon" href="/public/favicon.ico" />
6
7
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -1,4 +1,7 @@
1
+ require 'active_support/all'
2
+ require 'dotenv'
1
3
  require 'dry/system/container'
4
+ require 'sequel'
2
5
  require_relative 'boot'
3
6
 
4
7
  module Inferno
@@ -12,6 +12,7 @@ Inferno::Application.boot(:db) do
12
12
  config = YAML.load_file(config_path)[ENV['APP_ENV']]
13
13
  .merge(logger: Inferno::Application['logger'])
14
14
  connection = Sequel.connect(config)
15
+ connection.sql_log_level = :debug
15
16
 
16
17
  register('db.config', config)
17
18
  register('db.connection', connection)
@@ -19,14 +20,5 @@ Inferno::Application.boot(:db) do
19
20
 
20
21
  start do
21
22
  Sequel.extension :migration
22
- db = Inferno::Application['db.connection']
23
- migration_path = File.join(Inferno::Application.root, 'lib', 'inferno', 'db', 'migrations')
24
- Sequel::Migrator.run(db, migration_path)
25
-
26
- if ENV['APP_ENV'] == 'development'
27
- schema_path = File.join(Inferno::Application.root, 'lib', 'inferno', 'db', 'schema.rb')
28
- db.extension :schema_dumper
29
- File.open(schema_path, 'w') { |f| f.print(db.dump_schema_migration) }
30
- end
31
23
  end
32
24
  end
@@ -11,6 +11,8 @@ Inferno::Application.boot(:logging) do
11
11
  Logger.new($stdout)
12
12
  end
13
13
 
14
+ logger.level = Logger::INFO
15
+
14
16
  register('logger', logger)
15
17
  end
16
18
  end
@@ -116,7 +116,7 @@ Sequel.migration do
116
116
  # Requires requests to be a part of tests now.
117
117
  foreign_key :result_id, :results, index: true, type: String
118
118
  foreign_key :test_session_id, :test_sessions, index: true, type: String
119
- add_index [:test_session_id, :name], concurrently: true
119
+ index [:test_session_id, :name], concurrently: true
120
120
 
121
121
  column :created_at, DateTime, null: false
122
122
  column :updated_at, DateTime, null: false
@@ -95,13 +95,13 @@ Sequel.migration do
95
95
  String :response_body, :text=>true
96
96
  foreign_key :result_id, :results, :type=>String, :size=>255
97
97
  foreign_key :test_session_id, :test_sessions, :type=>String, :size=>255
98
- String :"[:test_session_id, :name]"
99
98
  DateTime :created_at, :null=>false
100
99
  DateTime :updated_at, :null=>false
101
100
 
102
101
  index [:id]
103
102
  index [:result_id]
104
103
  index [:test_session_id]
104
+ index [:test_session_id, :name]
105
105
  end
106
106
 
107
107
  create_table(:result_outputs, :ignore_index_errors=>true) do
@@ -3,22 +3,43 @@ require_relative '../exceptions'
3
3
  module Inferno
4
4
  module DSL
5
5
  module Assertions
6
+ # Make an assertion
7
+ #
8
+ # @param test a value whose truthiness will determine whether the
9
+ # assertion passes or fails
10
+ # @param message [String] failure message
6
11
  def assert(test, message = '')
7
12
  raise Exceptions::AssertionException, message unless test
8
13
  end
9
14
 
15
+ # @private
10
16
  def bad_response_status_message(expected, received)
11
17
  "Bad response status: expected #{Array.wrap(expected).join(', ')}, but received #{received}"
12
18
  end
13
19
 
20
+ # Check an response's status
21
+ #
22
+ # @param status [Integer, Array<Integer>] a single integer or an array of
23
+ # integer status codes
24
+ # @param response [Hash]
14
25
  def assert_response_status(status, response: self.response)
15
26
  assert Array.wrap(status).include?(response[:status]), bad_response_status_message(status, response[:status])
16
27
  end
17
28
 
29
+ # @private
18
30
  def bad_resource_type_message(expected, received)
19
31
  "Bad resource type received: expected #{expected}, but received #{received}"
20
32
  end
21
33
 
34
+ # Check a FHIR resource's type
35
+ #
36
+ # @param resource_type [String, Symbol, Class]
37
+ # @param resource [FHIR::Model]
38
+ # @example
39
+ # # The resource type can be a symbol, String, or FHIR::Model class
40
+ # assert_resource_type(:capability_statement)
41
+ # assert_resource_type('CapabilityStatement')
42
+ # assert_resource_type(FHIR::CapabilityStatement)
22
43
  def assert_resource_type(resource_type, resource: self.resource)
23
44
  resource_type_name = normalize_resource_type(resource_type)
24
45
 
@@ -26,15 +47,48 @@ module Inferno
26
47
  bad_resource_type_message(resource_type_name, resource&.resourceType)
27
48
  end
28
49
 
50
+ # @private
29
51
  def invalid_resource_message(profile_url)
30
52
  "Resource does not conform to the profile: #{profile_url}"
31
53
  end
32
54
 
55
+ # Validate a FHIR resource
56
+ #
57
+ # @param resource [FHIR::Model]
58
+ # @param profile_url [String] url of the profile to validate against,
59
+ # defaults to validating against the base FHIR resource
33
60
  def assert_valid_resource(resource: self.resource, profile_url: nil)
34
61
  assert resource_is_valid?(resource: resource, profile_url: profile_url),
35
62
  invalid_resource_message(profile_url)
36
63
  end
37
64
 
65
+ # Validate each entry of a Bundle
66
+ #
67
+ # @param bundle [FHIR::Bundle]
68
+ # @param resource_types
69
+ # [String,Symbol,FHIR::Model,Array<String,Symbol,FHIR::Model>,Hash] If a
70
+ # string, symbol, or FHIR::Model is provided, only that resource type
71
+ # will be validated. If an array of strings is provided, only those
72
+ # resource types will be validated. If a hash is provided with resource
73
+ # types as keys and profile urls (or nil) as values, only those resource
74
+ # types will be validated against the provided profile url or the base
75
+ # resource if nil.
76
+ # @example
77
+ # # Only validate Patient bundle entries
78
+ # assert_valid_bundle_entries(resource_types: 'Patient')
79
+ #
80
+ # # Only valiadte Patient and Condition bundle entries
81
+ # assert_valid_bundle_entries(resource_types: ['Patient', 'Condition'])
82
+ #
83
+ # # Only validate Patient and Condition bundle entries. Validate Patient
84
+ # # resources against the given profile, and Codition resources against the
85
+ # # base FHIR Condition resource.
86
+ # assert_valid_bundle_entries(
87
+ # resource_types: {
88
+ # 'Patient': 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient',
89
+ # 'Condition': nil
90
+ # }
91
+ # )
38
92
  def assert_valid_bundle_entries(bundle: resource, resource_types: {})
39
93
  assert_resource_type('Bundle', resource: bundle)
40
94
 
@@ -56,6 +110,7 @@ module Inferno
56
110
  assert invalid_resources.empty?, invalid_bundle_entries_message(invalid_resources)
57
111
  end
58
112
 
113
+ # @private
59
114
  def invalid_bundle_entries_message(invalid_resources)
60
115
  identifier_strings =
61
116
  invalid_resources
@@ -64,6 +119,7 @@ module Inferno
64
119
  "The following bundle entries are invalid: #{identifier_strings}"
65
120
  end
66
121
 
122
+ # @private
67
123
  def normalize_resource_type(resource_type)
68
124
  if resource_type.is_a? Class
69
125
  resource_type.name.demodulize
@@ -72,6 +128,7 @@ module Inferno
72
128
  end
73
129
  end
74
130
 
131
+ # @private
75
132
  def normalize_types_to_check(resource_types)
76
133
  case resource_types
77
134
  when Hash
@@ -83,14 +140,22 @@ module Inferno
83
140
  end
84
141
  end
85
142
 
143
+ # Check for valid JSON
144
+ #
145
+ # @param maybe_json_string [String]
146
+ # @param message [String] extra failure message
86
147
  def assert_valid_json(maybe_json_string, message = '')
87
148
  assert JSON.parse(maybe_json_string)
88
149
  rescue JSON::ParserError
89
150
  assert false, "Invalid JSON. #{message}"
90
151
  end
91
152
 
153
+ # Check for a valid http/https uri
154
+ #
155
+ # @param uri [String]
156
+ # @param message [String] custom failure message
92
157
  def assert_valid_http_uri(uri, message = '')
93
- error_message = message || "\"#{uri}\" is not a valid URI"
158
+ error_message = message.presence || "\"#{uri}\" is not a valid URI"
94
159
  assert uri =~ /\A#{URI::DEFAULT_PARSER.make_regexp(['http', 'https'])}\z/, error_message
95
160
  end
96
161
 
@@ -15,7 +15,7 @@ module Inferno
15
15
  @config.apply(new_configuration)
16
16
  end
17
17
 
18
- # @api private
18
+ # @private
19
19
  class Configuration
20
20
  attr_accessor :configuration
21
21
 
@@ -35,7 +35,7 @@ module Inferno
35
35
  # @see Inferno::FHIRClientBuilder Documentation for the client
36
36
  # configuration DSL
37
37
  module FHIRClient
38
- # @api private
38
+ # @private
39
39
  def self.included(klass)
40
40
  klass.extend ClassMethods
41
41
  klass.extend Forwardable
@@ -54,7 +54,7 @@ module Inferno
54
54
  FHIRClientBuilder.new.build(self, self.class.fhir_client_definitions[client])
55
55
  end
56
56
 
57
- # @api private
57
+ # @private
58
58
  def fhir_clients
59
59
  @fhir_clients ||= {}
60
60
  end
@@ -69,15 +69,15 @@ module Inferno
69
69
  # @param client [Symbol]
70
70
  # @param name [Symbol] Name for this request to allow it to be used by
71
71
  # other tests
72
- # @option options [Hash] Input headers here - headers are optional and
73
- # must be entered as the last piece of input to this method
72
+ # @param headers [Hash] custom headers for this operation
74
73
  # @return [Inferno::Entities::Request]
75
- def fhir_operation(path, body: nil, client: :default, name: nil, **options)
74
+ def fhir_operation(path, body: nil, client: :default, name: nil, headers: {})
76
75
  store_request('outgoing', name) do
77
- headers = fhir_client(client).fhir_headers
78
- headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
79
- headers.merge!(options[:headers]) if options[:headers].present?
80
- fhir_client(client).send(:post, path, body, headers)
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?
79
+
80
+ fhir_client(client).send(:post, path, body, operation_headers)
81
81
  end
82
82
  end
83
83
 
@@ -86,9 +86,8 @@ module Inferno
86
86
  # @param client [Symbol]
87
87
  # @param name [Symbol] Name for this request to allow it to be used by
88
88
  # other tests
89
- # @param _options [Hash] TODO
90
89
  # @return [Inferno::Entities::Request]
91
- def fhir_get_capability_statement(client: :default, name: nil, **_options)
90
+ def fhir_get_capability_statement(client: :default, name: nil)
92
91
  store_request('outgoing', name) do
93
92
  fhir_client(client).conformance_statement
94
93
  fhir_client(client).reply
@@ -102,9 +101,8 @@ module Inferno
102
101
  # @param client [Symbol]
103
102
  # @param name [Symbol] Name for this request to allow it to be used by
104
103
  # other tests
105
- # @param _options [Hash] TODO
106
104
  # @return [Inferno::Entities::Request]
107
- def fhir_read(resource_type, id, client: :default, name: nil, **_options)
105
+ def fhir_read(resource_type, id, client: :default, name: nil)
108
106
  store_request('outgoing', name) do
109
107
  fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
110
108
  end
@@ -117,24 +115,30 @@ module Inferno
117
115
  # @param params [Hash] the search params
118
116
  # @param name [Symbol] Name for this request to allow it to be used by
119
117
  # other tests
120
- # @param _options [Hash] TODO
118
+ # @param search_method [Symbol] Use `:post` to search via POST
121
119
  # @return [Inferno::Entities::Request]
122
- def fhir_search(resource_type, client: :default, params: {}, name: nil, **_options)
120
+ def fhir_search(resource_type, client: :default, params: {}, name: nil, search_method: :get)
121
+ search =
122
+ if search_method == :post
123
+ { body: params }
124
+ else
125
+ { parameters: params }
126
+ end
123
127
  store_request('outgoing', name) do
124
128
  fhir_client(client)
125
- .search(fhir_class_from_resource_type(resource_type), search: { parameters: params })
129
+ .search(fhir_class_from_resource_type(resource_type), { search: search })
126
130
  end
127
131
  end
128
132
 
129
133
  # @todo Make this a FHIR class method? Something like
130
134
  # FHIR.class_for(resource_type)
131
- # @api private
135
+ # @private
132
136
  def fhir_class_from_resource_type(resource_type)
133
137
  FHIR.const_get(resource_type.to_s.camelize)
134
138
  end
135
139
 
136
140
  module ClassMethods
137
- # @api private
141
+ # @private
138
142
  def fhir_client_definitions
139
143
  @fhir_client_definitions ||= {}
140
144
  end
@@ -6,7 +6,7 @@ module Inferno
6
6
  class FHIRClientBuilder
7
7
  attr_accessor :runnable
8
8
 
9
- # @api private
9
+ # @private
10
10
  def build(runnable, block)
11
11
  self.runnable = runnable
12
12
  instance_exec(self, &block)
@@ -56,14 +56,14 @@ module Inferno
56
56
  @headers ||= headers
57
57
  end
58
58
 
59
- # @api private
59
+ # @private
60
60
  def method_missing(name, *args, &block)
61
61
  return runnable.call(name, *args, &block) if runnable.respond_to? name
62
62
 
63
63
  super
64
64
  end
65
65
 
66
- # @api private
66
+ # @private
67
67
  def respond_to_missing?(name)
68
68
  runnable.respond_to?(name) || super
69
69
  end
@@ -1,38 +1,113 @@
1
1
  module Inferno
2
2
  module DSL
3
+ # This module contains the methods needed to configure a validator to
4
+ # perform validation of FHIR resources. The actual validation is performed
5
+ # by an external FHIR validation service. Tests will typically rely on
6
+ # `assert_valid_resource` for validation rather than directly calling
7
+ # methods on a validator.
8
+ #
9
+ # @example
10
+ #
11
+ # validator do
12
+ # url 'http://example.com/validator'
13
+ # exclude_message { |message| message.type == 'info' }
14
+ # perform_additional_validation do |resource, profile_url|
15
+ # if something_is_wrong
16
+ # { type: 'error', message: 'something is wrong' }
17
+ # else
18
+ # { type: 'info', message: 'everything is ok' }
19
+ # end
20
+ # end
21
+ # end
3
22
  module FHIRValidation
4
23
  def self.included(klass)
5
24
  klass.extend ClassMethods
6
25
  end
7
26
 
27
+ # Perform validation, and add validation messages to the runnable
28
+ #
29
+ # @param resource [FHIR::Model]
30
+ # @param profile_url [String]
31
+ # @param validator [Symbol] the name of the validator to use
32
+ # @return [Boolean] whether the resource is valid
8
33
  def resource_is_valid?(resource: self.resource, profile_url: nil, validator: :default)
9
34
  find_validator(validator).resource_is_valid?(resource, profile_url, self)
10
35
  end
11
36
 
37
+ # Find a particular validator. Looks through a runnable's parents up to
38
+ # the suite to find a validator with a particular name
12
39
  def find_validator(validator_name)
13
40
  self.class.find_validator(validator_name)
14
41
  end
15
42
 
16
43
  class Validator
44
+ # @private
17
45
  def initialize(&block)
18
46
  instance_eval(&block)
19
47
  end
20
48
 
49
+ # @private
21
50
  def default_validator_url
22
51
  ENV.fetch('VALIDATOR_URL')
23
52
  end
24
53
 
54
+ # Set the url of the validator service
55
+ #
56
+ # @param url [String]
25
57
  def url(validator_url = nil)
26
58
  @url = validator_url if validator_url
27
59
 
28
60
  @url
29
61
  end
30
62
 
63
+ # @private
64
+ def additional_validations
65
+ @additional_validations ||= []
66
+ end
67
+
68
+ # Perform validation steps in addition to FHIR validation.
69
+ #
70
+ # @example
71
+ # perform_additional_validation do |resource, profile_url|
72
+ # if something_is_wrong
73
+ # { type: 'error', message: 'something is wrong' }
74
+ # else
75
+ # { type: 'info', message: 'everything is ok' }
76
+ # end
77
+ # end
78
+ # @yieldparam resource [FHIR::Model] the resource being validated
79
+ # @yieldparam profile_url [String] the profile the resource is being
80
+ # validated against
81
+ # @yieldreturn [Array<Hash<Symbol, String>>,Hash<Symbol, String>] The
82
+ # block should return a Hash or an Array of Hashes if any validation
83
+ # messages should be added. The Hash must contain two keys: `:type`
84
+ # and `:message`. `:type` can have a value of `'info'`, `'warning'`,
85
+ # or `'error'`. A type of `'error'` means the resource is invalid.
86
+ # `:message` contains the message string itself.
87
+ def perform_additional_validation(&block)
88
+ additional_validations << block
89
+ end
90
+
91
+ # @private
92
+ def additional_validation_messages(resource, profile_url)
93
+ additional_validations
94
+ .flat_map { |step| step.call(resource, profile_url) }
95
+ .select { |message| message.is_a? Hash }
96
+ end
97
+
98
+ # Filter out unwanted validation messages
99
+ #
100
+ # @example
101
+ # validator do
102
+ # exclude_message { |message| message.type == 'info' }
103
+ # end
104
+ # @yieldparam message [Inferno::Entities::Message]
31
105
  def exclude_message(&block)
32
106
  @exclude_message = block if block_given?
33
107
  @exclude_message
34
108
  end
35
109
 
110
+ # @see Inferno::DSL::FHIRValidation#resource_is_valid?
36
111
  def resource_is_valid?(resource, profile_url, runnable)
37
112
  profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url
38
113
 
@@ -40,6 +115,8 @@ module Inferno
40
115
 
41
116
  message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue) } || []
42
117
 
118
+ message_hashes.concat(additional_validation_messages(resource, profile_url))
119
+
43
120
  filter_messages(message_hashes)
44
121
 
45
122
  message_hashes
@@ -47,10 +124,12 @@ module Inferno
47
124
  .none? { |message_hash| message_hash[:type] == 'error' }
48
125
  end
49
126
 
127
+ # @private
50
128
  def filter_messages(message_hashes)
51
129
  message_hashes.reject! { |message| exclude_message.call(Entities::Message.new(message)) } if exclude_message
52
130
  end
53
131
 
132
+ # @private
54
133
  def message_hash_from_issue(issue)
55
134
  {
56
135
  type: issue_severity(issue),
@@ -58,6 +137,7 @@ module Inferno
58
137
  }
59
138
  end
60
139
 
140
+ # @private
61
141
  def issue_severity(issue)
62
142
  case issue.severity
63
143
  when 'warning'
@@ -69,6 +149,7 @@ module Inferno
69
149
  end
70
150
  end
71
151
 
152
+ # @private
72
153
  def issue_message(issue)
73
154
  location = if issue.respond_to?(:expression)
74
155
  issue.expression&.join(', ')
@@ -79,6 +160,11 @@ module Inferno
79
160
  "#{location}: #{issue&.details&.text}"
80
161
  end
81
162
 
163
+ # Post a resource to the validation service for validating.
164
+ #
165
+ # @param resource [FHIR::Model]
166
+ # @param profile_url [String]
167
+ # @return [String] the body of the validation response
82
168
  def validate(resource, profile_url)
83
169
  RestClient.post(
84
170
  "#{url}/validate",
@@ -89,15 +175,33 @@ module Inferno
89
175
  end
90
176
 
91
177
  module ClassMethods
92
- # @api private
178
+ # @private
93
179
  def fhir_validators
94
180
  @fhir_validators ||= {}
95
181
  end
96
182
 
183
+ # Define a validator
184
+ # @example
185
+ # validator do
186
+ # url 'http://example.com/validator'
187
+ # exclude_message { |message| message.type == 'info' }
188
+ # perform_additional_validation do |resource, profile_url|
189
+ # if something_is_wrong
190
+ # { type: 'error', message: 'something is wrong' }
191
+ # else
192
+ # { type: 'info', message: 'everything is ok' }
193
+ # end
194
+ # end
195
+ # end
196
+ #
197
+ # @param name [Symbol] the name of the validator, only needed if you are
198
+ # using multiple validators
97
199
  def validator(name = :default, &block)
98
200
  fhir_validators[name] = Inferno::DSL::FHIRValidation::Validator.new(&block)
99
201
  end
100
202
 
203
+ # Find a particular validator. Looks through a runnable's parents up to
204
+ # the suite to find a validator with a particular name
101
205
  def find_validator(validator_name)
102
206
  validator = fhir_validators[validator_name] || parent&.find_validator(validator_name)
103
207
 
@@ -16,7 +16,7 @@ module Inferno
16
16
  # # performs a GET to https://example.com
17
17
  # get
18
18
  # # performs a GET to https://example.com/abc
19
- # get('abc')
19
+ # get('abc')
20
20
  #
21
21
  # request # the most recent request
22
22
  # response # the most recent response
@@ -27,7 +27,7 @@ module Inferno
27
27
  # @see Inferno::FHIRClientBuilder Documentation for the client
28
28
  # configuration DSL
29
29
  module HTTPClient
30
- # @api private
30
+ # @private
31
31
  def self.included(klass)
32
32
  klass.extend ClassMethods
33
33
  klass.include RequestStorage
@@ -47,7 +47,7 @@ module Inferno
47
47
  http_clients[client] = HTTPClientBuilder.new.build(self, definition)
48
48
  end
49
49
 
50
- # @api private
50
+ # @private
51
51
  def http_clients
52
52
  @http_clients ||= {}
53
53
  end
@@ -104,7 +104,7 @@ module Inferno
104
104
  end
105
105
 
106
106
  module ClassMethods
107
- # @api private
107
+ # @private
108
108
  def http_client_definitions
109
109
  @http_client_definitions ||= {}
110
110
  end