inferno_core 0.5.3 → 0.6.0

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