inferno_core 0.5.3 → 0.6.0

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: '090f68164e2fc19d1f97f62dd0fb13c12c77eb12c29bb9b528c9b9cb56f48770'
4
- data.tar.gz: e239a1702ee11a85229db5355ad7c8a415d71b40018c052bb3e2c972c824d12c
3
+ metadata.gz: ee348703cfa562d9316b03c470d62aebd0d4ac232d8dc6029bbac392cb5a64d1
4
+ data.tar.gz: 666f20e627f22eea29c764f9d10c6a2e421142edd0525631566a678f73ed3a45
5
5
  SHA512:
6
- metadata.gz: 2491100649bba23576a442fdeeef561b407e5f7b0b89fbd0827b55ced37e1dcf29c1f5b908c316e44c38301a74d760b5fa8bba4b2cfdf5e039d36fab4c3c181f
7
- data.tar.gz: e05eb12863387acecd5782267f25700d3a2e233ed8f581bda98627a76822c33b5e6a1f0ed8e770a6a9d971c88be610453f0aac9a13245b8412f2cb7673431327
6
+ metadata.gz: 85323238cd047bd7408aa5e50dcbf8bd7ff4bca02bbc985928627bf5431189bb6cee9f0d660218d7bd0684fb38e0f6bfd49aea164cdd08352c463cadaa30ed58
7
+ data.tar.gz: 8cd139293bf774f09749cbe3e723df226b2b74b79a3141ae144e09e573afcc8712495d9fa80eb62b901e05f5f0e2c7cc2810ea4b2e47ee70b53571964ad04683
@@ -1,14 +1,21 @@
1
1
  require_relative '../../../inferno/dsl/fhir_evaluation/evaluator'
2
+ require_relative '../../../inferno/entities'
3
+ require_relative '../../utils/ig_downloader'
4
+
5
+ require 'tempfile'
2
6
 
3
7
  module Inferno
4
8
  module CLI
5
- class Evaluate
6
- def run(ig_path, data_path, _log_level)
9
+ class Evaluate < Thor::Group
10
+ include Thor::Actions
11
+ include Inferno::Utils::IgDownloader
12
+
13
+ def evaluate(ig_path, data_path, _log_level)
7
14
  validate_args(ig_path, data_path)
15
+ _ig = get_ig(ig_path)
8
16
 
9
- # IG Import, rule execution, and result output below will be integrated at phase 2 and 3.
17
+ # Rule execution, and result output below will be integrated soon.
10
18
 
11
- # @ig = File.join(__dir__, 'ig', ig_path)
12
19
  # if data_path
13
20
  # DatasetLoader.from_path(File.join(__dir__, data_path))
14
21
  # else
@@ -30,6 +37,32 @@ module Inferno
30
37
  raise "Provided path '#{data_path}' is not a directory"
31
38
  end
32
39
 
40
+ def get_ig(ig_path)
41
+ if File.exist?(ig_path)
42
+ ig = Inferno::Entities::IG.from_file(ig_path)
43
+ elsif in_user_package_cache?(ig_path.sub('@', '#'))
44
+ # NPM syntax for a package identifier is id@version (eg, hl7.fhir.us.core@3.1.1)
45
+ # but in the cache the separator is # (hl7.fhir.us.core#3.1.1)
46
+ cache_directory = File.join(user_package_cache, ig_path.sub('@', '#'))
47
+ ig = Inferno::Entities::IG.from_file(cache_directory)
48
+ else
49
+ Tempfile.create('package.tgz') do |temp_file|
50
+ load_ig(ig_path, nil, { force: true }, temp_file.path)
51
+ ig = Inferno::Entities::IG.from_file(temp_file.path)
52
+ end
53
+ end
54
+ ig.add_self_to_repository
55
+ ig
56
+ end
57
+
58
+ def user_package_cache
59
+ File.join(Dir.home, '.fhir', 'packages')
60
+ end
61
+
62
+ def in_user_package_cache?(ig_identifier)
63
+ File.directory?(File.join(user_package_cache, ig_identifier))
64
+ end
65
+
33
66
  def output_results(results, output)
