inferno_core 0.0.6 → 0.0.8

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