inferno_core 0.0.7 → 0.0.8.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86c8b0374d22ea35f07944360943d2a1240f8ef7365b97d625b32facde4a2eaa
4
- data.tar.gz: '0258752a8738b7e9c1da7d9112d0ee4f15bac0a8c7b2acac5f9973bb35d8146c'
3
+ metadata.gz: 225c877bb710ee45ff9e6da9875ff1d08a46945bd0836d26c45f9864f08d8d57
4
+ data.tar.gz: 6bf0f02557ca8a6403b1da40afe0efadce186b4bb70076e9927c7d6283310889
5
5
  SHA512:
6
- metadata.gz: 76cd8133722b109ed66f9b79eea46279c177fdac68cfe9abea4d0e9caaa76b861e533909923a94121d17e15483be5254f8e36e67cf6abb6121bc12f1e37f5fd7
7
- data.tar.gz: 9be9f363e78e653b33818a8e8eee7fc74474ec2ca019b715054b7926d7e5675c46b290e138c0ee020e586d91eecef2759ead3272b6087461af89afeb8fdd7e94
6
+ metadata.gz: 357187f80bb794cb9ec0052526544aa5b8de34081c800fa9f291f695738cf84b498167fba0c777203b1c3f2a1126753cbe4e6081d187e050c1342d7d2bca5e4b
7
+ data.tar.gz: f8ae794f5ac94fb712a5d2397a47fe4cf727232601feedcef4d17af24f404a01f34e9dcdd701c4b366062e8bba3f539ddb03842c6c2958cb583a40c2ffa9e319
@@ -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" />
@@ -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,12 +115,18 @@ 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
 
@@ -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",
@@ -94,10 +180,28 @@ module Inferno
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
 
@@ -1 +1 @@
1
- (self.webpackChunkinferno_web_app=self.webpackChunkinferno_web_app||[]).push([[217],{3217:(t,e,n)=>{"use strict";n.r(e),n.d(e,{getCLS:()=>v,getFCP:()=>g,getFID:()=>h,getLCP:()=>y,getTTFB:()=>F});var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:e,delta:0,entries:[],id:r(),isFinal:!1}},s=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var n=new PerformanceObserver((function(t){return t.getEntries().map(e)}));return n.observe({type:t,buffered:!0}),n}}catch(t){}},u=!1,c=!1,p=function(t){u=!t.persisted},f=function(){addEventListener("pagehide",p),addEventListener("beforeunload",(function(){}))},l=function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(f(),c=!0),addEventListener("visibilitychange",(function(e){var n=e.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:n,isUnloading:u})}),{capture:!0,once:e})},d=function(t,e,n,i){var a;return function(){n&&e.isFinal&&n.disconnect(),e.value>=0&&(i||e.isFinal||"hidden"===document.visibilityState)&&(e.delta=e.value-(a||0),(e.delta||e.isFinal||void 0===a)&&(t(e),a=e.value))}},v=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),e())},r=s("layout-shift",a);r&&(e=d(t,i,r,n),l((function(t){var n=t.isUnloading;r.takeRecords().map(a),n&&(i.isFinal=!0),e()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,l((function(t){var e=t.timeStamp;return i=e}),!0)),{get timeStamp(){return i}}},g=function(t){var e,n=o("FCP"),i=m(),a=s("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(n.value=t.startTime,n.isFinal=!0,n.entries.push(t),e())}));a&&(e=d(t,n,a))},h=function(t){var e=o("FID"),n=m(),i=function(t){t.startTime<n.timeStamp&&(e.value=t.processingStart-t.startTime,e.entries.push(t),e.isFinal=!0,r())},a=s("first-input",i),r=d(t,e,a);a?l((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<n.timeStamp&&(e.value=t,e.isFinal=!0,e.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(e){addEventListener(e,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var n=t.startTime;n<a.timeStamp?(i.value=n,i.entries.push(t)):i.isFinal=!0,e()},u=s("largest-contentful-paint",r);if(u){e=d(t,i,u,n);var c=function(){i.isFinal||(u.takeRecords().map(r),i.isFinal=!0,e())};S().then(c),l(c,!0)}},F=function(t){var e,n=o("TTFB");e=function(){try{var e=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,e={entryType:"navigation",startTime:0};for(var n in t)"navigationStart"!==n&&"toJSON"!==n&&(e[n]=Math.max(t[n]-t.navigationStart,0));return e}();n.value=n.delta=e.responseStart,n.entries=[e],n.isFinal=!0,t(n)}catch(t){}},"complete"===document.readyState?setTimeout(e,0):addEventListener("pageshow",e)}}}]);
1
+ "use strict";(self.webpackChunkinferno_web_app=self.webpackChunkinferno_web_app||[]).push([[217],{3217:(t,e,n)=>{n.r(e),n.d(e,{getCLS:()=>v,getFCP:()=>g,getFID:()=>h,getLCP:()=>y,getTTFB:()=>F});var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:e,delta:0,entries:[],id:r(),isFinal:!1}},s=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var n=new PerformanceObserver((function(t){return t.getEntries().map(e)}));return n.observe({type:t,buffered:!0}),n}}catch(t){}},u=!1,c=!1,p=function(t){u=!t.persisted},f=function(){addEventListener("pagehide",p),addEventListener("beforeunload",(function(){}))},l=function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(f(),c=!0),addEventListener("visibilitychange",(function(e){var n=e.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:n,isUnloading:u})}),{capture:!0,once:e})},d=function(t,e,n,i){var a;return function(){n&&e.isFinal&&n.disconnect(),e.value>=0&&(i||e.isFinal||"hidden"===document.visibilityState)&&(e.delta=e.value-(a||0),(e.delta||e.isFinal||void 0===a)&&(t(e),a=e.value))}},v=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),e())},r=s("layout-shift",a);r&&(e=d(t,i,r,n),l((function(t){var n=t.isUnloading;r.takeRecords().map(a),n&&(i.isFinal=!0),e()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,l((function(t){var e=t.timeStamp;return i=e}),!0)),{get timeStamp(){return i}}},g=function(t){var e,n=o("FCP"),i=m(),a=s("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(n.value=t.startTime,n.isFinal=!0,n.entries.push(t),e())}));a&&(e=d(t,n,a))},h=function(t){var e=o("FID"),n=m(),i=function(t){t.startTime<n.timeStamp&&(e.value=t.processingStart-t.startTime,e.entries.push(t),e.isFinal=!0,r())},a=s("first-input",i),r=d(t,e,a);a?l((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<n.timeStamp&&(e.value=t,e.isFinal=!0,e.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(e){addEventListener(e,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var n=t.startTime;n<a.timeStamp?(i.value=n,i.entries.push(t)):i.isFinal=!0,e()},u=s("largest-contentful-paint",r);if(u){e=d(t,i,u,n);var c=function(){i.isFinal||(u.takeRecords().map(r),i.isFinal=!0,e())};S().then(c),l(c,!0)}},F=function(t){var e,n=o("TTFB");e=function(){try{var e=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,e={entryType:"navigation",startTime:0};for(var n in t)"navigationStart"!==n&&"toJSON"!==n&&(e[n]=Math.max(t[n]-t.navigationStart,0));return e}();n.value=n.delta=e.responseStart,n.entries=[e],n.isFinal=!0,t(n)}catch(t){}},"complete"===document.readyState?setTimeout(e,0):addEventListener("pageshow",e)}}}]);