inferno_core 0.6.4 → 0.6.5
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 +4 -4
- data/lib/inferno/apps/cli/evaluate.rb +1 -30
- data/lib/inferno/apps/cli/new.rb +1 -2
- data/lib/inferno/apps/cli/templates/.env.development +1 -0
- data/lib/inferno/apps/cli/templates/.env.production +1 -0
- data/lib/inferno/apps/cli/templates/.gitignore +1 -0
- data/lib/inferno/apps/cli/templates/data/igs/.keep +0 -0
- data/lib/inferno/apps/cli/templates/docker-compose.background.yml.tt +2 -2
- data/lib/inferno/apps/web/controllers/controller.rb +3 -1
- data/lib/inferno/apps/web/router.rb +12 -6
- data/lib/inferno/config/boot/ig_files.rb +47 -0
- data/lib/inferno/config/boot/validator.rb +1 -0
- data/lib/inferno/config/boot/web.rb +6 -2
- data/lib/inferno/dsl/assertions.rb +26 -0
- data/lib/inferno/dsl/fhir_client_builder.rb +1 -0
- data/lib/inferno/dsl/fhir_evaluation/rules/all_must_supports_present.rb +13 -308
- data/lib/inferno/dsl/fhir_resource_validation.rb +34 -2
- data/lib/inferno/dsl/fhir_validation.rb +13 -0
- data/lib/inferno/dsl/must_support_assessment.rb +365 -0
- data/lib/inferno/dsl/results.rb +36 -4
- data/lib/inferno/dsl/runnable.rb +71 -0
- data/lib/inferno/dsl.rb +3 -1
- data/lib/inferno/entities/ig.rb +4 -1
- data/lib/inferno/exceptions.rb +6 -0
- data/lib/inferno/public/bundle.js +34 -34
- data/lib/inferno/public/bundle.js.LICENSE.txt +3 -3
- data/lib/inferno/repositories/igs.rb +122 -0
- data/lib/inferno/repositories/in_memory_repository.rb +7 -0
- data/lib/inferno/utils/ig_downloader.rb +17 -6
- data/lib/inferno/version.rb +1 -1
- data/spec/shared/test_kit_examples.rb +69 -0
- metadata +5 -2
@@ -61,7 +61,7 @@
|
|
61
61
|
*/
|
62
62
|
|
63
63
|
/**
|
64
|
-
* @remix-run/router v1.
|
64
|
+
* @remix-run/router v1.21.0
|
65
65
|
*
|
66
66
|
* Copyright (c) Remix Software Inc.
|
67
67
|
*
|
@@ -72,7 +72,7 @@
|
|
72
72
|
*/
|
73
73
|
|
74
74
|
/**
|
75
|
-
* React Router DOM v6.
|
75
|
+
* React Router DOM v6.28.1
|
76
76
|
*
|
77
77
|
* Copyright (c) Remix Software Inc.
|
78
78
|
*
|
@@ -83,7 +83,7 @@
|
|
83
83
|
*/
|
84
84
|
|
85
85
|
/**
|
86
|
-
* React Router v6.
|
86
|
+
* React Router v6.28.1
|
87
87
|
*
|
88
88
|
* Copyright (c) Remix Software Inc.
|
89
89
|
*
|
@@ -1,9 +1,131 @@
|
|
1
1
|
require_relative 'in_memory_repository'
|
2
2
|
|
3
|
+
require_relative '../utils/ig_downloader'
|
4
|
+
|
3
5
|
module Inferno
|
4
6
|
module Repositories
|
5
7
|
# Repository that deals with persistence for the `IG` entity.
|
6
8
|
class IGs < InMemoryRepository
|
9
|
+
include Inferno::Utils::IgDownloader
|
10
|
+
|
11
|
+
# Get the instance of the IG specified by either identifier or file path.
|
12
|
+
# An in-memory instance will be returned if already loaded, otherwise
|
13
|
+
# the IG will be retrieved from the user package cache (~/.fhir/packages)
|
14
|
+
# or from the package server and then loaded into the repository.
|
15
|
+
# @param id_or_path [String] either an identifier, eg "hl7.fhir.us.core#3.1.1"
|
16
|
+
# or a file path, eg "./igs/uscore.tgz"
|
17
|
+
# @return [Inferno::Entities::IG]
|
18
|
+
def find_or_load(id_or_path)
|
19
|
+
return find(id_or_path) if exists?(id_or_path)
|
20
|
+
|
21
|
+
ig_by_path = find_by_path(id_or_path)
|
22
|
+
|
23
|
+
return ig_by_path if ig_by_path
|
24
|
+
|
25
|
+
load(id_or_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get the instance of the already-loaded IG specified by file path.
|
29
|
+
# @param path [String] file path, eg "./igs/uscore.tgz"
|
30
|
+
# @return [Inferno::Entities::IG]
|
31
|
+
def find_by_path(path)
|
32
|
+
all.find { |ig| ig.source_path == path }
|
33
|
+
end
|
34
|
+
|
35
|
+
# @private
|
36
|
+
def load(ig_path)
|
37
|
+
local_ig_file = find_local_file(ig_path)
|
38
|
+
if local_ig_file
|
39
|
+
ig = Inferno::Entities::IG.from_file(local_ig_file)
|
40
|
+
# To match the HL7 FHIR validator, DO NOT cache igs loaded from file
|
41
|
+
elsif in_user_package_cache?(ig_path.sub('@', '#'))
|
42
|
+
# NPM syntax for a package identifier is id@version (eg, hl7.fhir.us.core@3.1.1)
|
43
|
+
# but in the cache the separator is # (hl7.fhir.us.core#3.1.1)
|
44
|
+
cache_directory = File.join(user_package_cache, ig_path.sub('@', '#'))
|
45
|
+
ig = Inferno::Entities::IG.from_file(cache_directory)
|
46
|
+
else
|
47
|
+
Tempfile.create(['package', '.tgz']) do |temp_file|
|
48
|
+
load_ig(ig_path, nil, temp_file.path)
|
49
|
+
cache_directory = add_package_to_cache(ig_path.sub('@', '#'), temp_file.path)
|
50
|
+
ig = Inferno::Entities::IG.from_file(cache_directory)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
ig.add_self_to_repository
|
54
|
+
ig
|
55
|
+
end
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def find_local_file(ig_path)
|
59
|
+
return nil unless ['.tgz', '.tar.gz'].any? { |ext| ig_path.downcase.end_with?(ext) }
|
60
|
+
|
61
|
+
return ig_path if File.exist?(ig_path)
|
62
|
+
|
63
|
+
# IG packages are copied to ./data/igs to be used by the validator,
|
64
|
+
# and if referenced by file in the validator block the path given must be "igs/{filename}.tgz"
|
65
|
+
data_igs_path = File.join('data', ig_path)
|
66
|
+
return data_igs_path if File.exist?(data_igs_path)
|
67
|
+
|
68
|
+
# Last resort, try to find the file under the current working directory,
|
69
|
+
# eg, given ig_path: 'igs/package123.tgz'
|
70
|
+
# this would find 'lib/my_test_kit/igs/package123.tgz'
|
71
|
+
Dir.glob(File.join('**', ig_path))[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
# @private
|
75
|
+
def user_package_cache
|
76
|
+
File.join(Dir.home, '.fhir', 'packages')
|
77
|
+
end
|
78
|
+
|
79
|
+
# @private
|
80
|
+
def in_user_package_cache?(ig_identifier)
|
81
|
+
File.directory?(File.join(user_package_cache, ig_identifier))
|
82
|
+
end
|
83
|
+
|
84
|
+
# @private
|
85
|
+
def add_package_to_cache(ig_identifier, temp_tgz)
|
86
|
+
return temp_tgz if ENV['READ_ONLY_FHIR_PACKAGE_CACHE'].present?
|
87
|
+
|
88
|
+
# In the HL7 FHIR validator, this is handled by the FilesystemPackageCacheManager:
|
89
|
+
# https://github.com/hapifhir/org.hl7.fhir.core/blob/3cf2a06e7abda7dc32cdc052d3a356c1201139cf/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java#L558
|
90
|
+
# We try to follow the same approach here
|
91
|
+
|
92
|
+
lockfile_path = File.join(user_package_cache, "#{ig_identifier}.lock")
|
93
|
+
lockfile = File.open(lockfile_path, 'w+')
|
94
|
+
lockfile.flock(File::LOCK_EX)
|
95
|
+
|
96
|
+
target_cache_dir = File.join(user_package_cache, ig_identifier)
|
97
|
+
|
98
|
+
# Break early 1 - something else already created it while we got the lock
|
99
|
+
return target_cache_dir if in_user_package_cache?(ig_identifier)
|
100
|
+
|
101
|
+
# 1. Extract to a temp folder under the cache
|
102
|
+
temp_dir = File.join(user_package_cache, SecureRandom.uuid)
|
103
|
+
FileUtils.mkdir_p(temp_dir)
|
104
|
+
|
105
|
+
system "tar -xzf #{temp_tgz} --directory #{temp_dir}"
|
106
|
+
|
107
|
+
# Break early 2 - something else already created it
|
108
|
+
return target_cache_dir if in_user_package_cache?(ig_identifier)
|
109
|
+
|
110
|
+
# 2. Rename the temp folder to the correct name
|
111
|
+
File.rename(temp_dir, target_cache_dir)
|
112
|
+
|
113
|
+
# return the path in cache
|
114
|
+
target_cache_dir
|
115
|
+
rescue StandardError => e
|
116
|
+
Application['logger'].error(e.full_message)
|
117
|
+
# Don't leave a half extracted package behind
|
118
|
+
FileUtils.remove_dir(temp_dir, true)
|
119
|
+
|
120
|
+
# Return the tgz so that processing can continue
|
121
|
+
temp_tgz
|
122
|
+
ensure
|
123
|
+
if lockfile.present?
|
124
|
+
lockfile.flock(File::LOCK_UN)
|
125
|
+
lockfile.close
|
126
|
+
File.delete(lockfile)
|
127
|
+
end
|
128
|
+
end
|
7
129
|
end
|
8
130
|
end
|
9
131
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
1
3
|
module Inferno
|
2
4
|
module Utils
|
3
5
|
module IgDownloader
|
@@ -14,7 +16,7 @@ module Inferno
|
|
14
16
|
File.join(ig_path, suffix ? "package_#{suffix}.tgz" : 'package.tgz')
|
15
17
|
end
|
16
18
|
|
17
|
-
def load_ig(ig_input, idx = nil,
|
19
|
+
def load_ig(ig_input, idx = nil, output_path = nil)
|
18
20
|
case ig_input
|
19
21
|
when FHIR_PACKAGE_NAME_REG_EX
|
20
22
|
uri = ig_registry_url(ig_input)
|
@@ -29,19 +31,28 @@ module Inferno
|
|
29
31
|
end
|
30
32
|
|
31
33
|
destination = output_path || ig_file(idx)
|
32
|
-
|
33
|
-
get(uri, destination, thor_config)
|
34
|
+
download_file(uri, destination)
|
34
35
|
uri
|
35
36
|
end
|
36
37
|
|
38
|
+
def download_file(uri, destination)
|
39
|
+
# Inspired by Thor `get`
|
40
|
+
# https://github.com/rails/thor/blob/3178667e1727504bf4fb693bf4ac74a5ca6c691e/lib/thor/actions/file_manipulation.rb#L81
|
41
|
+
download = URI.send(:open, uri)
|
42
|
+
IO.copy_stream(download, destination)
|
43
|
+
end
|
44
|
+
|
37
45
|
def ig_registry_url(ig_npm_style)
|
38
|
-
|
46
|
+
if ig_npm_style.include?('@')
|
47
|
+
package_name, version = ig_npm_style.split('@')
|
48
|
+
elsif ig_npm_style.include?('#')
|
49
|
+
package_name, version = ig_npm_style.split('#')
|
50
|
+
else
|
39
51
|
raise StandardError, <<~NO_VERSION
|
40
|
-
No IG version specified for #{ig_npm_style}; you must specify one with '@'. I.e: hl7.fhir.us.core@6.1.0
|
52
|
+
No IG version specified for #{ig_npm_style}; you must specify one with '@' or '#'. I.e: hl7.fhir.us.core@6.1.0
|
41
53
|
NO_VERSION
|
42
54
|
end
|
43
55
|
|
44
|
-
package_name, version = ig_npm_style.split('@')
|
45
56
|
"https://packages.fhir.org/#{package_name}/-/#{package_name}-#{version}.tgz"
|
46
57
|
end
|
47
58
|
|
data/lib/inferno/version.rb
CHANGED
@@ -70,6 +70,62 @@ RSpec.shared_examples 'platform_deployable_test_kit' do
|
|
70
70
|
|
71
71
|
expect(dockerfile_contents.lines.first.chomp).to eq('FROM ruby:3.3.6')
|
72
72
|
end
|
73
|
+
|
74
|
+
context 'when it contains a validator service' do
|
75
|
+
it 'has a data/igs/.keep file' do
|
76
|
+
docker_compose_file_path = File.join(base_path, 'docker-compose.background.yml')
|
77
|
+
docker_compose_contents = YAML.load_file(docker_compose_file_path)
|
78
|
+
|
79
|
+
validator_service_images = [
|
80
|
+
'infernocommunity/fhir-validator-service',
|
81
|
+
'infernocommunity/inferno-resource-validator'
|
82
|
+
]
|
83
|
+
validator_service =
|
84
|
+
docker_compose_contents['services']
|
85
|
+
.any? { |_name, service| validator_service_images.include? service['image'] }
|
86
|
+
|
87
|
+
if validator_service.present?
|
88
|
+
igs_keep_file = File.join(base_path, 'data', 'igs', '.keep')
|
89
|
+
error_message =
|
90
|
+
"Create a 'data/igs/.keep' file and commit it"
|
91
|
+
expect(File.exist?(igs_keep_file)).to be(true), error_message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'uses data/igs as the path for test kit IGs in the hl7 validator service' do
|
96
|
+
docker_compose_file_path = File.join(base_path, 'docker-compose.background.yml')
|
97
|
+
docker_compose_contents = YAML.load_file(docker_compose_file_path)
|
98
|
+
|
99
|
+
hl7_validator_service =
|
100
|
+
docker_compose_contents['services']
|
101
|
+
.find { |_name, service| service['image'] == 'infernocommunity/inferno-resource-validator' }
|
102
|
+
if hl7_validator_service.present?
|
103
|
+
hl7_validator_ig_volume =
|
104
|
+
hl7_validator_service[1]['volumes']&.find { |volume| volume.end_with? 'igs' }
|
105
|
+
|
106
|
+
expected_hl7_validator_volume = './data/igs:/app/igs'
|
107
|
+
error_message =
|
108
|
+
"Update the hl7 validator service IG volume from '#{hl7_validator_ig_volume}' " \
|
109
|
+
"to '#{expected_hl7_validator_volume}'"
|
110
|
+
expect(hl7_validator_ig_volume).to eq(expected_hl7_validator_volume), error_message
|
111
|
+
end
|
112
|
+
|
113
|
+
standalone_validator_service =
|
114
|
+
docker_compose_contents['services']
|
115
|
+
.find { |_name, service| service['image'] == 'infernocommunity/fhir-validator-service' }
|
116
|
+
|
117
|
+
if standalone_validator_service.present?
|
118
|
+
standalone_validator_ig_volume =
|
119
|
+
standalone_validator_service[1]['volumes']&.find { |volume| volume.end_with? 'igs' }
|
120
|
+
|
121
|
+
expected_standalone_validator_volume = './data/igs:/home/igs'
|
122
|
+
error_message =
|
123
|
+
"Update the validator service IG volume from '#{standalone_validator_ig_volume}' " \
|
124
|
+
"to '#{expected_standalone_validator_volume}'"
|
125
|
+
expect(standalone_validator_ig_volume).to eq(expected_standalone_validator_volume), error_message
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
73
129
|
end
|
74
130
|
|
75
131
|
describe 'suites' do
|
@@ -99,6 +155,19 @@ RSpec.shared_examples 'platform_deployable_test_kit' do
|
|
99
155
|
expect(link_labels).to include(*expected_labels), error_message
|
100
156
|
end
|
101
157
|
end
|
158
|
+
|
159
|
+
it 'does not rely on the deprecated `Inferno::DSL::FHIRValidation`' do
|
160
|
+
suites.each do |suite|
|
161
|
+
suite.fhir_validators.each do |name, validators|
|
162
|
+
validators.each do |validator|
|
163
|
+
error_message =
|
164
|
+
"Validator '#{name}' in Suite '#{suite.id}' should be changed to a " \
|
165
|
+
'fhir_resource_validator'
|
166
|
+
expect(validator).to_not be_an_instance_of(Inferno::DSL::FHIRValidation::Validator), error_message
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
102
171
|
end
|
103
172
|
|
104
173
|
describe 'presets' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inferno_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen MacVicar
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2025-
|
13
|
+
date: 2025-03-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -441,6 +441,7 @@ files:
|
|
441
441
|
- lib/inferno/apps/cli/templates/config/nginx.conf.tt
|
442
442
|
- lib/inferno/apps/cli/templates/config/puma.rb.tt
|
443
443
|
- lib/inferno/apps/cli/templates/data/.keep
|
444
|
+
- lib/inferno/apps/cli/templates/data/igs/.keep
|
444
445
|
- lib/inferno/apps/cli/templates/data/redis/.keep
|
445
446
|
- lib/inferno/apps/cli/templates/docker-compose.background.yml.tt
|
446
447
|
- lib/inferno/apps/cli/templates/docker-compose.yml.tt
|
@@ -501,6 +502,7 @@ files:
|
|
501
502
|
- lib/inferno/config/boot.rb
|
502
503
|
- lib/inferno/config/boot/db.rb
|
503
504
|
- lib/inferno/config/boot/executor.rb
|
505
|
+
- lib/inferno/config/boot/ig_files.rb
|
504
506
|
- lib/inferno/config/boot/logging.rb
|
505
507
|
- lib/inferno/config/boot/presets.rb
|
506
508
|
- lib/inferno/config/boot/sidekiq.rb
|
@@ -546,6 +548,7 @@ files:
|
|
546
548
|
- lib/inferno/dsl/jwks.rb
|
547
549
|
- lib/inferno/dsl/links.rb
|
548
550
|
- lib/inferno/dsl/messages.rb
|
551
|
+
- lib/inferno/dsl/must_support_assessment.rb
|
549
552
|
- lib/inferno/dsl/must_support_metadata_extractor.rb
|
550
553
|
- lib/inferno/dsl/oauth_credentials.rb
|
551
554
|
- lib/inferno/dsl/primitive_type.rb
|