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