From 7e4c98595ba492208dad61b74760c5333e289b16 Mon Sep 17 00:00:00 2001 From: Chase Date: Wed, 29 Jul 2020 12:39:43 -0400 Subject: [PATCH 01/26] saving some progress --- generator/generic/generic_generator.rb | 18 ++++- generator/generic/search_test.rb | 27 +++++++ generator/generic/static/generic_utilities.rb | 58 ++++++++++++++ .../templates/sequence_definition.rb.erb | 12 +++ generator/search_parameter_metadata.rb | 75 +++++++++++++++++++ generator/sequence_metadata.rb | 53 ++++++++++++- generator/uscore/templates/sequence.rb.erb | 1 + generator/uscore/uscore_generator.rb | 2 +- 8 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 generator/generic/search_test.rb create mode 100644 generator/generic/static/generic_utilities.rb create mode 100644 generator/generic/templates/sequence_definition.rb.erb create mode 100644 generator/search_parameter_metadata.rb diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index e609b61f0..5814e3f81 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -2,21 +2,28 @@ require_relative '../generator_base' require_relative '../sequence_metadata' +require_relative '../search_parameter_metadata' require_relative './read_test' require_relative './profile_validation_test' +require_relative './search_test' module Inferno module Generator class GenericGenerator < Generator::Base include ReadTest include ProfileValidationTest + include SearchTest def resource_profiles resources_by_type['StructureDefinition'].reject { |definition| definition['type'] == 'Extension' } end def sequence_metadata - @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile) } + @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, capability_statement) } + end + + def search_parameter_metadata + @search_parameter_metadata ||= resources_by_type['SearchParameter'].map { |parameter_json| SearchParameterMetadata.new(parameter_json) } end def generate @@ -28,6 +35,7 @@ def generate_sequences sequence_metadata.each do |metadata| create_read_test(metadata) create_profile_validation_test(metadata) + create_search_tests(metadata) generate_sequence(metadata) end end @@ -41,6 +49,14 @@ def generate_sequence(metadata) File.write(file_name, output) end + def generate_sequence_definitions(metadata) + file_name = sequence_out_path + '/profile_definitions/' + sequence[:name].downcase + '_definitions.rb' + template = ERB.new(File.read(File.join(__dir__, 'templates/sequence_definition.rb.erb'))) + output = template.result_with_hash(metadata) + FileUtils.mkdir_p(sequence_out_path) unless File.directory?(sequence_out_path) + File.write(file_name, output) + end + def module_file_path "#{module_yml_out_path}/#{@path}_module.yml" end diff --git a/generator/generic/search_test.rb b/generator/generic/search_test.rb new file mode 100644 index 000000000..3da126254 --- /dev/null +++ b/generator/generic/search_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative '../test_metadata' + +module Inferno + module Generator + module SearchTest + def create_search_tests(metadata) + metadata.searches.each do |search| + search_test = TestMetadata.new( + title: "Server returns expected results from #{metadata.resource_type} search by #{search[:parameters].join('+')}", + key: :"search_by_#{search[:parameters].map(&:underscore).join('_')}", + description: "This test will verify that #{metadata.resource_type} resources can be searched from the server." + ) + + search_param_assignment = search[:parameters] + .map { |parameter| "#{parameter}_val = get_value_for" } + search_test.code = %( + return unless @resource_found.present? + + ) + metadata.add_test(search_test) + end + end + end + end +end diff --git a/generator/generic/static/generic_utilities.rb b/generator/generic/static/generic_utilities.rb new file mode 100644 index 000000000..da712d52f --- /dev/null +++ b/generator/generic/static/generic_utilities.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Inferno + module GenericUtilities + + def resolve_path(elements, path) + elements = Array.wrap(elements) + return elements if path.blank? + + paths = path.split('.') + + elements.flat_map do |element| + resolve_path(element&.send(paths.first), paths.drop(1).join('.')) + end.compact + end + + def get_value_for_search_param(element, include_system = false) + search_value = case element + when FHIR::Period + if element.start.present? + 'gt' + (DateTime.xmlschema(element.start) - 1).xmlschema + else + end_datetime = get_fhir_datetime_range(element.end)[:end] + 'lt' + (end_datetime + 1).xmlschema + end + when FHIR::Reference + element.reference + when FHIR::CodeableConcept + if include_system + coding_with_code = resolve_element_from_path(element, 'coding') { |coding| coding.code.present? } + coding_with_code.present? ? "#{coding_with_code.system}|#{coding_with_code.code}" : nil + else + resolve_element_from_path(element, 'coding.code') + end + when FHIR::Identifier + if include_system + "#{element.system}|#{element.value}" + else + element.value + end + when FHIR::Coding + if include_system + "#{element.system}|#{element.code}" + else + element.code + end + when FHIR::HumanName + element.family || element.given&.first || element.text + when FHIR::Address + element.text || element.city || element.state || element.postalCode || element.country + else + element + end + escaped_value = search_value&.gsub(',', '\\,') + escaped_value + end + end +end diff --git a/generator/generic/templates/sequence_definition.rb.erb b/generator/generic/templates/sequence_definition.rb.erb new file mode 100644 index 000000000..3af5f1fee --- /dev/null +++ b/generator/generic/templates/sequence_definition.rb.erb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Inferno + module USCore310ProfileDefinitions + class <%= metadata.class_name %>Definitions + <%=must_support_constants%> + <%=delayed_references_constant%> + + <%=bindings_constants%> + end + end +end \ No newline at end of file diff --git a/generator/search_parameter_metadata.rb b/generator/search_parameter_metadata.rb new file mode 100644 index 000000000..f2ec0f52d --- /dev/null +++ b/generator/search_parameter_metadata.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Inferno + module Generator + class SearchParameterMetadata + attr_reader :search_parameter_json + attr_writer :url, + :code, + :expression, + :multiple_or, + :multiple_or_expectation, + :multiple_and, + :multiple_and_expectation, + :modifiers, + :comparators + + EXPECTATION_URL = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation' + def initialize(search_parameter_json) + @search_parameter_json = search_parameter_json + end + + def url + @url ||= @search_parameter_json['url'] + end + + def code + @code ||= @search_parameter_json['code'] + end + + def expression + @expression ||= @search_parameter_json['expression'] + end + + # whether multiple or is allowed + def multiple_or + @multiple_or ||= @search_parameter_json['multipleOr'] + end + + # expectation if multiple or is allowed - unsure if this is generic or just us core specific + def multiple_or_expectation + @multiple_or_expectation ||= @search_parameter_json['_multipleOr']['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + end + + # whether multiple and is allowed + def multiple_and + @multiple_and ||= @search_parameter_json['multipleAnd'] + end + + # expectation if multiple and is allowed - unsure if this is generic or just us core specific + def multiple_and_expectation + @multiple_and_expectation ||= @search_parameter_json['_multipleAnd']['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + end + + def comparators + return [] if @search_parameter_json['comparator'].nil? + + @comparators ||= @search_parameter_json['comparator'].each_with_index.map do |comparator, index| + expectation_extension = @search_parameter_json['_comparator'] # unsure if this is us core specific + expectation = expectation_extension[index]['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] unless expectation_extension.nil? + { comparator: comparator, expectation: expectation } + end + end + + def modifiers + return [] if @search_parameter_json['modifier'].nil? + + @modifiers ||= @search_parameter_json['modifier'].each_with_index.map do |modifier, index| + expectation_extension = @search_parameter_json['_modifier'] # unsure if this is us core specific + expectation = expectation_extension[index]['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] unless expectation_extension.nil? + { comparator: modifier, expectation: expectation } + end + end + end + end +end diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 7603a5b74..41bcf31dc 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -4,18 +4,27 @@ module Inferno module Generator class SequenceMetadata attr_reader :profile, - :tests + :tests, + :capabilities attr_writer :class_name, :file_name, :requirements, :sequence_name, :test_id_prefix, :title, - :url + :url, + :searches, + :must_supports, + :search_parameters - def initialize(profile) + def initialize(profile, capability_statement = nil) @profile = profile @tests = [] + @capabilities = if capability_statement.present? + capability_statement['rest'] + .find { |rest| rest['mode'] == 'server' }['resource'] + .find { |resource| resource['type'] == profile['type'] } + end end def resource_type @@ -55,6 +64,44 @@ def add_test(test) @tests << test end + def searches + @searches ||= searches_from_capability_statement(capabilities) + end + + def searches_from_capability_statement(capabilities) + return [] unless capabilities.present? + + capability_statement_expectation_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation' + search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' + + searches = [] + basic_searches = capabilities['searchParam'] + basic_searches&.each do |search_param| + new_search = { + parameters: [search_param['name']], + expectation: search_param['extension'].find { |ext| ext['url'] == capability_statement_expectation_url } ['valueCode'] + } + searches << new_search + + param_url = search_param['definition'] + end + + capabilities['extension'] + .select { |ext| ext['url'] == search_combo_url } + .each do |combo| + expectation = combo['extension'].find { |ext| ext['url'] == capability_statement_expectation_url }['valueCode'] + combo_params = combo['extension'] + .reject { |ext| ext['url'] == capability_statement_expectation_url } + .map { |ext| ext['valueString'] } + new_search = { + parameters: combo_params, + expectation: expectation + } + searches << new_search + end + searches + end + private def initial_sequence_name diff --git a/generator/uscore/templates/sequence.rb.erb b/generator/uscore/templates/sequence.rb.erb index 6b7aaa2aa..9ea63ef7b 100644 --- a/generator/uscore/templates/sequence.rb.erb +++ b/generator/uscore/templates/sequence.rb.erb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative './data_absent_reason_checker' +require_relative './generic_utilities' module Inferno module Sequence diff --git a/generator/uscore/uscore_generator.rb b/generator/uscore/uscore_generator.rb index 93e8a7447..5eacbb33f 100644 --- a/generator/uscore/uscore_generator.rb +++ b/generator/uscore/uscore_generator.rb @@ -889,7 +889,7 @@ def param_value_name(param) "search_params[:#{param_key}]" end - def get_first_search(search_parameters, sequence) + defc(search_parameters, sequence) save_resource_references_arguments = [ "versioned_resource_class('#{sequence[:resource]}')", "@#{sequence[:resource].underscore}_ary#{'[patient]' unless sequence[:delayed_sequence]}", From c751a7fe2c78011fce008c2e4e73e5ac1a0ed08b Mon Sep 17 00:00:00 2001 From: Chase Date: Thu, 30 Jul 2020 08:08:53 -0400 Subject: [PATCH 02/26] more progress --- generator/generic/generic_generator.rb | 60 +++++++- generator/generic/search_test.rb | 35 ++++- generator/generic/static/generic_utilities.rb | 5 +- generator/generic/templates/sequence.rb.erb | 6 + .../templates/sequence_definition.rb.erb | 7 +- generator/search_parameter_metadata.rb | 7 +- generator/sequence_metadata.rb | 131 ++++++++++++++++-- generator/test_metadata.rb | 3 +- 8 files changed, 226 insertions(+), 28 deletions(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 5814e3f81..3cedf5e77 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -19,7 +19,7 @@ def resource_profiles end def sequence_metadata - @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, capability_statement) } + @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, search_parameter_metadata, capability_statement) } end def search_parameter_metadata @@ -28,6 +28,7 @@ def search_parameter_metadata def generate generate_sequences + copy_static_files generate_module end @@ -47,16 +48,61 @@ def generate_sequence(metadata) output = template.result_with_hash(metadata: metadata) FileUtils.mkdir_p(sequence_out_path + '/') unless File.directory?(sequence_out_path + '/') File.write(file_name, output) + + generate_sequence_definitions(metadata) end def generate_sequence_definitions(metadata) - file_name = sequence_out_path + '/profile_definitions/' + sequence[:name].downcase + '_definitions.rb' + file_name = sequence_out_path + '/profile_definitions/' + metadata.file_name + '_definitions.rb' template = ERB.new(File.read(File.join(__dir__, 'templates/sequence_definition.rb.erb'))) - output = template.result_with_hash(metadata) - FileUtils.mkdir_p(sequence_out_path) unless File.directory?(sequence_out_path) + output = template.result_with_hash(sequence_definition_hash(metadata)) + FileUtils.mkdir_p(sequence_out_path + '/profile_definitions/') unless File.directory?(sequence_out_path + '/profile_definitions/') File.write(file_name, output) end + def sequence_definition_hash(metadata) + search_parameters = metadata.search_parameter_metadata&.map do |param_metadata| + { + url: param_metadata.url, + code: param_metadata.code, + expression: param_metadata.expression, + multipleOr: param_metadata.multiple_or, + multipleOrExpectation: param_metadata.multiple_or_expectation, + multipleAnd: param_metadata.multiple_and, + multipleAndExpectation: param_metadata.multiple_and_expectation, + modifiers: param_metadata.modifiers, + comparators: param_metadata.comparators + } + end + search_parameters ||= [] + { + class_name: metadata.class_name + 'Definition', + search_parameters: structure_to_string(search_parameters) + } + end + + def structure_to_string(struct) + if struct.is_a? Hash + %({ + #{struct.map { |k, v| "#{k}: #{structure_to_string(v)}" }.join(",\n")} + }) + elsif struct.is_a? Array + if struct.empty? + '[]' + else + %([ + #{struct.map { |el| structure_to_string(el) }.join(",\n")} + ]) + end + elsif struct.is_a? String + "'#{struct}'" + elsif [true, false].include? struct + struct.to_s + else + "''" + end + end + def module_file_path "#{module_yml_out_path}/#{@path}_module.yml" end @@ -75,6 +121,12 @@ def generate_module File.write(file_name, output) end + + def copy_static_files + Dir.glob(File.join(__dir__, 'static', '*')).each do |static_file| + FileUtils.cp(static_file, sequence_out_path) + end + end end end end diff --git a/generator/generic/search_test.rb b/generator/generic/search_test.rb index 3da126254..32cc71b02 100644 --- a/generator/generic/search_test.rb +++ b/generator/generic/search_test.rb @@ -10,18 +10,43 @@ def create_search_tests(metadata) search_test = TestMetadata.new( title: "Server returns expected results from #{metadata.resource_type} search by #{search[:parameters].join('+')}", key: :"search_by_#{search[:parameters].map(&:underscore).join('_')}", - description: "This test will verify that #{metadata.resource_type} resources can be searched from the server." + description: "This test will verify that #{metadata.resource_type} resources can be searched from the server.", + optional: search[:expectation] != 'SHALL' ) - search_param_assignment = search[:parameters] - .map { |parameter| "#{parameter}_val = get_value_for" } + search_parameter_assignments = search[:parameters].map do |parameter| + param_metadata = metadata.search_parameter_metadata.find { |parameter_metadata| parameter_metadata.code == parameter } + path = param_metadata + .expression + .gsub(/(? < SequenceBase + include Inferno::GenericUtilities + title '<%= metadata.title %> Tests' description 'Verify support for the server capabilities required by the <%= metadata.title %> profile.' details %( @@ -12,6 +16,8 @@ module Inferno @resource_found = nil + <%=metadata.create_search_validation%> + <% metadata.tests.each_with_index do |test, idx|%> <% if test.key.present? %> test :<%= test.key %> do diff --git a/generator/generic/templates/sequence_definition.rb.erb b/generator/generic/templates/sequence_definition.rb.erb index 3af5f1fee..9633dc113 100644 --- a/generator/generic/templates/sequence_definition.rb.erb +++ b/generator/generic/templates/sequence_definition.rb.erb @@ -2,11 +2,8 @@ module Inferno module USCore310ProfileDefinitions - class <%= metadata.class_name %>Definitions - <%=must_support_constants%> - <%=delayed_references_constant%> - - <%=bindings_constants%> + class <%= class_name %> + SEARCH_PARAMETERS = <%= search_parameters %>.freeze end end end \ No newline at end of file diff --git a/generator/search_parameter_metadata.rb b/generator/search_parameter_metadata.rb index f2ec0f52d..726ff5e2d 100644 --- a/generator/search_parameter_metadata.rb +++ b/generator/search_parameter_metadata.rb @@ -6,6 +6,7 @@ class SearchParameterMetadata attr_reader :search_parameter_json attr_writer :url, :code, + :type, :expression, :multiple_or, :multiple_or_expectation, @@ -27,6 +28,10 @@ def code @code ||= @search_parameter_json['code'] end + def type + @type ||= @search_parameter_json['type'] + end + def expression @expression ||= @search_parameter_json['expression'] end @@ -67,7 +72,7 @@ def modifiers @modifiers ||= @search_parameter_json['modifier'].each_with_index.map do |modifier, index| expectation_extension = @search_parameter_json['_modifier'] # unsure if this is us core specific expectation = expectation_extension[index]['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] unless expectation_extension.nil? - { comparator: modifier, expectation: expectation } + { modifier: modifier, expectation: expectation } end end end diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 41bcf31dc..8f428fbb0 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -5,7 +5,8 @@ module Generator class SequenceMetadata attr_reader :profile, :tests, - :capabilities + :capabilities, + :search_parameter_metadata attr_writer :class_name, :file_name, :requirements, @@ -14,17 +15,20 @@ class SequenceMetadata :title, :url, :searches, - :must_supports, - :search_parameters + :must_supports - def initialize(profile, capability_statement = nil) + def initialize(profile, all_search_parameter_metadata, capability_statement = nil) @profile = profile @tests = [] - @capabilities = if capability_statement.present? - capability_statement['rest'] - .find { |rest| rest['mode'] == 'server' }['resource'] - .find { |resource| resource['type'] == profile['type'] } - end + return unless capability_statement.present? + + @capabilities = capability_statement['rest'] + .find { |rest| rest['mode'] == 'server' }['resource'] + .find { |resource| resource['type'] == profile['type'] } + + @search_parameter_metadata = capabilities['searchParam']&.map do |param| + all_search_parameter_metadata.find { |param_metadata| param_metadata.url == param['definition'] } + end end def resource_type @@ -82,8 +86,6 @@ def searches_from_capability_statement(capabilities) expectation: search_param['extension'].find { |ext| ext['url'] == capability_statement_expectation_url } ['valueCode'] } searches << new_search - - param_url = search_param['definition'] end capabilities['extension'] @@ -102,6 +104,113 @@ def searches_from_capability_statement(capabilities) searches end + def create_search_validation + search_validators = '' + search_parameter_metadata&.each do |parameter_metadata| + type = parameter_metadata.type + path = parameter_metadata.expression + .gsub(/(? Date: Thu, 30 Jul 2020 09:31:37 -0400 Subject: [PATCH 03/26] add interaction test --- generator/generic/generic_generator.rb | 27 +--- generator/generic/interaction_test.rb | 34 +++++ generator/generic/templates/sequence.rb.erb | 2 +- generator/generic_generator_utilities.rb | 139 ++++++++++++++++++++ generator/search_parameter_metadata.rb | 4 +- generator/sequence_metadata.rb | 136 ++++--------------- 6 files changed, 206 insertions(+), 136 deletions(-) create mode 100644 generator/generic/interaction_test.rb create mode 100644 generator/generic_generator_utilities.rb diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 3cedf5e77..e7102cbd7 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -6,6 +6,8 @@ require_relative './read_test' require_relative './profile_validation_test' require_relative './search_test' +require_relative './interaction_test' +require_relative '../generic_generator_utilities' module Inferno module Generator @@ -13,6 +15,8 @@ class GenericGenerator < Generator::Base include ReadTest include ProfileValidationTest include SearchTest + include InteractionTest + include Inferno::Generator::GenericGeneratorUtilties def resource_profiles resources_by_type['StructureDefinition'].reject { |definition| definition['type'] == 'Extension' } @@ -37,6 +41,7 @@ def generate_sequences create_read_test(metadata) create_profile_validation_test(metadata) create_search_tests(metadata) + create_interaction_tests(metadata) generate_sequence(metadata) end end @@ -81,28 +86,6 @@ def sequence_definition_hash(metadata) } end - def structure_to_string(struct) - if struct.is_a? Hash - %({ - #{struct.map { |k, v| "#{k}: #{structure_to_string(v)}" }.join(",\n")} - }) - elsif struct.is_a? Array - if struct.empty? - '[]' - else - %([ - #{struct.map { |el| structure_to_string(el) }.join(",\n")} - ]) - end - elsif struct.is_a? String - "'#{struct}'" - elsif [true, false].include? struct - struct.to_s - else - "''" - end - end - def module_file_path "#{module_yml_out_path}/#{@path}_module.yml" end diff --git a/generator/generic/interaction_test.rb b/generator/generic/interaction_test.rb new file mode 100644 index 000000000..092511631 --- /dev/null +++ b/generator/generic/interaction_test.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative '../test_metadata' + +module Inferno + module Generator + module InteractionTest + def create_interaction_tests(metadata) + metadata.interactions.each do |interaction| + next if ['read', 'search-type'].include? interaction[:code] + + interaction_test = TestMetadata.new( + title: "Server supports the #{metadata.resource_type} #{interaction[:code]} interaction", + key: "resource_#{interaction[:code].gsub('-', '_').downcase}".to_sym, + description: "This test will verify that #{metadata.resource_type} #{interaction[:code]} interactions are supported by the server.", + optional: interaction[:expectation] != 'SHALL' + ) + + validate_reply_args = [ + '@resource_found', + "versioned_resource_class('#{metadata.resource_type}')" + ] + validate_reply_args_string = validate_reply_args.join(', ') + + interaction_test.code = %( + skip 'No resource found from Read test' unless @resource_found.present? + validate_#{interaction[:code].gsub('-', '_')}_reply(#{validate_reply_args_string}) + ) + metadata.add_test(interaction_test) + end + end + end + end +end diff --git a/generator/generic/templates/sequence.rb.erb b/generator/generic/templates/sequence.rb.erb index 744442080..7c2fce238 100644 --- a/generator/generic/templates/sequence.rb.erb +++ b/generator/generic/templates/sequence.rb.erb @@ -16,7 +16,7 @@ module Inferno @resource_found = nil - <%=metadata.create_search_validation%> + <%=metadata.create_search_validation(metadata)%> <% metadata.tests.each_with_index do |test, idx|%> <% if test.key.present? %> diff --git a/generator/generic_generator_utilities.rb b/generator/generic_generator_utilities.rb new file mode 100644 index 000000000..b12247579 --- /dev/null +++ b/generator/generic_generator_utilities.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +module Inferno + module Generator + module GenericGeneratorUtilties + + EXPECTATION_URL = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation' + + def structure_to_string(struct) + if struct.is_a? Hash + %({ + #{struct.map { |k, v| "#{k}: #{structure_to_string(v)}" }.join(",\n")} + }) + elsif struct.is_a? Array + if struct.empty? + '[]' + else + %([ + #{struct.map { |el| structure_to_string(el) }.join(",\n")} + ]) + end + elsif struct.is_a? String + "'#{struct}'" + elsif [true, false].include? struct + struct.to_s + else + "''" + end + end + + def create_search_validation(sequence_metadata) + search_validators = '' + sequence_metadata.search_parameter_metadata&.each do |parameter_metadata| + type = parameter_metadata.type + path = parameter_metadata.expression + .gsub(/(? Date: Tue, 4 Aug 2020 10:19:57 -0400 Subject: [PATCH 04/26] fix some interaction tests --- generator/generic/interaction_test.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/generator/generic/interaction_test.rb b/generator/generic/interaction_test.rb index 092511631..5981d73e1 100644 --- a/generator/generic/interaction_test.rb +++ b/generator/generic/interaction_test.rb @@ -7,7 +7,10 @@ module Generator module InteractionTest def create_interaction_tests(metadata) metadata.interactions.each do |interaction| - next if ['read', 'search-type'].include? interaction[:code] + next if ['read', 'search-type'].include? interaction[:code] # already have tests for + next if ['create', 'update', 'patch', 'delete', 'history-type'].include? interaction[:code] # not currently supported + + interaction[:code] = 'history' if interaction[:code] == 'history-instance' # how the history interaction is called already interaction_test = TestMetadata.new( title: "Server supports the #{metadata.resource_type} #{interaction[:code]} interaction", From 4621846457e85fd29377da0690d9ecab9ed6a440 Mon Sep 17 00:00:00 2001 From: Chase Zhou Date: Tue, 4 Aug 2020 14:36:40 -0400 Subject: [PATCH 05/26] some fixes --- generator/generic/search_test.rb | 9 +++++++-- generator/generic/templates/sequence_definition.rb.erb | 2 +- generator/generic_generator_utilities.rb | 5 +++-- generator/search_parameter_metadata.rb | 1 + generator/uscore/uscore_generator.rb | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/generator/generic/search_test.rb b/generator/generic/search_test.rb index 32cc71b02..29e1f3752 100644 --- a/generator/generic/search_test.rb +++ b/generator/generic/search_test.rb @@ -28,13 +28,13 @@ def create_search_tests(metadata) as_type = path.scan(/.as\((.*?)\)/).flatten.first path = path.gsub(/.as\((.*?)\)/, capitalize_first_letter(as_type)) if as_type.present? - "#{parameter.gsub('-', '_')}_val = find_search_parameter_value_from_resource(@resource_found, '#{path}')" + "#{search_param_value_name(parameter)} = find_search_parameter_value_from_resource(@resource_found, '#{path}')" end search_test.code = %( skip 'No resource found from Read test' unless @resource_found.present? #{search_parameter_assignments.join("\n")} search_parameters = { - #{search[:parameters].map { |parameter| "'#{parameter}': #{parameter.gsub('-', '_')}_val" }.join(",\n")} + #{search[:parameters].map { |parameter| "'#{parameter}': #{search_param_value_name(parameter)}" }.join(",\n")} } reply = get_resource_by_params(versioned_resource_class('#{metadata.resource_type}'), search_parameters) @@ -44,6 +44,11 @@ def create_search_tests(metadata) end end + def search_param_value_name(parameter) + parameter.gsub!(/^[\W_]+|[\W_]+$"/, '') # remove non-character elements from beginning and end of name + "#{parameter.gsub('-', '_')}_val" + end + def capitalize_first_letter(str) str.slice(0).capitalize + str.slice(1..-1) end diff --git a/generator/generic/templates/sequence_definition.rb.erb b/generator/generic/templates/sequence_definition.rb.erb index 9633dc113..ff16ff176 100644 --- a/generator/generic/templates/sequence_definition.rb.erb +++ b/generator/generic/templates/sequence_definition.rb.erb @@ -6,4 +6,4 @@ module Inferno SEARCH_PARAMETERS = <%= search_parameters %>.freeze end end -end \ No newline at end of file +end diff --git a/generator/generic_generator_utilities.rb b/generator/generic_generator_utilities.rb index b12247579..04e717a50 100644 --- a/generator/generic_generator_utilities.rb +++ b/generator/generic_generator_utilities.rb @@ -3,7 +3,6 @@ module Inferno module Generator module GenericGeneratorUtilties - EXPECTATION_URL = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation' def structure_to_string(struct) @@ -38,11 +37,13 @@ def create_search_validation(sequence_metadata) .drop(1) .join('.') path += get_value_path_by_type(type) unless ['Period', 'date', 'HumanName', 'Address', 'CodeableConcept', 'Coding', 'Identifier'].include? type + parameter_code = parameter_metadata.code + resource_type = sequence_metadata.resource_type search_validators += %( when '#{parameter_metadata.code}' values_found = resolve_path(resource, '#{path}') #{search_param_match_found_code(type, parameter_metadata.code)} - assert match_found, "#{parameter_metadata.code} in #{sequence_metadata.resource_type}/\#{resource.id} (\#{values_found}) does not match #{parameter_metadata.code} requested (\#{value})" + assert match_found, "#{parameter_code} in #{resource_type}/\#{resource.id} (\#{values_found}) does not match #{parameter_code} requested (\#{value})" ) end diff --git a/generator/search_parameter_metadata.rb b/generator/search_parameter_metadata.rb index e0fda2095..b9de28071 100644 --- a/generator/search_parameter_metadata.rb +++ b/generator/search_parameter_metadata.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require_relative './generic_generator_utilities' module Inferno diff --git a/generator/uscore/uscore_generator.rb b/generator/uscore/uscore_generator.rb index 5eacbb33f..93e8a7447 100644 --- a/generator/uscore/uscore_generator.rb +++ b/generator/uscore/uscore_generator.rb @@ -889,7 +889,7 @@ def param_value_name(param) "search_params[:#{param_key}]" end - defc(search_parameters, sequence) + def get_first_search(search_parameters, sequence) save_resource_references_arguments = [ "versioned_resource_class('#{sequence[:resource]}')", "@#{sequence[:resource].underscore}_ary#{'[patient]' unless sequence[:delayed_sequence]}", From 768698abbc4ab62fd9d485ac193f4ad3a3dbc843 Mon Sep 17 00:00:00 2001 From: Chase Zhou Date: Tue, 4 Aug 2020 16:06:22 -0400 Subject: [PATCH 06/26] more fixes --- generator/generic/profile_validation_test.rb | 2 +- generator/generic/read_test.rb | 1 + generator/generic_generator_utilities.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/generator/generic/profile_validation_test.rb b/generator/generic/profile_validation_test.rb index 7ee8e5fa7..b9ca2e5ff 100644 --- a/generator/generic/profile_validation_test.rb +++ b/generator/generic/profile_validation_test.rb @@ -13,7 +13,7 @@ def create_profile_validation_test(metadata) ) profile_validation_test.code = %( skip 'No resource found from Read test' unless @resource_found.present? - test_resource_against_profile('#{metadata.resource_type}', @resource_found, '#{metadata.url}') + test_resources_against_profile('#{metadata.resource_type}') ) metadata.add_test(profile_validation_test) end diff --git a/generator/generic/read_test.rb b/generator/generic/read_test.rb index 766fbd96d..588fb2187 100644 --- a/generator/generic/read_test.rb +++ b/generator/generic/read_test.rb @@ -14,6 +14,7 @@ def create_read_test(metadata) read_test.code = %( resource_id = @instance.#{metadata.resource_type.underscore}_id @resource_found = validate_read_reply(FHIR::#{metadata.resource_type}.new(id: resource_id), FHIR::#{metadata.resource_type}) + save_resource_references(versioned_resource_class('#{metadata.resource_type}'), [@resource_found]) ) metadata.add_test(read_test) end diff --git a/generator/generic_generator_utilities.rb b/generator/generic_generator_utilities.rb index 04e717a50..567e251f3 100644 --- a/generator/generic_generator_utilities.rb +++ b/generator/generic_generator_utilities.rb @@ -119,7 +119,7 @@ def get_value_path_by_type(type) case type when 'CodeableConcept' '.coding.code' - when 'Reference' + when 'Reference', 'reference' '.reference' when 'Period' '.start' From 66223a3951a6f6bd5bc38edeb7c05d66519ebc33 Mon Sep 17 00:00:00 2001 From: Chase Zhou Date: Tue, 4 Aug 2020 16:41:11 -0400 Subject: [PATCH 07/26] get correct type for parameters --- generator/generic_generator_utilities.rb | 2 +- generator/sequence_metadata.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/generator/generic_generator_utilities.rb b/generator/generic_generator_utilities.rb index 567e251f3..21170a862 100644 --- a/generator/generic_generator_utilities.rb +++ b/generator/generic_generator_utilities.rb @@ -30,7 +30,7 @@ def structure_to_string(struct) def create_search_validation(sequence_metadata) search_validators = '' sequence_metadata.search_parameter_metadata&.each do |parameter_metadata| - type = parameter_metadata.type + type = sequence_metadata.element_type_by_path(parameter_metadata.expression) || parameter_metadata.type path = parameter_metadata.expression .gsub(/(? Date: Tue, 4 Aug 2020 16:57:40 -0400 Subject: [PATCH 08/26] add requirements to module file for generic ui --- generator/generic/templates/module.yml.erb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/generator/generic/templates/module.yml.erb b/generator/generic/templates/module.yml.erb index 0c100a5a4..6f1c7ef08 100644 --- a/generator/generic/templates/module.yml.erb +++ b/generator/generic/templates/module.yml.erb @@ -12,3 +12,6 @@ test_sets: run_all: true sequences:<% sequences.each do |sequence| %> - <%= sequence.class_name %><% end %> +sequence_requirements:<% sequences.flat_map { |seq| seq.requirements }.uniq.map { |req| req.gsub(':', '') }.each do |requirement| %> + <%= requirement %>: + label: <%= requirement %><% end %> From 2b19c8e09987c0f1c39b330eeb5f86d96221e592 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 09:34:52 -0400 Subject: [PATCH 09/26] nil safety for multiple_and_expecation and multiple_or_expectation --- generator/search_parameter_metadata.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/generator/search_parameter_metadata.rb b/generator/search_parameter_metadata.rb index b9de28071..6ffce6f29 100644 --- a/generator/search_parameter_metadata.rb +++ b/generator/search_parameter_metadata.rb @@ -46,7 +46,9 @@ def multiple_or # expectation if multiple or is allowed - unsure if this is generic or just us core specific def multiple_or_expectation - @multiple_or_expectation ||= @search_parameter_json['_multipleOr']['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + @multiple_or_expectation ||= @search_parameter_json.dig('_multipleOr', 'extension') + &.find { |ext| ext['url'] == EXPECTATION_URL } + &.dig('valueCode') end # whether multiple and is allowed @@ -56,7 +58,9 @@ def multiple_and # expectation if multiple and is allowed - unsure if this is generic or just us core specific def multiple_and_expectation - @multiple_and_expectation ||= @search_parameter_json['_multipleAnd']['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + @multiple_and_expectation ||= @search_parameter_json.dig('_multipleAnd', 'extension') + &.find { |ext| ext['url'] == EXPECTATION_URL } + &.dig('valueCode') end def comparators From d4871705b8d6c91ab2f89d0e1cf650e363c61905 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 09:45:17 -0400 Subject: [PATCH 10/26] break out search combos and add some nil safety --- generator/sequence_metadata.rb | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 02e438556..31f6e7210 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -90,9 +90,6 @@ def searches def searches_from_capability_statement(capabilities) return [] unless capabilities.present? - - search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' - searches = [] basic_searches = capabilities['searchParam'] basic_searches&.each do |search_param| @@ -102,21 +99,26 @@ def searches_from_capability_statement(capabilities) } searches << new_search end + search_combinations = search_combinations_from_capability_statement(capabilities) + searches.append(search_combinations) unless search_combinations.nil? + searches + end + def search_combinations_from_capability_statement(capabilities) + search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' capabilities['extension'] - .select { |ext| ext['url'] == search_combo_url } - .each do |combo| - expectation = combo['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] - combo_params = combo['extension'] - .reject { |ext| ext['url'] == EXPECTATION_URL } - .map { |ext| ext['valueString'] } - new_search = { + &.select { |ext| ext['url'] == search_combo_url } + &.map do |combo| + expectation = combo['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + combo_params = combo['extension'] + .reject { |ext| ext['url'] == EXPECTATION_URL } + .map { |ext| ext['valueString'] } + new_search = { parameters: combo_params, expectation: expectation - } - searches << new_search - end - searches + } + new_search + end end def add_test(test) From 7a6efbb9d8b61d4ea7366c6c2cdee832f6371c45 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 09:56:43 -0400 Subject: [PATCH 11/26] move validation tests after read and search tests --- generator/generic/generic_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index e7102cbd7..32360fcf7 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -39,8 +39,8 @@ def generate def generate_sequences sequence_metadata.each do |metadata| create_read_test(metadata) - create_profile_validation_test(metadata) create_search_tests(metadata) + create_profile_validation_test(metadata) create_interaction_tests(metadata) generate_sequence(metadata) end From 636fc730d577e6ed0361b7cffdfa4bec4d59089f Mon Sep 17 00:00:00 2001 From: Reece Adamson <41651655+radamson@users.noreply.github.com> Date: Wed, 5 Aug 2020 12:09:44 -0400 Subject: [PATCH 12/26] Update generator/generic/generic_generator.rb Co-authored-by: Stephen MacVicar --- generator/generic/generic_generator.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 32360fcf7..df24e9851 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -58,7 +58,8 @@ def generate_sequence(metadata) end def generate_sequence_definitions(metadata) - file_name = sequence_out_path + '/profile_definitions/' + metadata.file_name + '_definitions.rb' + output_directory = File.join(sequence_out_path, 'profile_definitions') + file_name = File.join(output_directory, metadata.file_name + '_definitions.rb') template = ERB.new(File.read(File.join(__dir__, 'templates/sequence_definition.rb.erb'))) output = template.result_with_hash(sequence_definition_hash(metadata)) FileUtils.mkdir_p(sequence_out_path + '/profile_definitions/') unless File.directory?(sequence_out_path + '/profile_definitions/') From 467b335114a72d24ec3785e87cb7ebc66328b090 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 12:31:09 -0400 Subject: [PATCH 13/26] rubocop and fix spacing --- generator/sequence_metadata.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 31f6e7210..6dbd7b5c4 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -90,6 +90,7 @@ def searches def searches_from_capability_statement(capabilities) return [] unless capabilities.present? + searches = [] basic_searches = capabilities['searchParam'] basic_searches&.each do |search_param| @@ -107,17 +108,16 @@ def searches_from_capability_statement(capabilities) def search_combinations_from_capability_statement(capabilities) search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' capabilities['extension'] - &.select { |ext| ext['url'] == search_combo_url } - &.map do |combo| + &.select { |ext| ext['url'] == search_combo_url } + &.map do |combo| expectation = combo['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] combo_params = combo['extension'] - .reject { |ext| ext['url'] == EXPECTATION_URL } - .map { |ext| ext['valueString'] } - new_search = { - parameters: combo_params, - expectation: expectation + .reject { |ext| ext['url'] == EXPECTATION_URL } + .map { |ext| ext['valueString'] } + { + parameters: combo_params, + expectation: expectation } - new_search end end From e421cda029d001a87c835788f484c71870378d8d Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 13:49:22 -0400 Subject: [PATCH 14/26] use capabilities attr_accessor instead of passing it around --- generator/sequence_metadata.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 6dbd7b5c4..d64319386 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -70,10 +70,10 @@ def url end def interactions - @interactions ||= interactions_from_capability_statement(capabilities) + @interactions ||= interactions_from_capability_statement end - def interactions_from_capability_statement(capabilities) + def interactions_from_capability_statement return [] unless capabilities.present? capabilities['interaction'].map do |interaction| @@ -85,10 +85,10 @@ def interactions_from_capability_statement(capabilities) end def searches - @searches ||= searches_from_capability_statement(capabilities) + @searches ||= searches_from_capability_statement end - def searches_from_capability_statement(capabilities) + def searches_from_capability_statement return [] unless capabilities.present? searches = [] @@ -100,12 +100,12 @@ def searches_from_capability_statement(capabilities) } searches << new_search end - search_combinations = search_combinations_from_capability_statement(capabilities) + search_combinations = search_combinations_from_capability_statement searches.append(search_combinations) unless search_combinations.nil? searches end - def search_combinations_from_capability_statement(capabilities) + def search_combinations_from_capability_statement search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' capabilities['extension'] &.select { |ext| ext['url'] == search_combo_url } From e04568601a7cd9ee601ee47cb05d8f5f49aaf956 Mon Sep 17 00:00:00 2001 From: Chase Zhou Date: Wed, 5 Aug 2020 15:24:22 -0400 Subject: [PATCH 15/26] fix search combo change and change definitions module --- generator/generic/templates/sequence_definition.rb.erb | 2 +- generator/sequence_metadata.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/generator/generic/templates/sequence_definition.rb.erb b/generator/generic/templates/sequence_definition.rb.erb index ff16ff176..fba61d051 100644 --- a/generator/generic/templates/sequence_definition.rb.erb +++ b/generator/generic/templates/sequence_definition.rb.erb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Inferno - module USCore310ProfileDefinitions + module ProfileDefinitions class <%= class_name %> SEARCH_PARAMETERS = <%= search_parameters %>.freeze end diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index d64319386..b31517756 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -85,10 +85,10 @@ def interactions_from_capability_statement end def searches - @searches ||= searches_from_capability_statement + @searches ||= basic_searches_from_capability_statement + search_combinations_from_capability_statement end - def searches_from_capability_statement + def basic_searches_from_capability_statement return [] unless capabilities.present? searches = [] @@ -100,8 +100,6 @@ def searches_from_capability_statement } searches << new_search end - search_combinations = search_combinations_from_capability_statement - searches.append(search_combinations) unless search_combinations.nil? searches end From f8221c0a9ead94f874d0622ac21267b5946e64cf Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 17:15:14 -0400 Subject: [PATCH 16/26] rename generic_utilities to sequence_utilities --- generator/generic/templates/sequence.rb.erb | 4 +--- generator/sequence_metadata.rb | 2 +- generator/uscore/templates/sequence.rb.erb | 1 - lib/app/sequence_base.rb | 1 + .../app/utils/sequence_utilities.rb | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) rename generator/generic/static/generic_utilities.rb => lib/app/utils/sequence_utilities.rb (98%) diff --git a/generator/generic/templates/sequence.rb.erb b/generator/generic/templates/sequence.rb.erb index 7c2fce238..08388fa94 100644 --- a/generator/generic/templates/sequence.rb.erb +++ b/generator/generic/templates/sequence.rb.erb @@ -1,11 +1,9 @@ # frozen_string_literal: true -require_relative './generic_utilities' - module Inferno module Sequence class <%= metadata.class_name %> < SequenceBase - include Inferno::GenericUtilities + include Inferno::SequenceUtilities title '<%= metadata.title %> Tests' description 'Verify support for the server capabilities required by the <%= metadata.title %> profile.' diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index b31517756..cd32bb4b5 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -116,7 +116,7 @@ def search_combinations_from_capability_statement parameters: combo_params, expectation: expectation } - end + end || [] end def add_test(test) diff --git a/generator/uscore/templates/sequence.rb.erb b/generator/uscore/templates/sequence.rb.erb index 9ea63ef7b..6b7aaa2aa 100644 --- a/generator/uscore/templates/sequence.rb.erb +++ b/generator/uscore/templates/sequence.rb.erb @@ -1,7 +1,6 @@ # frozen_string_literal: true require_relative './data_absent_reason_checker' -require_relative './generic_utilities' module Inferno module Sequence diff --git a/lib/app/sequence_base.rb b/lib/app/sequence_base.rb index 49b3e07a6..7c90efa64 100644 --- a/lib/app/sequence_base.rb +++ b/lib/app/sequence_base.rb @@ -11,6 +11,7 @@ require_relative 'utils/terminology' require_relative 'utils/result_statuses' require_relative 'utils/search_validation' +require_relative 'utils/sequence_utilities' require_relative 'models/testing_instance' require_relative 'models/inferno_test' require_relative 'utils/hl7_validator' diff --git a/generator/generic/static/generic_utilities.rb b/lib/app/utils/sequence_utilities.rb similarity index 98% rename from generator/generic/static/generic_utilities.rb rename to lib/app/utils/sequence_utilities.rb index ff94d1144..93f1b0e2f 100644 --- a/generator/generic/static/generic_utilities.rb +++ b/lib/app/utils/sequence_utilities.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Inferno - module GenericUtilities + module SequenceUtilities def resolve_path(elements, path) elements = Array.wrap(elements) return elements if path.blank? From 755a88c504e0bdfb7400838849a1884810dc24d2 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Wed, 5 Aug 2020 17:22:02 -0400 Subject: [PATCH 17/26] use map in basic_searches_from_capability_statement --- generator/sequence_metadata.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index cd32bb4b5..6ff93051b 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -91,16 +91,12 @@ def searches def basic_searches_from_capability_statement return [] unless capabilities.present? - searches = [] - basic_searches = capabilities['searchParam'] - basic_searches&.each do |search_param| - new_search = { + capabilities['searchParam']&.map do |search_param| + { parameters: [search_param['name']], - expectation: search_param['extension'].find { |ext| ext['url'] == EXPECTATION_URL } ['valueCode'] + expectation: search_param['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] } - searches << new_search - end - searches + end || [] end def search_combinations_from_capability_statement From 31020e7101be77ba11de78b05e8ac65ddb1aeed3 Mon Sep 17 00:00:00 2001 From: Chase Date: Thu, 6 Aug 2020 12:06:20 -0400 Subject: [PATCH 18/26] catch json parsing error --- generator/generator_base.rb | 8 +++++++- generator/sequence_metadata.rb | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/generator/generator_base.rb b/generator/generator_base.rb index c895e0d58..042c74383 100644 --- a/generator/generator_base.rb +++ b/generator/generator_base.rb @@ -27,7 +27,13 @@ def load_resources # We should consider using the native Ruby models instead of JSON # There were problems with round-tripping certain SearchParameters though - new_resource_json = JSON.parse(File.read(resource)) + + begin + new_resource_json = JSON.parse(File.read(resource)) + rescue JSON::ParserError # failed to parse json + next + end + new_resource = FHIR.from_contents(File.read(resource)) next unless new_resource.present? diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 6ff93051b..ed003e546 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -25,12 +25,15 @@ class SequenceMetadata def initialize(profile, all_search_parameter_metadata, capability_statement = nil) @profile = profile @tests = [] + @search_parameter_metadata = [] return unless capability_statement.present? @capabilities = capability_statement['rest'] .find { |rest| rest['mode'] == 'server' }['resource'] .find { |resource| resource['type'] == profile['type'] } + return unless capabilities.present? + @search_parameter_metadata = capabilities['searchParam']&.map do |param| all_search_parameter_metadata.find { |param_metadata| param_metadata.url == param['definition'] } end @@ -100,6 +103,8 @@ def basic_searches_from_capability_statement end def search_combinations_from_capability_statement + return [] unless capabilities.present? + search_combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination' capabilities['extension'] &.select { |ext| ext['url'] == search_combo_url } From e9d87d494dad393119c0adf7ebefe29071ca3383 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Thu, 6 Aug 2020 14:47:14 -0400 Subject: [PATCH 19/26] update generator to test against correct profile --- generator/generic/profile_validation_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generic/profile_validation_test.rb b/generator/generic/profile_validation_test.rb index b9ca2e5ff..bad9b4b81 100644 --- a/generator/generic/profile_validation_test.rb +++ b/generator/generic/profile_validation_test.rb @@ -13,7 +13,7 @@ def create_profile_validation_test(metadata) ) profile_validation_test.code = %( skip 'No resource found from Read test' unless @resource_found.present? - test_resources_against_profile('#{metadata.resource_type}') + test_resources_against_profile('#{metadata.resource_type}','#{metadata.url}') ) metadata.add_test(profile_validation_test) end From 84fab529200a5a34518934914e6f527c184804aa Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Thu, 6 Aug 2020 15:43:51 -0400 Subject: [PATCH 20/26] save resources with profile reference --- generator/generic/read_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generic/read_test.rb b/generator/generic/read_test.rb index 588fb2187..fed59b71e 100644 --- a/generator/generic/read_test.rb +++ b/generator/generic/read_test.rb @@ -14,7 +14,7 @@ def create_read_test(metadata) read_test.code = %( resource_id = @instance.#{metadata.resource_type.underscore}_id @resource_found = validate_read_reply(FHIR::#{metadata.resource_type}.new(id: resource_id), FHIR::#{metadata.resource_type}) - save_resource_references(versioned_resource_class('#{metadata.resource_type}'), [@resource_found]) + save_resource_references(versioned_resource_class('#{metadata.resource_type}'), [@resource_found], '#{metadata.url}') ) metadata.add_test(read_test) end From 40c9969ad8fa6819827fae0a7be557d6915d45e1 Mon Sep 17 00:00:00 2001 From: Chase Zhou Date: Thu, 6 Aug 2020 16:10:37 -0400 Subject: [PATCH 21/26] us path when making sequence class name --- generator/generic/generic_generator.rb | 2 +- generator/sequence_metadata.rb | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index df24e9851..75de6dd41 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -23,7 +23,7 @@ def resource_profiles end def sequence_metadata - @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, search_parameter_metadata, capability_statement) } + @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, @path, search_parameter_metadata, capability_statement) } end def search_parameter_metadata diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index ed003e546..6e8b72a35 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -22,9 +22,10 @@ class SequenceMetadata :must_supports, :interactions - def initialize(profile, all_search_parameter_metadata, capability_statement = nil) + def initialize(profile, path, all_search_parameter_metadata, capability_statement = nil) @profile = profile @tests = [] + @path = path @search_parameter_metadata = [] return unless capability_statement.present? @@ -134,9 +135,12 @@ def element_type_by_path(path) private def initial_sequence_name - return profile['name'] unless profile['name'].include?('-') - - profile['name'].split('-').map(&:capitalize).join + delimiters = ['-', '_'] + (@path + '-' + profile['name']) + .gsub('.', '') + .split(Regexp.union(delimiters)) + .map(&:capitalize) + .join end end end From e946a8166a00bff4d8568024bb9cea7e6f7270e3 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Fri, 7 Aug 2020 11:03:38 -0400 Subject: [PATCH 22/26] rename ProfileDefinitions to include module path --- generator/generic/generic_generator.rb | 1 + generator/generic/templates/sequence_definition.rb.erb | 2 +- generator/sequence_metadata.rb | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 75de6dd41..d4ac1cb90 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -82,6 +82,7 @@ def sequence_definition_hash(metadata) end search_parameters ||= [] { + module_name: metadata.path.capitalize + 'Definitions', class_name: metadata.class_name + 'Definition', search_parameters: structure_to_string(search_parameters) } diff --git a/generator/generic/templates/sequence_definition.rb.erb b/generator/generic/templates/sequence_definition.rb.erb index fba61d051..0de34d704 100644 --- a/generator/generic/templates/sequence_definition.rb.erb +++ b/generator/generic/templates/sequence_definition.rb.erb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Inferno - module ProfileDefinitions + module <%= module_name %> class <%= class_name %> SEARCH_PARAMETERS = <%= search_parameters %>.freeze end diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 6e8b72a35..e9068bc6d 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -10,7 +10,8 @@ class SequenceMetadata attr_reader :profile, :tests, :capabilities, - :search_parameter_metadata + :search_parameter_metadata, + :path attr_writer :class_name, :file_name, :requirements, From ff99f473f1dfb0ff7f310fccec14c3b1b50ed868 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Fri, 7 Aug 2020 11:06:16 -0400 Subject: [PATCH 23/26] Definitions -> ProfileDefinitions to match what previously existed --- generator/generic/generic_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index d4ac1cb90..92fec7a12 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -82,7 +82,7 @@ def sequence_definition_hash(metadata) end search_parameters ||= [] { - module_name: metadata.path.capitalize + 'Definitions', + module_name: metadata.path.capitalize + 'ProfileDefinitions', class_name: metadata.class_name + 'Definition', search_parameters: structure_to_string(search_parameters) } From f3240fc7f366b00ae265ee203af1a7ca58c2b7f2 Mon Sep 17 00:00:00 2001 From: Reece Adamson Date: Fri, 7 Aug 2020 11:47:38 -0400 Subject: [PATCH 24/26] fix indentation --- generator/sequence_metadata.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index e9068bc6d..71b89b6c9 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -111,14 +111,16 @@ def search_combinations_from_capability_statement capabilities['extension'] &.select { |ext| ext['url'] == search_combo_url } &.map do |combo| - expectation = combo['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] - combo_params = combo['extension'] - .reject { |ext| ext['url'] == EXPECTATION_URL } - .map { |ext| ext['valueString'] } - { - parameters: combo_params, - expectation: expectation - } + # rubocop:disable Layout/IndentationWidth, Layout/CommentIndentation: + expectation = combo['extension'].find { |ext| ext['url'] == EXPECTATION_URL }['valueCode'] + combo_params = combo['extension'] + .reject { |ext| ext['url'] == EXPECTATION_URL } + .map { |ext| ext['valueString'] } + { + parameters: combo_params, + expectation: expectation + } + # rubocop:enable Layout/IndentationWidth, Layout/CommentIndentation: end || [] end From 1c4f53831632f6a94d284c96785b9bd6d355bf0b Mon Sep 17 00:00:00 2001 From: Chase Date: Fri, 7 Aug 2020 13:40:28 -0400 Subject: [PATCH 25/26] add logger line for json parsing error and fix profiledefinition module name --- generator/generator_base.rb | 3 ++- generator/generic/generic_generator.rb | 11 +++++++++-- generator/sequence_metadata.rb | 11 +++++------ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/generator/generator_base.rb b/generator/generator_base.rb index 042c74383..e8e808246 100644 --- a/generator/generator_base.rb +++ b/generator/generator_base.rb @@ -30,7 +30,8 @@ def load_resources begin new_resource_json = JSON.parse(File.read(resource)) - rescue JSON::ParserError # failed to parse json + rescue JSON::ParserError + Inferno.logger.debug("Failed to parse JSON: #{resource}") next end diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 92fec7a12..70787dacc 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -23,7 +23,7 @@ def resource_profiles end def sequence_metadata - @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, @path, search_parameter_metadata, capability_statement) } + @sequence_metadata ||= resource_profiles.map { |profile| SequenceMetadata.new(profile, module_name, search_parameter_metadata, capability_statement) } end def search_parameter_metadata @@ -82,12 +82,19 @@ def sequence_definition_hash(metadata) end search_parameters ||= [] { - module_name: metadata.path.capitalize + 'ProfileDefinitions', + module_name: module_name + 'ProfileDefinitions', class_name: metadata.class_name + 'Definition', search_parameters: structure_to_string(search_parameters) } end + def module_name + delimiters = ['-', '_','.'] + @path.split(Regexp.union(delimiters)) + .map(&:capitalize) + .join + end + def module_file_path "#{module_yml_out_path}/#{@path}_module.yml" end diff --git a/generator/sequence_metadata.rb b/generator/sequence_metadata.rb index 71b89b6c9..f26cfe0a8 100644 --- a/generator/sequence_metadata.rb +++ b/generator/sequence_metadata.rb @@ -11,7 +11,7 @@ class SequenceMetadata :tests, :capabilities, :search_parameter_metadata, - :path + :module_name attr_writer :class_name, :file_name, :requirements, @@ -23,10 +23,10 @@ class SequenceMetadata :must_supports, :interactions - def initialize(profile, path, all_search_parameter_metadata, capability_statement = nil) + def initialize(profile, module_name, all_search_parameter_metadata, capability_statement = nil) @profile = profile @tests = [] - @path = path + @module_name = module_name @search_parameter_metadata = [] return unless capability_statement.present? @@ -138,9 +138,8 @@ def element_type_by_path(path) private def initial_sequence_name - delimiters = ['-', '_'] - (@path + '-' + profile['name']) - .gsub('.', '') + delimiters = ['-', '_', '.'] + (@module_name + '.' + profile['name']) .split(Regexp.union(delimiters)) .map(&:capitalize) .join From 467895fe2dd79e69c214aea314c5f2ca43c220d7 Mon Sep 17 00:00:00 2001 From: Chase Date: Fri, 7 Aug 2020 13:59:01 -0400 Subject: [PATCH 26/26] fix rubocop spacing issue --- generator/generic/generic_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generic/generic_generator.rb b/generator/generic/generic_generator.rb index 70787dacc..86b472d96 100644 --- a/generator/generic/generic_generator.rb +++ b/generator/generic/generic_generator.rb @@ -89,7 +89,7 @@ def sequence_definition_hash(metadata) end def module_name - delimiters = ['-', '_','.'] + delimiters = ['-', '_', '.'] @path.split(Regexp.union(delimiters)) .map(&:capitalize) .join