34
67
  if output&.end_with?('json')
35
68
  oo = FhirEvaluator::EvaluationResult.to_operation_outcome(results)
@@ -51,8 +51,8 @@ module Inferno
51
51
 
52
52
  # private
53
53
 
54
- def verbose_print(options, *args)
55
- print(color.dim(*args)) if options[:verbose]
54
+ def verbose_print(options, *)
55
+ print(color.dim(*)) if options[:verbose]
56
56
  end
57
57
 
58
58
  def color
@@ -45,7 +45,7 @@ module Inferno
45
45
  type: :string,
46
46
  desc: 'Export evaluation result to outcome.json as an OperationOutcome'
47
47
  def evaluate(ig_path)
48
- Evaluate.new.run(ig_path, options[:data_path], Logger::INFO)
48
+ Evaluate.new.evaluate(ig_path, options[:data_path], Logger::INFO)
49
49
  end
50
50
 
51
51
  desc 'console', 'Start an interactive console session with Inferno'
@@ -71,6 +71,7 @@ module Inferno
71
71
  inside(root_name) do
72
72
  bundle_install
73
73
  inferno_migrate
74
+ initialize_git_repo
74
75
  load_igs
75
76
  end
76
77
 
@@ -85,7 +86,7 @@ module Inferno
85
86
  private
86
87
 
87
88
  def authors
88
- options['author'].presence || [default_author]
89
+ (options['author'].presence || [default_author]).to_json.gsub('"', "'")
89
90
  end
90
91
 
91
92
  def default_author
@@ -106,6 +107,10 @@ module Inferno
106
107
  run 'bundle exec inferno migrate', verbose: !options['quiet'], capture: options['quiet']
107
108
  end
108
109
 
110
+ def initialize_git_repo
111
+ run 'git init -q && git add . && git commit -aqm "initial commit"'
112
+ end
113
+
109
114
  def load_igs
110
115
  config = { verbose: !options['quiet'] }
111
116
  options['implementation_guide']&.each_with_index do |ig, idx|
@@ -119,8 +124,8 @@ module Inferno
119
124
  end
120
125
  end
121
126
 
122
- def say_unless_quiet(*args)
123
- say(*args) unless options['quiet']
127
+ def say_unless_quiet(*)
128
+ say(*) unless options['quiet']
124
129
  end
125
130
  end
126
131
  end
@@ -6,8 +6,6 @@ module Inferno
6
6
  def run
7
7
  ENV['NO_DB'] = 'true'
8
8
 
9
- require_relative '../../../inferno'
10
-
11
9
  Inferno::Application.start(:suites)
12
10
 
13
11
  suites = Inferno::Repositories::TestSuites.new.all
@@ -5,29 +5,18 @@ Gem::Specification.new do |spec|
5
5
  spec.version = <%= module_name %>::VERSION
6
6
  spec.authors = <%= authors %>
7
7
  # spec.email = ['TODO']
8
- spec.date = Time.now.utc.strftime('%Y-%m-%d')
9
8
  spec.summary = '<%= title_name %>'
10
9
  # spec.description = <<~DESCRIPTION
11
10
  # This is a big markdown description of the test kit.
12
11
  # DESCRIPTION
13
12
  # spec.homepage = 'TODO'
14
13
  spec.license = 'Apache-2.0'
15
- spec.add_runtime_dependency 'inferno_core', '~> <%= Inferno::VERSION %>'
16
- spec.add_development_dependency 'database_cleaner-sequel', '~> 1.8'
17
- spec.add_development_dependency 'factory_bot', '~> 6.1'
18
- spec.add_development_dependency 'rspec', '~> 3.10'
19
- spec.add_development_dependency 'webmock', '~> 3.11'
20
- spec.required_ruby_version = Gem::Requirement.new('>= 3.1.2')
14
+ spec.add_dependency 'inferno_core', '~> <%= Inferno::VERSION %>'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.3.6')
21
16
  spec.metadata['inferno_test_kit'] = 'true'
