inferno_core 0.0.1
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 +7 -0
- data/LICENSE +201 -0
- data/bin/inferno-console +8 -0
- data/lib/inferno.rb +15 -0
- data/lib/inferno/apps/web/application.rb +12 -0
- data/lib/inferno/apps/web/controllers/controller.rb +34 -0
- data/lib/inferno/apps/web/controllers/requests/show.rb +16 -0
- data/lib/inferno/apps/web/controllers/test_runs/create.rb +39 -0
- data/lib/inferno/apps/web/controllers/test_runs/results/index.rb +18 -0
- data/lib/inferno/apps/web/controllers/test_runs/show.rb +16 -0
- data/lib/inferno/apps/web/controllers/test_sessions/create.rb +26 -0
- data/lib/inferno/apps/web/controllers/test_sessions/results/index.rb +17 -0
- data/lib/inferno/apps/web/controllers/test_sessions/show.rb +16 -0
- data/lib/inferno/apps/web/controllers/test_suites/index.rb +13 -0
- data/lib/inferno/apps/web/controllers/test_suites/show.rb +16 -0
- data/lib/inferno/apps/web/router.rb +27 -0
- data/lib/inferno/apps/web/serializers/header.rb +12 -0
- data/lib/inferno/apps/web/serializers/input.rb +16 -0
- data/lib/inferno/apps/web/serializers/message.rb +10 -0
- data/lib/inferno/apps/web/serializers/request.rb +26 -0
- data/lib/inferno/apps/web/serializers/result.rb +21 -0
- data/lib/inferno/apps/web/serializers/serializer.rb +16 -0
- data/lib/inferno/apps/web/serializers/test.rb +17 -0
- data/lib/inferno/apps/web/serializers/test_group.rb +23 -0
- data/lib/inferno/apps/web/serializers/test_run.rb +19 -0
- data/lib/inferno/apps/web/serializers/test_session.rb +17 -0
- data/lib/inferno/apps/web/serializers/test_suite.rb +18 -0
- data/lib/inferno/config/application.rb +22 -0
- data/lib/inferno/config/boot.rb +5 -0
- data/lib/inferno/config/boot/db.rb +23 -0
- data/lib/inferno/config/boot/logging.rb +16 -0
- data/lib/inferno/config/boot/suites.rb +27 -0
- data/lib/inferno/config/boot/web.rb +17 -0
- data/lib/inferno/db/migrations/001_create_initial_structure.rb +165 -0
- data/lib/inferno/dsl.rb +35 -0
- data/lib/inferno/dsl/assertions.rb +93 -0
- data/lib/inferno/dsl/fhir_client.rb +152 -0
- data/lib/inferno/dsl/fhir_client_builder.rb +56 -0
- data/lib/inferno/dsl/fhir_manipulation.rb +25 -0
- data/lib/inferno/dsl/fhir_validation.rb +111 -0
- data/lib/inferno/dsl/http_client.rb +122 -0
- data/lib/inferno/dsl/http_client_builder.rb +54 -0
- data/lib/inferno/dsl/request_storage.rb +101 -0
- data/lib/inferno/dsl/results.rb +54 -0
- data/lib/inferno/dsl/runnable.rb +250 -0
- data/lib/inferno/entities.rb +19 -0
- data/lib/inferno/entities/attributes.rb +9 -0
- data/lib/inferno/entities/entity.rb +15 -0
- data/lib/inferno/entities/header.rb +42 -0
- data/lib/inferno/entities/message.rb +22 -0
- data/lib/inferno/entities/request.rb +166 -0
- data/lib/inferno/entities/result.rb +49 -0
- data/lib/inferno/entities/test.rb +173 -0
- data/lib/inferno/entities/test_group.rb +87 -0
- data/lib/inferno/entities/test_input.rb +20 -0
- data/lib/inferno/entities/test_run.rb +63 -0
- data/lib/inferno/entities/test_session.rb +26 -0
- data/lib/inferno/entities/test_suite.rb +73 -0
- data/lib/inferno/exceptions.rb +42 -0
- data/lib/inferno/public/217.bundle.js +1 -0
- data/lib/inferno/public/assets.json +6 -0
- data/lib/inferno/public/bundle.js +2 -0
- data/lib/inferno/public/bundle.js.LICENSE.txt +65 -0
- data/lib/inferno/public/e09b16f5cb645eb05f90c8f38f3409fb.png +0 -0
- data/lib/inferno/public/favicon.ico +0 -0
- data/lib/inferno/public/logo192.png +0 -0
- data/lib/inferno/public/logo512.png +0 -0
- data/lib/inferno/repositories.rb +27 -0
- data/lib/inferno/repositories/headers.rb +22 -0
- data/lib/inferno/repositories/in_memory_repository.rb +41 -0
- data/lib/inferno/repositories/messages.rb +35 -0
- data/lib/inferno/repositories/repository.rb +106 -0
- data/lib/inferno/repositories/requests.rb +89 -0
- data/lib/inferno/repositories/results.rb +72 -0
- data/lib/inferno/repositories/test_groups.rb +9 -0
- data/lib/inferno/repositories/test_runs.rb +46 -0
- data/lib/inferno/repositories/test_sessions.rb +56 -0
- data/lib/inferno/repositories/test_suites.rb +9 -0
- data/lib/inferno/repositories/tests.rb +9 -0
- data/lib/inferno/repositories/validate_runnable_reference.rb +42 -0
- data/lib/inferno/spec_support.rb +9 -0
- data/lib/inferno/test_runner.rb +81 -0
- data/lib/inferno/utils/middleware/request_logger.rb +55 -0
- data/lib/inferno/version.rb +3 -0
- data/spec/support/factory_bot.rb +21 -0
- metadata +514 -0
data/lib/inferno/dsl.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require_relative 'dsl/assertions'
|
|
2
|
+
require_relative 'dsl/fhir_client'
|
|
3
|
+
require_relative 'dsl/fhir_manipulation'
|
|
4
|
+
require_relative 'dsl/fhir_validation'
|
|
5
|
+
require_relative 'dsl/http_client'
|
|
6
|
+
require_relative 'dsl/results'
|
|
7
|
+
require_relative 'dsl/runnable'
|
|
8
|
+
|
|
9
|
+
module Inferno
|
|
10
|
+
# The DSL for writing tests.
|
|
11
|
+
module DSL
|
|
12
|
+
INCLUDABLE_DSL_MODULES = [
|
|
13
|
+
Assertions,
|
|
14
|
+
FHIRClient,
|
|
15
|
+
HTTPClient,
|
|
16
|
+
Results,
|
|
17
|
+
FHIRValidation,
|
|
18
|
+
FHIRManipulation
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
EXTENDABLE_DSL_MODULES = [
|
|
22
|
+
Runnable
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
def self.included(klass)
|
|
26
|
+
INCLUDABLE_DSL_MODULES.each do |dsl_module|
|
|
27
|
+
klass.include dsl_module
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
EXTENDABLE_DSL_MODULES.each do |dsl_module|
|
|
31
|
+
klass.extend dsl_module
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require_relative '../exceptions'
|
|
2
|
+
|
|
3
|
+
module Inferno
|
|
4
|
+
module DSL
|
|
5
|
+
module Assertions
|
|
6
|
+
def assert(test, message = '')
|
|
7
|
+
raise Exceptions::AssertionException, message unless test
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def bad_response_status_message(expected, received)
|
|
11
|
+
"Bad response status: expected #{Array.wrap(expected).join(', ')}, but received #{received}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def assert_response_status(status, response: self.response)
|
|
15
|
+
assert Array.wrap(status).include?(response[:status]), bad_response_status_message(status, response[:status])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bad_resource_type_message(expected, received)
|
|
19
|
+
"Bad resource type received: expected #{expected}, but received #{received}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def assert_resource_type(resource_type, resource: self.resource)
|
|
23
|
+
resource_type_name = normalize_resource_type(resource_type)
|
|
24
|
+
|
|
25
|
+
assert resource&.resourceType == resource_type_name,
|
|
26
|
+
bad_resource_type_message(resource_type_name, resource&.resourceType)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def invalid_resource_message(profile_url)
|
|
30
|
+
"Resource does not conform to the profile: #{profile_url}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def assert_valid_resource(resource: self.resource, profile_url: nil)
|
|
34
|
+
assert resource_is_valid?(resource: resource, profile_url: profile_url),
|
|
35
|
+
invalid_resource_message(profile_url)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def assert_valid_bundle_entries(bundle: resource, resource_types: {})
|
|
39
|
+
assert_resource_type('Bundle', resource: bundle)
|
|
40
|
+
|
|
41
|
+
types_to_check = normalize_types_to_check(resource_types)
|
|
42
|
+
|
|
43
|
+
invalid_resources =
|
|
44
|
+
bundle
|
|
45
|
+
.entry
|
|
46
|
+
.map(&:resource)
|
|
47
|
+
.select { |resource| types_to_check.empty? || types_to_check.include?(resource.resourceType) }
|
|
48
|
+
.reject do |resource|
|
|
49
|
+
validation_params = { resource: resource }
|
|
50
|
+
profile = types_to_check[resource.resourceType]
|
|
51
|
+
validation_params[:profile_url] = profile if profile
|
|
52
|
+
|
|
53
|
+
resource_is_valid?(**validation_params)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
assert invalid_resources.empty?, invalid_bundle_entries_message(invalid_resources)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def invalid_bundle_entries_message(invalid_resources)
|
|
60
|
+
identifier_strings =
|
|
61
|
+
invalid_resources
|
|
62
|
+
.map { |resource| "#{resource.resourceType}##{resource.id}" }
|
|
63
|
+
.join(', ')
|
|
64
|
+
"The following bundle entries are invalid: #{identifier_strings}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def normalize_resource_type(resource_type)
|
|
68
|
+
if resource_type.is_a? Class
|
|
69
|
+
resource_type.name.demodulize
|
|
70
|
+
else
|
|
71
|
+
resource_type.to_s.camelize
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def normalize_types_to_check(resource_types)
|
|
76
|
+
case resource_types
|
|
77
|
+
when Hash
|
|
78
|
+
resource_types.transform_keys { |type| normalize_resource_type(type) }
|
|
79
|
+
when String
|
|
80
|
+
{ normalize_resource_type(resource_types) => nil }
|
|
81
|
+
when Array
|
|
82
|
+
resource_types.each_with_object({}) { |type, types| types[normalize_resource_type(type)] = nil }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def assert_valid_json(maybe_json_string, message = '')
|
|
87
|
+
assert JSON.parse(maybe_json_string)
|
|
88
|
+
rescue JSON::ParserError
|
|
89
|
+
assert false, "Invalid JSON. #{message}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require_relative 'request_storage'
|
|
2
|
+
|
|
3
|
+
module Inferno
|
|
4
|
+
module DSL
|
|
5
|
+
# This module contains the FHIR DSL available to test writers.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class MyTestGroup < Inferno::TestGroup
|
|
9
|
+
# # create a "default" client for a group
|
|
10
|
+
# fhir_client do
|
|
11
|
+
# url 'https://example.com/fhir'
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# # create a named client for a group
|
|
15
|
+
# fhir_client :with_custom_header do
|
|
16
|
+
# url 'https://example.com/fhir'
|
|
17
|
+
# headers { 'X-my-custom-header': 'ABC123' }
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# test :some_test do
|
|
21
|
+
# run do
|
|
22
|
+
# # uses the default client
|
|
23
|
+
# fhir_read('Patient', 5)
|
|
24
|
+
#
|
|
25
|
+
# # uses a named client
|
|
26
|
+
# fhir_read('Patient', 5, client: :with_custom_header)
|
|
27
|
+
#
|
|
28
|
+
# request # the most recent request
|
|
29
|
+
# response # the most recent response
|
|
30
|
+
# resource # the resource from the most recent response
|
|
31
|
+
# requests # all of the requests which have been made in this test
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
# @see Inferno::FHIRClientBuilder Documentation for the client
|
|
36
|
+
# configuration DSL
|
|
37
|
+
module FHIRClient
|
|
38
|
+
# @api private
|
|
39
|
+
def self.included(klass)
|
|
40
|
+
klass.extend ClassMethods
|
|
41
|
+
klass.extend Forwardable
|
|
42
|
+
klass.include RequestStorage
|
|
43
|
+
|
|
44
|
+
klass.def_delegators 'self.class', :profile_url, :validator_url
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return a previously defined FHIR client
|
|
48
|
+
#
|
|
49
|
+
# @param client [Symbol] the name of the client
|
|
50
|
+
# @return [FHIR::Client]
|
|
51
|
+
# @see Inferno::FHIRClientBuilder
|
|
52
|
+
def fhir_client(client = :default)
|
|
53
|
+
fhir_clients[client] ||=
|
|
54
|
+
FHIRClientBuilder.new.build(self, self.class.fhir_client_definitions[client])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @api private
|
|
58
|
+
def fhir_clients
|
|
59
|
+
@fhir_clients ||= {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Perform a FHIR operation
|
|
63
|
+
#
|
|
64
|
+
# @note This is a placeholder method until the FHIR::Client supports
|
|
65
|
+
# general operations
|
|
66
|
+
#
|
|
67
|
+
# @param path [String]
|
|
68
|
+
# @param body [FHIR::Parameters]
|
|
69
|
+
# @param client [Symbol]
|
|
70
|
+
# @param name [Symbol] Name for this request to allow it to be used by
|
|
71
|
+
# other tests
|
|
72
|
+
# @param _options [Hash] TODO
|
|
73
|
+
# @return [Inferno::Entities::Request]
|
|
74
|
+
def fhir_operation(path, body: nil, client: :default, name: nil, **_options)
|
|
75
|
+
store_request('outgoing', name) do
|
|
76
|
+
headers = fhir_client(client).fhir_headers
|
|
77
|
+
headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
|
|
78
|
+
fhir_client(client).send(:post, path, body, headers)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Fetch the capability statement.
|
|
83
|
+
#
|
|
84
|
+
# @param client [Symbol]
|
|
85
|
+
# @param name [Symbol] Name for this request to allow it to be used by
|
|
86
|
+
# other tests
|
|
87
|
+
# @param _options [Hash] TODO
|
|
88
|
+
# @return [Inferno::Entities::Request]
|
|
89
|
+
def fhir_get_capability_statement(client: :default, name: nil, **_options)
|
|
90
|
+
store_request('outgoing', name) do
|
|
91
|
+
fhir_client(client).conformance_statement
|
|
92
|
+
fhir_client(client).reply
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Perform a FHIR read interaction.
|
|
97
|
+
#
|
|
98
|
+
# @param resource_type [String, Symbol, Class]
|
|
99
|
+
# @param id [String]
|
|
100
|
+
# @param client [Symbol]
|
|
101
|
+
# @param name [Symbol] Name for this request to allow it to be used by
|
|
102
|
+
# other tests
|
|
103
|
+
# @param _options [Hash] TODO
|
|
104
|
+
# @return [Inferno::Entities::Request]
|
|
105
|
+
def fhir_read(resource_type, id, client: :default, name: nil, **_options)
|
|
106
|
+
store_request('outgoing', name) do
|
|
107
|
+
fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Perform a FHIR search interaction.
|
|
112
|
+
#
|
|
113
|
+
# @param resource_type [String, Symbol, Class]
|
|
114
|
+
# @param client [Symbol]
|
|
115
|
+
# @param params [Hash] the search params
|
|
116
|
+
# @param name [Symbol] Name for this request to allow it to be used by
|
|
117
|
+
# other tests
|
|
118
|
+
# @param _options [Hash] TODO
|
|
119
|
+
# @return [Inferno::Entities::Request]
|
|
120
|
+
def fhir_search(resource_type, client: :default, params: {}, name: nil, **_options)
|
|
121
|
+
store_request('outgoing', name) do
|
|
122
|
+
fhir_client(client)
|
|
123
|
+
.search(fhir_class_from_resource_type(resource_type), search: { parameters: params })
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @todo Make this a FHIR class method? Something like
|
|
128
|
+
# FHIR.class_for(resource_type)
|
|
129
|
+
# @api private
|
|
130
|
+
def fhir_class_from_resource_type(resource_type)
|
|
131
|
+
FHIR.const_get(resource_type.to_s.camelize)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
module ClassMethods
|
|
135
|
+
# @api private
|
|
136
|
+
def fhir_client_definitions
|
|
137
|
+
@fhir_client_definitions ||= {}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Define a FHIR client to be used by a Runnable.
|
|
141
|
+
#
|
|
142
|
+
# @param name [Symbol] a name used to reference this particular client
|
|
143
|
+
# @param block a block to configure the client
|
|
144
|
+
# @see Inferno::FHIRClientBuilder Documentation for the client
|
|
145
|
+
# configuration DSL
|
|
146
|
+
def fhir_client(name = :default, &block)
|
|
147
|
+
fhir_client_definitions[name] = block
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'fhir_client'
|
|
2
|
+
|
|
3
|
+
module Inferno
|
|
4
|
+
module DSL
|
|
5
|
+
# DSL for configuring FHIR clients
|
|
6
|
+
class FHIRClientBuilder
|
|
7
|
+
attr_accessor :runnable
|
|
8
|
+
|
|
9
|
+
# @api private
|
|
10
|
+
def build(runnable, block)
|
|
11
|
+
self.runnable = runnable
|
|
12
|
+
instance_exec(self, &block)
|
|
13
|
+
|
|
14
|
+
FHIR::Client.new(url).tap do |client|
|
|
15
|
+
client.additional_headers = headers if headers
|
|
16
|
+
client.default_json
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Define the base FHIR url for a client. A string or symbol can be provided.
|
|
21
|
+
# A string is interpreted as a url. A symbol is interpreted as the name of
|
|
22
|
+
# an input to the Runnable.
|
|
23
|
+
#
|
|
24
|
+
# @param url [String, Symbol]
|
|
25
|
+
# @return [void]
|
|
26
|
+
def url(url = nil)
|
|
27
|
+
@url ||=
|
|
28
|
+
if url.is_a? Symbol
|
|
29
|
+
runnable.send(url)
|
|
30
|
+
else
|
|
31
|
+
url
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Define custom headers for a client
|
|
36
|
+
#
|
|
37
|
+
# @param headers [Hash]
|
|
38
|
+
# @return [void]
|
|
39
|
+
def headers(headers = nil)
|
|
40
|
+
@headers ||= headers
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @api private
|
|
44
|
+
def method_missing(name, *args, &block)
|
|
45
|
+
return runnable.call(name, *args, &block) if runnable.respond_to? name
|
|
46
|
+
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api private
|
|
51
|
+
def respond_to_missing?(name)
|
|
52
|
+
runnable.respond_to?(name) || super
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Inferno
|
|
2
|
+
module DSL
|
|
3
|
+
module FHIRManipulation
|
|
4
|
+
def walk_resource(resource, path = nil, &block)
|
|
5
|
+
resource.class::METADATA.each do |field_name, meta|
|
|
6
|
+
local_name = meta.fetch :local_name, field_name
|
|
7
|
+
values = [resource.instance_variable_get("@#{local_name}")].flatten.compact
|
|
8
|
+
next if values.empty?
|
|
9
|
+
|
|
10
|
+
values.each_with_index do |value, i|
|
|
11
|
+
child_path = if path.nil?
|
|
12
|
+
field_name
|
|
13
|
+
elsif meta['max'] > 1
|
|
14
|
+
"#{path}.#{field_name}[#{i}]"
|
|
15
|
+
else
|
|
16
|
+
"#{path}.#{field_name}"
|
|
17
|
+
end
|
|
18
|
+
yield value, meta, child_path
|
|
19
|
+
walk_resource value, child_path, &block unless FHIR::PRIMITIVES.include? meta['type']
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Inferno
|
|
2
|
+
module DSL
|
|
3
|
+
module FHIRValidation
|
|
4
|
+
def self.included(klass)
|
|
5
|
+
klass.extend ClassMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def resource_is_valid?(resource: self.resource, profile_url: nil, validator: :default)
|
|
9
|
+
find_validator(validator).resource_is_valid?(resource, profile_url, self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def find_validator(validator_name)
|
|
13
|
+
self.class.find_validator(validator_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Validator
|
|
17
|
+
def initialize(&block)
|
|
18
|
+
instance_eval(&block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def default_validator_url
|
|
22
|
+
ENV.fetch('VALIDATOR_URL')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def url(validator_url = nil)
|
|
26
|
+
@url = validator_url if validator_url
|
|
27
|
+
|
|
28
|
+
@url
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def exclude_message(&block)
|
|
32
|
+
@exclude_message = block if block_given?
|
|
33
|
+
@exclude_message
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resource_is_valid?(resource, profile_url, runnable)
|
|
37
|
+
profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url
|
|
38
|
+
|
|
39
|
+
outcome = FHIR::OperationOutcome.new(JSON.parse(validate(resource, profile_url)))
|
|
40
|
+
|
|
41
|
+
message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue) } || []
|
|
42
|
+
|
|
43
|
+
filter_messages(message_hashes)
|
|
44
|
+
|
|
45
|
+
message_hashes
|
|
46
|
+
.each { |message_hash| runnable.add_message(message_hash[:type], message_hash[:message]) }
|
|
47
|
+
.none? { |message_hash| message_hash[:type] == 'error' }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def filter_messages(message_hashes)
|
|
51
|
+
message_hashes.reject! { |message| exclude_message.call(Entities::Message.new(message)) } if exclude_message
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def message_hash_from_issue(issue)
|
|
55
|
+
{
|
|
56
|
+
type: issue_severity(issue),
|
|
57
|
+
message: issue_message(issue)
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def issue_severity(issue)
|
|
62
|
+
case issue.severity
|
|
63
|
+
when 'warning'
|
|
64
|
+
'warning'
|
|
65
|
+
when 'information'
|
|
66
|
+
'info'
|
|
67
|
+
else
|
|
68
|
+
'error'
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def issue_message(issue)
|
|
73
|
+
location = if issue.respond_to?(:expression)
|
|
74
|
+
issue.expression&.join(', ')
|
|
75
|
+
else
|
|
76
|
+
issue.location&.join(', ')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
"#{location}: #{issue&.details&.text}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate(resource, profile_url)
|
|
83
|
+
RestClient.post(
|
|
84
|
+
"#{url}/validate",
|
|
85
|
+
resource.to_json,
|
|
86
|
+
params: { profile: profile_url }
|
|
87
|
+
).body
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
module ClassMethods
|
|
92
|
+
# @api private
|
|
93
|
+
def fhir_validators
|
|
94
|
+
@fhir_validators ||= {}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def validator(name = :default, &block)
|
|
98
|
+
fhir_validators[name] = Inferno::DSL::FHIRValidation::Validator.new(&block)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def find_validator(validator_name)
|
|
102
|
+
validator = fhir_validators[validator_name] || parent&.find_validator(validator_name)
|
|
103
|
+
|
|
104
|
+
raise Exceptions::ValidatorNotFoundException, validator_name if validator.nil?
|
|
105
|
+
|
|
106
|
+
validator
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|