Skip to content

Commit

Permalink
FI-3440 review feedback, part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
dehall committed Dec 17, 2024
1 parent f60f763 commit 9ad449e
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 102 deletions.
38 changes: 28 additions & 10 deletions lib/inferno/apps/cli/evaluate.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require_relative '../../../inferno/dsl/fhir_evaluation/evaluator'
require_relative '../../../inferno/dsl/ig'
require_relative '../../../inferno/entities'
require_relative '../../utils/ig_downloader'

require 'tempfile'
Expand All @@ -12,15 +12,7 @@ class Evaluate < Thor::Group

def evaluate(ig_path, data_path, _log_level)
validate_args(ig_path, data_path)

if File.file?(ig_path)
ig = Inferno::DSL::IG.from_file(ig_path)
else
Tempfile.create('package.tgz') do |temp_file|
load_ig(ig_path, nil, { force: true }, temp_file.path)
ig = Inferno::DSL::IG.from_file(temp_file.path)
end
end
_ig = get_ig(ig_path)

# Rule execution, and result output below will be integrated soon.

Expand All @@ -45,6 +37,32 @@ def validate_args(ig_path, data_path)
raise "Provided path '#{data_path}' is not a directory"
end

def get_ig(ig_path)
if File.exist?(ig_path)
ig = Inferno::Entities::IG.from_file(ig_path)
elsif in_user_package_cache?(ig_path.sub('@', '#'))
# NPM syntax for a package identifier is id@version (eg, [email protected])
# but in the cache the separator is # (hl7.fhir.us.core#3.1.1)
cache_directory = File.join(user_package_cache, ig_path.sub('@', '#'))
ig = Inferno::Entities::IG.from_file(cache_directory)
else
Tempfile.create('package.tgz') do |temp_file|
load_ig(ig_path, nil, { force: true }, temp_file.path)
ig = Inferno::Entities::IG.from_file(temp_file.path)
end
end
ig.add_self_to_repository
ig
end

def user_package_cache
File.join(Dir.home, '.fhir', 'packages')
end

def in_user_package_cache?(ig_identifier)
File.directory?(File.join(user_package_cache, ig_identifier))
end

def output_results(results, output)
if output&.end_with?('json')
oo = FhirEvaluator::EvaluationResult.to_operation_outcome(results)
Expand Down
90 changes: 0 additions & 90 deletions lib/inferno/dsl/ig.rb

This file was deleted.

1 change: 1 addition & 0 deletions lib/inferno/entities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require_relative 'entities/has_runnable'
require_relative 'entities/entity'
require_relative 'entities/header'
require_relative 'entities/ig'
require_relative 'entities/message'
require_relative 'entities/request'
require_relative 'entities/result'
Expand Down
147 changes: 147 additions & 0 deletions lib/inferno/entities/ig.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# frozen_string_literal: true

require 'fhir_models'
require 'rubygems/package'
require 'zlib'

require_relative '../repositories/igs'

module Inferno
module Entities
# IG is a wrapper class around the relevant concepts inside an IG.
# Not everything within an IG is currently used by Inferno.
class IG < Entity
ATTRIBUTES = [
:id,
:profiles,
:extensions,
:value_sets,
:search_params,
:examples
].freeze

include Inferno::Entities::Attributes

def initialize(params)
super(params, ATTRIBUTES)

@profiles = []
@extensions = []
@value_sets = []
@examples = []
@search_params = []
end

def self.from_file(ig_path)
raise "#{ig_path} does not exist" unless File.exist?(ig_path)

# fhir_models by default logs the entire content of non-FHIR files
# which could be things like a package.json
original_logger = FHIR.logger
FHIR.logger = Logger.new('/dev/null')

if File.directory?(ig_path)
from_directory(ig_path)
elsif ig_path.end_with? '.tgz'
from_tgz(ig_path)
else
raise "Unable to load #{ig_path} as it does not appear to be a directory or a .tgz file"
end
ensure
FHIR.logger = original_logger if defined? original_logger
end

def self.from_tgz(ig_path)
tar = Gem::Package::TarReader.new(
Zlib::GzipReader.open(ig_path)
)

ig = IG.new({})

tar.each do |entry|
next if skip_item?(entry.full_name, entry.directory?)

begin
resource = FHIR::Json.from_json(entry.read)
next if resource.nil?

ig.handle_resource(resource, entry.full_name)
rescue StandardError
next
end
end
ig
end

def self.from_directory(ig_path)
ig = IG.new({})

Dir.glob("#{ig_path}/**/*") do |f|
next if skip_item?(f, File.directory?(f))

begin
resource = FHIR::Json.from_json(File.read(f))
next if resource.nil?

ig.handle_resource(resource, f)
rescue StandardError
next
end
end
ig
end

# These files aren't FHIR resources
FILES_TO_SKIP = ['package.json', 'validation-summary.json'].freeze

def self.skip_item?(relative_path, is_directory)
return true if is_directory

file_name = relative_path.split('/').last

# TODO: consider making these regexes we can iterate over in a single loop
return true unless file_name.end_with? '.json'
return true unless relative_path.start_with? 'package/'

return true if file_name.start_with? '.' # ignore hidden files
return true if file_name.end_with? '.openapi.json'
return true if FILES_TO_SKIP.include? file_name

false
end

def handle_resource(resource, relative_path)
case resource.resourceType
when 'StructureDefinition'
if resource.type == 'Extension'
extensions.push resource
else
profiles.push resource
end
when 'ValueSet'
value_sets.push resource
when 'SearchParameter'
search_params.push resource
when 'ImplementationGuide'
@id = extract_package_id(resource)
else
examples.push(resource) if relative_path.start_with? 'package/example'
end
end

def extract_package_id(ig_resource)
"#{ig_resource.id}##{ig_resource.version || 'current'}"
end

# @private
def add_self_to_repository
repository.insert(self)
end

# @private
def repository
Inferno::Repositories::IGs.new
end
end
end
end
9 changes: 9 additions & 0 deletions lib/inferno/repositories/igs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require_relative 'in_memory_repository'

module Inferno
module Repositories
# Repository that deals with persistence for the `IG` entity.
class IGs < InMemoryRepository
end
end
end
1 change: 0 additions & 1 deletion lib/inferno/utils/ig_downloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def load_ig(ig_input, idx = nil, thor_config = { verbose: true }, output_path =
else
raise StandardError, <<~FAILED_TO_LOAD
Could not find implementation guide: #{ig_input}
Put its package.tgz file directly in #{ig_path}
FAILED_TO_LOAD
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
RSpec.describe Inferno::DSL::IG do
RSpec.describe Inferno::Entities::IG do
let(:uscore3_package) { File.expand_path('../../fixtures/uscore311.tgz', __dir__) }

describe '#from_file' do
Expand Down

0 comments on commit 9ad449e

Please sign in to comment.