22
17
  # spec.metadata['homepage_uri'] = spec.homepage
23
18
  # spec.metadata['source_code_uri'] = 'TODO'
24
- spec.files = [
25
- Dir['lib/**/*.rb'],
26
- Dir['lib/**/*.json'],
27
- Dir['config/presets/*.json'],
28
- Dir['config/presets/*.json.erb'],
29
- 'LICENSE'
30
- ].flatten
19
+ spec.files = `[ -d .git ] && git ls-files -z lib config/presets LICENSE`.split("\x0")
31
20
 
32
21
  spec.require_paths = ['lib']
33
22
  end
@@ -0,0 +1,77 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ SuggestExtensions: false
7
+ TargetRubyVersion: 3.3
8
+ Exclude:
9
+ - 'Gemfile'
10
+ - 'vendor/**/*'
11
+
12
+ Layout/LineLength:
13
+ Max: 120
14
+
15
+ Layout/MultilineMethodCallIndentation:
16
+ EnforcedStyle: 'indented'
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Style/NumericLiterals:
25
+ Enabled: false
26
+
27
+ Style/OpenStructUse:
28
+ Exclude:
29
+ - 'spec/**/*'
30
+
31
+ Style/SymbolArray:
32
+ Enabled: false
33
+
34
+ Style/WordArray:
35
+ Enabled: false
36
+
37
+ Metrics/AbcSize:
38
+ Enabled: false
39
+
40
+ Metrics/BlockLength:
41
+ Enabled: false
42
+
43
+ Metrics/ClassLength:
44
+ Enabled: false
45
+
46
+ Metrics/MethodLength:
47
+ Enabled: false
48
+
49
+ Metrics/ModuleLength:
50
+ Enabled: false
51
+
52
+ Metrics/PerceivedComplexity:
53
+ Enabled: false
54
+
55
+ Metrics/ParameterLists:
56
+ Enabled: false
57
+
58
+ RSpec/AnyInstance:
59
+ Enabled: false
60
+
61
+ RSpec/ExampleLength:
62
+ Enabled: false
63
+
64
+ RSpec/MultipleExpectations:
65
+ Enabled: false
66
+
67
+ RSpec/MultipleMemoizedHelpers:
68
+ Enabled: false
69
+
70
+ RSpec/NestedGroups:
71
+ Enabled: false
72
+
73
+ RSpec/NotToNot:
74
+ EnforcedStyle: to_not
75
+
76
+ Gemspec/RequireMFA:
77
+ Enabled: false
@@ -1 +1 @@
1
- 3.1.2
1
+ 3.3.6
@@ -1 +1 @@
1
- ruby 3.1.2
1
+ ruby 3.3.6
@@ -1,4 +1,4 @@
1
- FROM ruby:3.1.2
1
+ FROM ruby:3.3.6
2
2
 
3
3
  ENV INSTALL_PATH=/opt/inferno/
4
4
  ENV APP_ENV=production
@@ -6,8 +6,15 @@ gemspec
6
6
 
7
7
  group :development, :test do
8
8
  gem 'debug'
9
+ gem 'rubocop', '~> 1.9'
10
+ gem 'rubocop-rspec', require: false
9
11
  end
10
12
 
11
13
  group :test do
14
+ gem 'database_cleaner-sequel', '~> 1.8'
15
+ gem 'factory_bot', '~> 6.1'
12
16
  gem 'rack-test'
17
+ gem 'rspec', '~> 3.10'
18
+ gem 'simplecov', '0.21.2', require: false
19
+ gem 'webmock', '~> 3.11'
13
20
  end
