From d6e45e562d592eae41d4411a6fa788bcc2b92c5d Mon Sep 17 00:00:00 2001 From: Yuta Iwama Date: Wed, 16 Feb 2022 13:03:49 +0900 Subject: [PATCH] Cache schema It reduces a lot of duplicated object creation when you want to apply the same schema but different data ``` schema = .... validator = JSON::Validator.new(schema) validator.validate(data1) validator.validate(data2) validator.validate(data3) ``` --- lib/json-schema/validator.rb | 22 ++-- test/full_validation_test.rb | 35 ++++++ test/initialize_data_test.rb | 223 +++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+), 10 deletions(-) diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index c13db4ee..e365a9b3 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -37,7 +37,7 @@ class Validator @@serializer = nil @@mutex = Mutex.new - def initialize(schema_data, data, opts={}) + def initialize(schema_data, opts={}) @options = @@default_opts.clone.merge(opts) @errors = [] @@ -50,15 +50,13 @@ def initialize(schema_data, data, opts={}) @validation_options[:clear_cache] = true if !@@cache_schemas || @options[:clear_cache] @@mutex.synchronize { @base_schema = initialize_schema(schema_data, configured_validator) } - @original_data = data - @data = initialize_data(data) @@mutex.synchronize { build_schemas(@base_schema) } # validate the schema, if requested if @options[:validate_schema] # Don't clear the cache during metaschema validation! - meta_validator = self.class.new(@base_schema.validator.metaschema, @base_schema.schema, {:clear_cache => false}) - meta_validator.validate + meta_validator = self.class.new(@base_schema.validator.metaschema, {:clear_cache => false}) + meta_validator.validate(@base_schema.schema) end # If the :fragment option is set, try and validate against the fragment @@ -102,8 +100,10 @@ def schema_from_fragment(base_schema, fragment) end # Run a simple true/false validation of data against a schema - def validate - @base_schema.validate(@data,[],self,@validation_options) + def validate(data) + original_data = data + data = initialize_data(data) + @base_schema.validate(data,[],self,@validation_options) if @options[:record_errors] if @options[:errors_as_objects] @@ -115,11 +115,13 @@ def validate true end ensure + @errors = [] + if @validation_options[:clear_cache] == true self.class.clear_cache end if @validation_options[:insert_defaults] - self.class.merge_missing_values(@data, @original_data) + self.class.merge_missing_values(data, original_data) end end @@ -243,8 +245,8 @@ def validate_uri(schema, data, opts={}) end def validate!(schema, data,opts={}) - validator = new(schema, data, opts) - validator.validate + validator = new(schema, opts) + validator.validate(data) end def validate2(schema, data, opts={}) diff --git a/test/full_validation_test.rb b/test/full_validation_test.rb index eb0fb8fe..3eca142c 100644 --- a/test/full_validation_test.rb +++ b/test/full_validation_test.rb @@ -33,6 +33,41 @@ def test_full_validation assert(errors.length == 2) end + def test_full_validation_with_instantiated_validator + data = {"b" => {"a" => 5}} + schema = { + "type" => "object", + "required" => ["b"], + "properties" => { + "b" => { + } + } + } + + validator = JSON::Validator.new(schema, { record_errors: true }) + assert(validator.validate(data).empty?) + assert(validator.validate(data).empty?) + assert(validator.validate(data).empty?) + + data = {"c" => 5} + schema = { + "type" => "object", + "required" => ["b"], + "properties" => { + "b" => { + }, + "c" => { + "type" => "string" + } + } + } + + validator = JSON::Validator.new(schema, { record_errors: true }) + assert(validator.validate(data).length == 2) + assert(validator.validate(data).length == 2) + assert(validator.validate(data).length == 2) + end + def test_full_validation_with_union_types data = {"b" => 5} schema = { diff --git a/test/initialize_data_test.rb b/test/initialize_data_test.rb index cf08cbe1..7a11881d 100644 --- a/test/initialize_data_test.rb +++ b/test/initialize_data_test.rb @@ -151,4 +151,227 @@ def test_parse_hash assert_raises(TypeError) { JSON::Validator.validate(schema, data, :uri => true) } end + + def test_parse_character_string_with_instantiated_validator + schema = {'type' => 'string'} + data = 'hello world' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + end + + def test_parse_integer_string_with_instantiated_validator + schema = {'type' => 'integer'} + data = '42' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert_raises(JSON::Schema::ValidationError) { v.validate(data) } + assert_raises(JSON::Schema::ValidationError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :json => true }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + end + + def test_parse_hash_string_with_instantiated_validator + schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } } + data = '{"a": "b"}' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert_raises(JSON::Schema::ValidationError) { v.validate(data) } + assert_raises(JSON::Schema::ValidationError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :json => true }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::UriError) { v.validate(data) } + assert_raises(JSON::Schema::UriError) { v.validate(data) } + end + + def test_parse_json_string_with_instantiated_validator + schema = {'type' => 'string'} + data = '"hello world"' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + end + + def test_parse_plain_text_string_with_instantiated_validator + schema = {'type' => 'string'} + data = 'kapow' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + end + + def test_parse_valid_uri_string_with_instantiated_validator + schema = {'type' => 'string'} + data = 'http://foo.bar/' + + stub_request(:get, "foo.bar").to_return(:body => '"hello world"', :status => 200) + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert(v.validate(data)) + assert(v.validate(data)) + end + + def test_parse_invalid_uri_string_with_instantiated_validator + schema = {'type' => 'string'} + data = 'http://foo.bar/' + + stub_request(:get, "foo.bar").to_timeout + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + stub_request(:get, "foo.bar").to_return(:status => [500, "Internal Server Error"]) + + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) } + end + + def test_parse_invalid_scheme_string_with_instantiated_validator + schema = {'type' => 'string'} + data = 'pick one: [1, 2, 3]' + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + assert_raises(JSON::Schema::JsonParseError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(JSON::Schema::UriError) { v.validate(data) } + assert_raises(JSON::Schema::UriError) { v.validate(data) } + end + + def test_parse_integer_with_instantiated_validator + schema = {'type' => 'integer'} + data = 42 + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(TypeError) { v.validate(data) } + assert_raises(TypeError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(TypeError) { v.validate(data) } + assert_raises(TypeError) { v.validate(data) } + end + + def test_parse_hash_with_instantiated_validator + schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } } + data = { 'a' => 'b' } + + v = JSON::Validator.new(schema) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :parse_data => false }) + assert(v.validate(data)) + assert(v.validate(data)) + + v = JSON::Validator.new(schema, { :json => true }) + assert_raises(TypeError) { v.validate(data) } + assert_raises(TypeError) { v.validate(data) } + + v = JSON::Validator.new(schema, { :uri => true }) + assert_raises(TypeError) { v.validate(data) } + assert_raises(TypeError) { v.validate(data) } + end end