From eccf80f492462b34afd8ddd6b7e0509231d069b7 Mon Sep 17 00:00:00 2001 From: Iain Beeston Date: Thu, 30 Jun 2016 13:26:31 +0100 Subject: [PATCH] Moved Validator#absolutized_uri and RefAttribute's ref parsing into the URI module I just noticed that we have this URI processing method in Validator, and similar code in RefAttribute. I had thought we'd moved all of these into the URI module, but these must have been missed. I've taken the opportunity to tidy them up slightly too. One day we might be able to unify these methods --- CHANGELOG.md | 2 + lib/json-schema/attributes/ref.rb | 18 +-- lib/json-schema/util/uri.rb | 37 ++++++ lib/json-schema/validator.rb | 13 +- test/uri_parsing_test.rb | 113 ----------------- test/uri_util_test.rb | 196 ++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 142 deletions(-) create mode 100644 test/uri_util_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index c0aabc30..ab330ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Made the `:clear_cache` option for `validate` also clear the URI parse cache +- Moved `JSON::Validator.absolutize_ref` and the ref manipulating code in + `JSON::Schema::RefAttribute` into `JSON::Util::URI` ## [2.7.0] - 2016-09-29 diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index 2db8b2f5..9c1f0be5 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -22,23 +22,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options def self.get_referenced_uri_and_schema(s, current_schema, validator) uri,schema = nil,nil - temp_uri = JSON::Util::URI.parse(s['$ref']) - temp_uri.defer_validation do - if temp_uri.relative? - temp_uri.merge!(current_schema.uri) - # Check for absolute path - path, fragment = s['$ref'].split("#") - if path.nil? || path == '' - temp_uri.path = current_schema.uri.path - elsif path[0,1] == "/" - temp_uri.path = Pathname.new(path).cleanpath.to_s - else - temp_uri.join!(path) - end - temp_uri.fragment = fragment - end - temp_uri.fragment = "" if temp_uri.fragment.nil? || temp_uri.fragment.empty? - end + temp_uri = JSON::Util::URI.normalize_ref(s['$ref'], current_schema.uri) # Grab the parent schema from the schema list schema_key = temp_uri.to_s.split("#")[0] + "#" diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 86e56bf1..9f7a3104 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -23,6 +23,43 @@ def self.normalized_uri(uri, base_path = Dir.pwd) normalized_uri end + def self.absolutize_ref(ref, base) + ref_uri = strip_fragment(ref.dup) + + return ref_uri if ref_uri.absolute? + return parse(base) if ref_uri.path.empty? + + uri = strip_fragment(base.dup).join(ref_uri.path) + normalized_uri(uri) + end + + def self.normalize_ref(ref, base) + ref_uri = parse(ref) + base_uri = parse(base) + + ref_uri.defer_validation do + if ref_uri.relative? + ref_uri.merge!(base_uri) + + # Check for absolute path + path, fragment = ref.to_s.split("#") + if path.nil? || path == '' + ref_uri.path = base_uri.path + elsif path[0,1] == "/" + ref_uri.path = Pathname.new(path).cleanpath.to_s + else + ref_uri.join!(path) + end + + ref_uri.fragment = fragment + end + + ref_uri.fragment = "" if ref_uri.fragment.nil? || ref_uri.fragment.empty? + end + + ref_uri + end + def self.parse(uri) if uri.is_a?(Addressable::URI) return uri.dup diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index cb3edbb6..c8298a9d 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -131,7 +131,7 @@ def validate end def load_ref_schema(parent_schema, ref) - schema_uri = absolutize_ref_uri(ref, parent_schema.uri) + schema_uri = JSON::Util::URI.absolutize_ref(ref, parent_schema.uri) return true if self.class.schema_loaded?(schema_uri) schema = @options[:schema_reader].read(schema_uri) @@ -139,17 +139,6 @@ def load_ref_schema(parent_schema, ref) build_schemas(schema) end - def absolutize_ref_uri(ref, parent_schema_uri) - ref_uri = JSON::Util::URI.strip_fragment(ref) - - return ref_uri if ref_uri.absolute? - # This is a self reference and thus the schema does not need to be re-loaded - return parent_schema_uri if ref_uri.path.empty? - - uri = JSON::Util::URI.strip_fragment(parent_schema_uri.dup) - Util::URI.normalized_uri(uri.join(ref_uri.path)) - end - # Build all schemas with IDs, mapping out the namespace def build_schemas(parent_schema) schema = parent_schema.schema diff --git a/test/uri_parsing_test.rb b/test/uri_parsing_test.rb index 8c6cbc5e..cecd8f97 100644 --- a/test/uri_parsing_test.rb +++ b/test/uri_parsing_test.rb @@ -2,16 +2,6 @@ require File.expand_path('../support/test_helper', __FILE__) class UriParsingTest < Minitest::Test - def populate_cache_with(str, &blk) - cached_uri = Addressable::URI.parse(str) - Addressable::URI.stub(:parse, cached_uri, &blk) - cached_uri - end - - def teardown - JSON::Util::URI.clear_cache - end - def test_asian_characters schema = { "$schema"=> "http://json-schema.org/draft-04/schema#", @@ -74,107 +64,4 @@ def test_schema_from_file_with_spaces schema = "test/schemas/ref john with spaces schema.json" assert_valid schema, data end - - def test_normalized_uri - str = "https://www.google.com/search" - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search') - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_with_empty_fragment - str = "https://www.google.com/search#" - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: nil) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_with_fragment - str = "https://www.google.com/search#foo" - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: 'foo') - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_for_absolute_path - str = "/foo/bar.json" - uri = Addressable::URI.new(scheme: 'file', - path: '///foo/bar.json') - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_for_relartive_path - str = "foo/bar.json" - uri = Addressable::URI.new(scheme: 'file', - path: '///home/foo/bar.json') - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_uri_parse - str = "https://www.google.com/search" - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search') - assert_equal uri, JSON::Util::URI.parse(str) - end - - def test_invalid_uri_parse - uri = ":::::::" - assert_raises(JSON::Schema::UriError) do - JSON::Util::URI.parse(uri) - end - end - - def test_normalization_cache - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.normalized_uri('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - - JSON::Util::URI.clear_cache - - refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - end - - def test_parse_cache - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.parse('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.parse('foo')) - - JSON::Util::URI.clear_cache - - refute_equal(cached_uri, JSON::Util::URI.parse('foo')) - end - - def test_validator_clear_cache_for_normalized_uri - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.normalized_uri('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - - validation_errors({"type" => "string"}, "foo", :clear_cache => true) - - refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - end - - def test_validator_clear_cache_for_parse - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.parse('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.parse('foo')) - - validation_errors({"type" => "string"}, "foo", :clear_cache => true) - - refute_equal(cached_uri, JSON::Util::URI.parse('foo')) - end end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb new file mode 100644 index 00000000..af0a8059 --- /dev/null +++ b/test/uri_util_test.rb @@ -0,0 +1,196 @@ +require File.expand_path('../support/test_helper', __FILE__) + +class UriUtilTest < Minitest::Test + def populate_cache_with(str, &blk) + cached_uri = Addressable::URI.parse(str) + Addressable::URI.stub(:parse, cached_uri, &blk) + cached_uri + end + + def teardown + JSON::Util::URI.clear_cache + end + + def test_normalized_uri + str = "https://www.google.com/search" + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') + end + + def test_normalized_uri_with_empty_fragment + str = "https://www.google.com/search#" + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: nil) + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') + end + + def test_normalized_uri_with_fragment + str = "https://www.google.com/search#foo" + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: 'foo') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') + end + + def test_normalized_uri_for_absolute_path + str = "/foo/bar.json" + uri = Addressable::URI.new(scheme: 'file', + path: '///foo/bar.json') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') + end + + def test_normalized_uri_for_relartive_path + str = "foo/bar.json" + uri = Addressable::URI.new(scheme: 'file', + path: '///home/foo/bar.json') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') + end + + def test_uri_parse + str = "https://www.google.com/search" + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search') + assert_equal uri, JSON::Util::URI.parse(str) + end + + def test_invalid_uri_parse + uri = ":::::::" + assert_raises(JSON::Schema::UriError) do + JSON::Util::URI.parse(uri) + end + end + + def test_normalization_cache + cached_uri = populate_cache_with('www.google.com') do + JSON::Util::URI.normalized_uri('foo') + end + + assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) + + JSON::Util::URI.clear_cache + + refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) + end + + def test_parse_cache + cached_uri = populate_cache_with('www.google.com') do + JSON::Util::URI.parse('foo') + end + + assert_equal(cached_uri, JSON::Util::URI.parse('foo')) + + JSON::Util::URI.clear_cache + + refute_equal(cached_uri, JSON::Util::URI.parse('foo')) + end + + def test_validator_clear_cache_for_normalized_uri + cached_uri = populate_cache_with('www.google.com') do + JSON::Util::URI.normalized_uri('foo') + end + + assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) + + validation_errors({"type" => "string"}, "foo", :clear_cache => true) + + refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) + end + + def test_validator_clear_cache_for_parse + cached_uri = populate_cache_with('www.google.com') do + JSON::Util::URI.parse('foo') + end + + assert_equal(cached_uri, JSON::Util::URI.parse('foo')) + + validation_errors({"type" => "string"}, "foo", :clear_cache => true) + + refute_equal(cached_uri, JSON::Util::URI.parse('foo')) + end + + def test_ref_fragment_path + uri = '#some-thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_file_path + uri = '/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri + uri = 'http://foo-bar.com' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_path + uri = 'http://foo-bar.com/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment_and_base_with_no_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_relative_path + uri = 'hello/world' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host + uri = Addressable::URI.new(:host => 'foo-bar.com') + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/#world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host_and_path + uri = Addressable::URI.new(:host => 'foo-bar.com', :path => '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_shceme_host_and_path + uri = Addressable::URI.new(:scheme => 'https', :host => 'foo-bar.com', :path => '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end +end