Skip to content

Commit

Permalink
Merge branch 'main' into add_json_files_to_gemspec
Browse files Browse the repository at this point in the history
  • Loading branch information
vanessuniq authored Sep 12, 2024
2 parents cacb772 + 500ebf3 commit 0eaba2a
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 10 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ VALIDATOR_URL=http://localhost/validatorapi
REDIS_URL=redis://localhost:6379/0
G10_VALIDATOR_URL=http://localhost/validatorapi
FHIR_RESOURCE_VALIDATOR_URL=http://localhost/hl7validatorapi
FHIRPATH_URL=http://localhost:6789

# The base path where inferno will be hosted. Leave blank to host inferno at the
# root of its host.
Expand Down
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
LOAD_DEV_SUITES=dev_*
VALIDATOR_URL=https://example.com/validatorapi
ASYNC_JOBS=false
FHIRPATH_URL=https://example.com/fhirpath
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 0.4.40
* Update README content; fix broken links and improve generally. by @arscan in https://github.com/inferno-framework/inferno-core/pull/511
* FI-2863: Added support for auth_info to fhir_client by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/512
* FI-2865: FHIR Client Auth Info Refresh by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/513
* FI-2910: Host JWKS for Client Assertion by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/515
* FI-2857: Add auth input option by @AlyssaWang in https://github.com/inferno-framework/inferno-core/pull/514
* FI-2990: Fix typo in Request Details Modal by @AlyssaWang in https://github.com/inferno-framework/inferno-core/pull/519
* FI-2728: Prevent Test Runner from Crashing When Validator Response Contains Bad Characters by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/518
* Fi-1930: Custom Result Rollup by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/516
* FI-2750: Add FHIRPath Support by @vanessuniq in https://github.com/inferno-framework/inferno-core/pull/520

## 0.4.39
* FI-2836: Fix double requests from suite endpoints by @Jammjammjamm in https://github.com/inferno-framework/inferno-core/pull/505
* Bump braces from 3.0.2 to 3.0.3 by @dependabot in https://github.com/inferno-framework/inferno-core/pull/506
Expand Down
18 changes: 9 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
inferno_core (0.4.39)
inferno_core (0.4.40)
activesupport (~> 6.1.7.5)
base62-rb (= 0.3.1)
blueprinter (= 0.25.2)
Expand Down Expand Up @@ -106,7 +106,7 @@ GEM
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
Expand Down Expand Up @@ -156,7 +156,7 @@ GEM
hansi (0.2.1)
hashdiff (1.0.1)
http-accept (1.7.0)
http-cookie (1.0.6)
http-cookie (1.0.7)
domain_name (~> 0.5)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
Expand All @@ -170,7 +170,7 @@ GEM
method_source (1.0.0)
mime-types (3.5.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2024.0604)
mime-types-data (3.2024.0903)
minitest (5.18.0)
multi_json (1.15.0)
multi_xml (0.7.1)
Expand All @@ -183,11 +183,11 @@ GEM
mustermann (= 1.1.2)
netrc (0.11.0)
nio4r (2.7.3)
nokogiri (1.16.6-arm64-darwin)
nokogiri (1.16.7-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-darwin)
nokogiri (1.16.7-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-linux)
nokogiri (1.16.7-x86_64-linux)
racc (~> 1.4)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
Expand All @@ -210,7 +210,7 @@ GEM
public_suffix (4.0.7)
puma (5.6.8)
nio4r (~> 2.0)
racc (1.8.0)
racc (1.8.1)
rack (2.2.9)
rack-test (1.1.0)
rack (>= 1.0, < 3)
Expand Down Expand Up @@ -286,7 +286,7 @@ GEM
strings-ansi (0.2.0)
strscan (3.1.0)
thor (1.2.2)
tilt (2.3.0)
tilt (2.4.0)
tty-color (0.6.0)
tty-markdown (0.7.2)
kramdown (>= 1.16.2, < 3.0)
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.background.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ services:
# To let the service share your local FHIR package cache,
# uncomment the below line
# - ~/.fhir:/home/ktor/.fhir
fhirpath:
image: infernocommunity/fhirpath-service
ports:
- "6789:6789"
2 changes: 2 additions & 0 deletions lib/inferno/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require_relative 'dsl/fhir_client'
require_relative 'dsl/fhir_validation'
require_relative 'dsl/fhir_resource_validation'
require_relative 'dsl/fhirpath_evaluation'
require_relative 'dsl/http_client'
require_relative 'dsl/results'
require_relative 'dsl/runnable'
Expand All @@ -18,6 +19,7 @@ module DSL
Results,
FHIRValidation,
FHIRResourceValidation,
FhirpathEvaluation,
Messages
].freeze

Expand Down
121 changes: 121 additions & 0 deletions lib/inferno/dsl/fhirpath_evaluation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
module Inferno
module DSL
# This module contains the methods needed to perform FHIRPath evaluations
# on FHIR resources/elements. The actual evaluation is typically performed by an external
# FHIRPath evaluation service.
#
# Tests can leverage the evaluation functionality by calling `evaluate_fhirpath` to retrieve
# results of FHIRPath expressions.
#
# @example
#
# results = evaluate_fhirpath(resource: patient_resource, path: 'Patient.name.given')
#
# results will be an array representing the result of evaluating the given
# expression against the given root element. Each "result" in the returned
# array will be in the form
# `{ "type": "[FHIR datatype of the result]", "element": "[result value of the FHIRPath expression]" }`.
# @note the `element` field can either be a primitive value (string, boolean, etc.) or a FHIR::Model.
module FhirpathEvaluation
def self.included(klass)
klass.extend ClassMethods
end

