diff --git a/Gemfile b/Gemfile index 344fb95..c01fbf9 100644 --- a/Gemfile +++ b/Gemfile @@ -12,4 +12,5 @@ group :development do gem 'rubocop-performance', '~> 1.17' gem 'rubocop-rake', '~> 0.6' gem 'rubocop-rspec', '~> 2.10' + gem 'wasabi', '~> 4.0' end diff --git a/lib/xsd/base_object.rb b/lib/xsd/base_object.rb index 502e94b..72e8e48 100644 --- a/lib/xsd/base_object.rb +++ b/lib/xsd/base_object.rb @@ -66,16 +66,16 @@ def nodes(name = :*) node.xpath("./xs:#{name}", { 'xs' => XML_SCHEMA }) end - # Get schema by namespace or namespace prefix - # @param [String, nil] namespace + # Get schemas by namespace or prefix + # @param [String, nil] ns_or_prefix # @return Array - def schemas_for_namespace(namespace) - if schema.targets_namespace?(namespace) + def schemas_for_namespace(ns_or_prefix) + if schema.targets_namespace?(ns_or_prefix) [schema, *schema.includes.map(&:imported_schema)] - elsif (import = schema.import_by_namespace(namespace)) + elsif (import = schema.import_by_namespace(ns_or_prefix)) [import.imported_schema] else - raise Error, "Schema not found for namespace '#{namespace}' in '#{schema.id || schema.target_namespace}'" + raise Error, "Schema not found for namespace '#{ns_or_prefix}' in '#{schema.id || schema.target_namespace}'" end end diff --git a/lib/xsd/objects/schema.rb b/lib/xsd/objects/schema.rb index 91a541c..0bde60d 100644 --- a/lib/xsd/objects/schema.rb +++ b/lib/xsd/objects/schema.rb @@ -154,10 +154,15 @@ def import_map_children(name, cache) end.compact.flatten end - # Get import by namespace + # Get import by namespace or prefix + # @param [String, nil] ns_or_prefix # @return Import - def import_by_namespace(ns) - aliases = [ns, namespaces["xmlns:#{(ns || '').gsub(/^xmlns:/, '')}"], reader.namespace_prefixes[ns]].compact + def import_by_namespace(ns_or_prefix) + aliases = [ + ns_or_prefix, + namespaces["xmlns:#{(ns_or_prefix || '').gsub(/^xmlns:/, '')}"], + ].compact + imports.find { |import| aliases.include?(import.namespace) } end @@ -224,12 +229,25 @@ def schema_validator # @param [Set] processed def recursive_import_xsd(schema, file, processed, &block) # handle recursion - namespace = schema.target_namespace - return if processed.include?(namespace) + return if processed.include?(schema.target_namespace) + + processed.add(schema.target_namespace) + + # prepare schema XML with all namespaces included, clone node to avoid mutating original schema + node = schema.node + if node.namespaces.size != node.namespace_definitions.size + prefixes = node.namespace_definitions.map(&:prefix) + node = schema.node.dup - processed.add(namespace) + schema.node.namespaces.each do |attr, ns| + prefix = attr == 'xmlns' ? nil : attr.sub('xmlns:', '') + # does not work! + # node.add_namespace_definition(p, ns) unless prefixes.include?(prefix) + node[prefix] = ns unless prefixes.include?(prefix) + end + end - data = schema.node.to_xml + data = node.to_xml schema.imports.each do |import| name = "#{::SecureRandom.urlsafe_base64}.xsd" diff --git a/lib/xsd/xml.rb b/lib/xsd/xml.rb index dde118b..1a14e06 100644 --- a/lib/xsd/xml.rb +++ b/lib/xsd/xml.rb @@ -7,7 +7,7 @@ module XSD class XML include Generator - attr_reader :options, :object_cache, :schemas, :namespace_prefixes + attr_reader :options, :object_cache, :schemas DEFAULT_RESOURCE_RESOLVER = proc do |location, namespace| if location =~ /^https?:/ @@ -74,10 +74,9 @@ def self.open(path, **options) end def initialize(**options) - @options = options - @object_cache = {} - @schemas = [] - @namespace_prefixes = {} + @options = options + @object_cache = {} + @schemas = [] end def logger @@ -115,24 +114,16 @@ def add_schema_node(node) new_schema end - # Add prefixes defined outside of processed schemas, for example in WSDL document - # @param [String] prefix - # @param [String] namespace - def add_namespace_prefix(prefix, namespace) - @namespace_prefixes[prefix] = namespace - end - # Get first added (considered primary) schema # @return Schema, nil def schema schemas.first end - # Get schema by namespace or namespace prefix + # Get schemas by namespace # @param [String, nil] namespace # @return Array def schemas_for_namespace(namespace) - namespace = namespace_prefixes[namespace] if namespace_prefixes.key?(namespace) schemas.select { |schema| schema.target_namespace == namespace } end diff --git a/spec/fixtures/mvd/service.wsdl b/spec/fixtures/mvd/service.wsdl new file mode 100644 index 0000000..9562037 --- /dev/null +++ b/spec/fixtures/mvd/service.wsdl @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c47a54f..89c1684 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'xsd' +require 'wasabi' require_relative 'support/helper_methods' diff --git a/spec/xsd/schema_spec.rb b/spec/xsd/schema_spec.rb index adad1be..5c49d8d 100644 --- a/spec/xsd/schema_spec.rb +++ b/spec/xsd/schema_spec.rb @@ -103,4 +103,18 @@ end end end + + context 'with mvd example files' do + describe '#validate' do + + it 'validates schema from WSDL document' do + wsdl = Wasabi.document(fixture_file(%w[mvd service.wsdl])) + + xsd = XSD::XML.new(logger: spec_logger) + xsd.add_schema_node(wsdl.parser.schemas.first) + + expect { xsd.schema.validate }.not_to raise_error + end + end + end end