@@ -56,4 +56,4 @@ module <%= module_name %>
56
56
  # using their id
57
57
  group from: :patient_group
58
58
  end
59
- end
59
+ end
@@ -6,6 +6,7 @@ Inferno::Application.register_provider(:suites) do
6
6
  require 'inferno/entities/test_group'
7
7
  require 'inferno/entities/test_suite'
8
8
  require 'inferno/entities/test_kit'
9
+ require 'inferno/route_storage'
9
10
 
10
11
  files_to_load = Dir.glob(File.join(Dir.pwd, 'lib', '*.rb'))
11
12
 
@@ -380,10 +380,10 @@ module Inferno
380
380
  # using multiple validators
381
381
  # @param required_suite_options [Hash] suite options that must be
382
382
  # selected in order to use this validator
383
- def fhir_resource_validator(name = :default, required_suite_options: nil, &block)
383
+ def fhir_resource_validator(name = :default, required_suite_options: nil, &)
384
384
  current_validators = fhir_validators[name] || []
385
385
 
386
- new_validator = Inferno::DSL::FHIRResourceValidation::Validator.new(name, id, required_suite_options, &block)
386
+ new_validator = Inferno::DSL::FHIRResourceValidation::Validator.new(name, id, required_suite_options, &)
387
387
 
388
388
  current_validators.reject! { |validator| validator.requirements == required_suite_options }
389
389
  current_validators << new_validator
@@ -254,10 +254,10 @@ module Inferno
254
254
  # using multiple validators
255
255
  # @param required_suite_options [Hash] suite options that must be
256
256
  # selected in order to use this validator
257
- def validator(name = :default, required_suite_options: nil, &block)
257
+ def validator(name = :default, required_suite_options: nil, &)
258
258
  current_validators = fhir_validators[name] || []
259
259
 
260
- new_validator = Inferno::DSL::FHIRValidation::Validator.new(required_suite_options, &block)
260
+ new_validator = Inferno::DSL::FHIRValidation::Validator.new(required_suite_options, &)
261
261
 
262
262
  current_validators.reject! { |validator| validator.requirements == required_suite_options }
263
263
  current_validators << new_validator
@@ -12,7 +12,7 @@ module Inferno
12
12
  # class AuthorizedEndpoint < Inferno::DSL::SuiteEndpoint
13
13
  # # Identify the incoming request based on a bearer token
14
14
  # def test_run_identifier
15
- # request.header['authorization']&.delete_prefix('Bearer ')
15
+ # request.headers['authorization']&.delete_prefix('Bearer ')
16
16
  # end
17
17
  #