# Evaluates a fhirpath expression for a given FHIR resource
#
# @param resource [FHIR::Model] the root FHIR resource to use when evaluating the fhirpath expression.
# @param path [String] The FHIRPath expression to evaluate.
# @param url [String] the url of the fhirpath service to use.
# @return [Array<Hash>] An array of hashes representing the result of evaluating the given expression against
# the given root resource.
def evaluate_fhirpath(resource:, path:, url: nil)
self.class.evaluator(url).evaluate_fhirpath(resource, path, self)
end

class Evaluator
# @private
def initialize(url = nil)
url(url)
end

# @private
def default_fhirpath_url
ENV.fetch('FHIRPATH_URL')
end

# Set/Get the url of the fhirpath service
#
# @param fhirpath_url [String]
# @return [String]
def url(fhirpath_url = nil)
@url ||= fhirpath_url || default_fhirpath_url
end

# Evaluates a fhirpath expression for a given FHIR resource
#
# @param fhir_resource [FHIR::Model] the root FHIR resource to use when evaluating the fhirpath expression.
# @param fhirpath_expression [String] The FHIRPath expression to evaluate.
# @param runnable [Inferno::Test] to add any error message that occurs.
# @return [Array<Hash>] An array hashes representing the result of evaluating the given expression against
# the given root resource. Each "result" in the returned array will be in the form
# `{ "type": "[FHIR datatype of the result]", "element": "[result value of the FHIRPath expression]" }`.
# @note the `element` field can either be a primitive value (string, boolean, etc.) or a FHIR::Model.
def evaluate_fhirpath(fhir_resource, fhirpath_expression, runnable)
begin
response = call_fhirpath_service(fhir_resource, fhirpath_expression)
rescue StandardError => e
# This could be a complete failure to connect (fhirpath service isn't running)
# or a timeout (fhirpath service took too long to respond).
runnable.add_message('error', e.message)
raise Inferno::Exceptions::ErrorInFhirpathException, "Unable to connect to FHIRPath service at #{url}."
end

sanitized_body = remove_invalid_characters(response.body)
return transform_fhirpath_results(JSON.parse(sanitized_body)) if response.status.to_s.start_with? '2'

runnable.add_message('error', "FHIRPath service Response: HTTP #{response.status}\n#{sanitized_body}")
raise Inferno::Exceptions::ErrorInFhirpathException,
'FHIRPath service call failed. Review Messages tab for more information.'
rescue JSON::ParserError
runnable.add_message('error', "Invalid FHIRPath service response format:\n#{sanitized_body}")
raise Inferno::Exceptions::ErrorInFhirpathException,
'Error occurred in the FHIRPath service. Review Messages tab for more information.'
end

# @private
def transform_fhirpath_results(fhirpath_results)
fhirpath_results.each do |result|
klass = FHIR.const_get(result['type'])
result['element'] = klass.new(result['element'])
rescue NameError
next
end
fhirpath_results
end

def call_fhirpath_service(fhir_resource, fhirpath_expression)
Faraday.new(
url,
request: { timeout: 600 }
).post(
"evaluate?path=#{fhirpath_expression}",
fhir_resource.to_json,
content_type: 'application/json'
)
end

# @private
def remove_invalid_characters(string)
string.gsub(/[^[:print:]\r\n]+/, '')
end
end

module ClassMethods
# @private
def evaluator(url = nil)
@evaluator ||= Inferno::DSL::FhirpathEvaluation::Evaluator.new(url)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/inferno/entities/test_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TestGroup
extend DSL::HTTPClient::ClassMethods
extend DSL::Runnable
include DSL::FHIRValidation
include DSL::FhirpathEvaluation
include DSL::Results
include DSL::Assertions
include DSL::Messages
Expand Down
1 change: 1 addition & 0 deletions lib/inferno/entities/test_suite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class TestSuite
extend DSL::HTTPClient::ClassMethods
include DSL::FHIRValidation
include DSL::FHIRResourceValidation
include DSL::FhirpathEvaluation
include DSL::Results
include DSL::Assertions
include DSL::Messages
Expand Down
19 changes: 19 additions & 0 deletions lib/inferno/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ def initialize(validator_name)
end
end

class FhirpathNotFoundException < RuntimeError
def initialize(fhirpath_name)
super("No '#{fhirpath_name}' FHIRPath evaluator found")
end
end

# ErrorInFhirpathException is used when an exception occurred in
# calling the FHIRPath service, for example a connection timeout
# or an unexpected response format.
# Note: This class extends TestResultException instead of RuntimeError
# to bypass printing the stack trace in the UI, since
# the stack trace of this exception is not likely be useful.
# Instead the message should point to where in the fhirpath evaluator an error occurred.
class ErrorInFhirpathException < TestResultException
def result
'error'
end
end

class RequiredInputsNotFound < RuntimeError
def initialize(missing_inputs)
super("Missing the following required inputs: #{missing_inputs.join(', ')}")
Expand Down
2 changes: 1 addition & 1 deletion lib/inferno/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Inferno
# Standard patterns for gem versions: https://guides.rubygems.org/patterns/
VERSION = '0.4.39'.freeze
VERSION = '0.4.40'.freeze
end
Loading

0 comments on commit 0eaba2a

Please sign in to comment.