18
18
  # # Return a json FHIR Patient resource
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fhir_models'
4
+ require 'pathname'
5
+ require 'rubygems/package'
6
+ require 'zlib'
7
+
8
+ require_relative '../repositories/igs'
9
+
10
+ module Inferno
11
+ module Entities
12
+ # IG is a wrapper class around the relevant concepts inside an IG.
13
+ # Not everything within an IG is currently used by Inferno.
14
+ class IG < Entity
15
+ ATTRIBUTES = [
16
+ :id,
17
+ :profiles,
18
+ :extensions,
19
+ :value_sets,
20
+ :search_params,
21
+ :examples
22
+ ].freeze
23
+
24
+ include Inferno::Entities::Attributes
25
+
26
+ def initialize(**params)
27
+ super(params, ATTRIBUTES)
28
+
29
+ @profiles = []
30
+ @extensions = []
31
+ @value_sets = []
32
+ @examples = []
33
+ @search_params = []
34
+ end
35
+
36
+ def self.from_file(ig_path)
37
+ raise "#{ig_path} does not exist" unless File.exist?(ig_path)
38
+
39
+ # fhir_models by default logs the entire content of non-FHIR files
40
+ # which could be things like a package.json
41
+ original_logger = FHIR.logger
42
+ FHIR.logger = Logger.new('/dev/null')
43
+
44
+ if File.directory?(ig_path)
45
+ from_directory(ig_path)
46
+ elsif ig_path.end_with? '.tgz'
47
+ from_tgz(ig_path)
48
+ else
49
+ raise "Unable to load #{ig_path} as it does not appear to be a directory or a .tgz file"
50
+ end
51
+ ensure
52
+ FHIR.logger = original_logger if defined? original_logger
53
+ end
54
+
55
+ def self.from_tgz(ig_path)
56
+ tar = Gem::Package::TarReader.new(
57
+ Zlib::GzipReader.open(ig_path)
58
+ )
59
+
60
+ ig = IG.new
61
+
62
+ tar.each do |entry|
63
+ next if skip_item?(entry.full_name, entry.directory?)
64
+
65
+ begin
66
+ resource = FHIR::Json.from_json(entry.read)
67
+ next if resource.nil?
68
+
69
+ ig.handle_resource(resource, entry.full_name)
70
+ rescue StandardError
71
+ next
72
+ end
73
+ end
74
+ ig
75
+ end
76
+
77
+ def self.from_directory(ig_directory)
78
+ ig = IG.new
79
+
80
+ ig_path = Pathname.new(ig_directory)
81
+ Dir.glob("#{ig_path}/**/*") do |f|
82
+ relative_path = Pathname.new(f).relative_path_from(ig_path).to_s
83
+ next if skip_item?(relative_path, File.directory?(f))
84
+
85
+ begin
86
+ resource = FHIR::Json.from_json(File.read(f))
87
+ next if resource.nil?
88
+
89
+ ig.handle_resource(resource, relative_path)
90
+ rescue StandardError
91
+ next
92
+ end
93
+ end
94
+ ig
95
+ end
96
+
97
+ # These files aren't FHIR resources
98
+ FILES_TO_SKIP = ['package.json', 'validation-summary.json'].freeze
99
+
100
+ def self.skip_item?(relative_path, is_directory)
101
+ return true if is_directory
102
+
103
+ file_name = relative_path.split('/').last
104
+
105
+ return true unless file_name.end_with? '.json'
106
+ return true unless relative_path.start_with? 'package/'
107
+
108
+ return true if file_name.start_with? '.' # ignore hidden files
109
+ return true if file_name.end_with? '.openapi.json'
110
+ return true if FILES_TO_SKIP.include? file_name
111
+
112
+ false
113
+ end
114
+
115
+ def handle_resource(resource, relative_path)
116
+ case resource.resourceType
117
+ when 'StructureDefinition'
118
+ if resource.type == 'Extension'
119
+ extensions.push resource
120
+ else
121
+ profiles.push resource
122
+ end
123
+ when 'ValueSet'
124
+ value_sets.push resource
125
+ when 'SearchParameter'
126
+ search_params.push resource
127
+ when 'ImplementationGuide'
128
+ @id = extract_package_id(resource)
129
+ else
130
+ examples.push(resource) if relative_path.start_with? 'package/example'
131
+ end
132
+ end
133
+
134
+ def extract_package_id(ig_resource)
135
+ "#{ig_resource.id}##{ig_resource.version || 'current'}"
136
+ end
137
+
138
+ # @private
139
+ def add_self_to_repository
140
+ repository.insert(self)
141
+ end
142
+
143
+ # @private
144
+ def repository
145
+ Inferno::Repositories::IGs.new
146
+ end
147
+ end
148
+ end
149
+ end
@@ -2,6 +2,7 @@ require_relative 'entities/attributes'
2
2
  require_relative 'entities/has_runnable'
3
3
  require_relative 'entities/entity'
4
4
  require_relative 'entities/header'
5
+ require_relative 'entities/ig'
5
6
  require_relative 'entities/message'
6
7
  require_relative 'entities/request'
7
8
  require_relative 'entities/result'