From e739c5804dc7be6322d61ae62751d700056b9c39 Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:06:24 +0530 Subject: [PATCH 1/7] Fix for issue #472 --- lib/json-schema/attribute.rb | 3 ++- lib/json-schema/attributes/type.rb | 4 +++- lib/json-schema/attributes/type_v4.rb | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/json-schema/attribute.rb b/lib/json-schema/attribute.rb index 31eb98f7..b7dcb04a 100644 --- a/lib/json-schema/attribute.rb +++ b/lib/json-schema/attribute.rb @@ -33,8 +33,9 @@ def self.validation_errors(validator) 'any' => Object, } - def self.data_valid_for_type?(data, type) + def self.data_valid_for_type?(data, type, nullable=false) valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true } + valid_classes = [valid_classes, NilClass] if nullable Array(valid_classes).any? { |c| data.is_a?(c) } end diff --git a/lib/json-schema/attributes/type.rb b/lib/json-schema/attributes/type.rb index 149ac6af..2a418fba 100644 --- a/lib/json-schema/attributes/type.rb +++ b/lib/json-schema/attributes/type.rb @@ -5,10 +5,12 @@ class Schema class TypeAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) union = true + nullable = false if options[:disallow] types = current_schema.schema['disallow'] else types = current_schema.schema['type'] + nullable = current_schema.schema['nullable'] end if !types.is_a?(Array) @@ -22,7 +24,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options types.each_with_index do |type, type_index| if type.is_a?(String) - valid = data_valid_for_type?(data, type) + valid = data_valid_for_type?(data, type, nullable) elsif type.is_a?(Hash) && union # Validate as a schema schema = JSON::Schema.new(type, current_schema.uri, validator) diff --git a/lib/json-schema/attributes/type_v4.rb b/lib/json-schema/attributes/type_v4.rb index f5d5a3a7..f05dd041 100644 --- a/lib/json-schema/attributes/type_v4.rb +++ b/lib/json-schema/attributes/type_v4.rb @@ -6,12 +6,13 @@ class TypeV4Attribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) union = true types = current_schema.schema['type'] + nullable = current_schema.schema['nullable'] if !types.is_a?(Array) types = [types] union = false end - return if types.any? { |type| data_valid_for_type?(data, type) } + return if types.any? { |type| data_valid_for_type?(data, type, nullable) } types = types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ') message = format( From cce630c4197e59862d3804a9536d047420d4635d Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:09:48 +0530 Subject: [PATCH 2/7] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 726a5b83..2dd7a669 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,18 @@ JSON::Validator.validate(schema, { "a" => 1 }, :parse_data => false) # => false JSON::Validator.validate(schema, '{ "a": 1 }', :parse_data => false) +# +# with the `:nullable` option set to true, any key with null value will be evaluated to true +# +schema = { + "type": "object", + "properties": { + "github_url": { "type": "string", "nullable": true } + } +} +# => true +JSON::Validator.validate!(schema, { "github_url" => null }) + # # with the `:parse_integer` option set to false, the integer value given as string will not be parsed. # From ba5c5b548202414dabc3a650f33dcdc98c01d85a Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:37:59 +0530 Subject: [PATCH 3/7] Test cases --- lib/json-schema/attribute.rb | 2 +- test/initialize_data_test.rb | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/json-schema/attribute.rb b/lib/json-schema/attribute.rb index b7dcb04a..03fb10e0 100644 --- a/lib/json-schema/attribute.rb +++ b/lib/json-schema/attribute.rb @@ -33,7 +33,7 @@ def self.validation_errors(validator) 'any' => Object, } - def self.data_valid_for_type?(data, type, nullable=false) + def self.data_valid_for_type?(data, type, nullable: false) valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true } valid_classes = [valid_classes, NilClass] if nullable Array(valid_classes).any? { |c| data.is_a?(c) } diff --git a/test/initialize_data_test.rb b/test/initialize_data_test.rb index 4b6c9635..f5b8efde 100644 --- a/test/initialize_data_test.rb +++ b/test/initialize_data_test.rb @@ -373,4 +373,26 @@ def test_parse_hash_with_instantiated_validator assert_raises(TypeError) { v.validate(data) } assert_raises(TypeError) { v.validate(data) } end + + def test_nullable_param + schema = { + "type": "object", + "properties": { + "github_url": { "type": "string", "nullable": true } + } + } + data = { "github_url" => nil } + + assert(JSON::Validator.validate(schema, data)) + + schema = { + "type": "object", + "properties": { + "github_url": { "type": "string"} + } + } + refute(JSON::Validator.validate(schema, data)) + + end + end From 00aa6d80f608fad7427d2e73b80b53a3d351a2af Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:41:23 +0530 Subject: [PATCH 4/7] Fixing testcase error --- test/initialize_data_test.rb | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/test/initialize_data_test.rb b/test/initialize_data_test.rb index d392252d..46d4f49f 100644 --- a/test/initialize_data_test.rb +++ b/test/initialize_data_test.rb @@ -381,24 +381,14 @@ def test_parse_hash_with_instantiated_validator end def test_nullable_param - schema = { - "type": "object", - "properties": { - "github_url": { "type": "string", "nullable": true } - } - } - data = { "github_url" => nil } - + schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string', 'nullable' => true } } } + data = { 'a' => nil } + assert(JSON::Validator.validate(schema, data)) - schema = { - "type": "object", - "properties": { - "github_url": { "type": "string"} - } - } + schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } } + refute(JSON::Validator.validate(schema, data)) - end end From f1c40b8b19b51ce0908c919132e45cf0f4ea0b73 Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:47:00 +0530 Subject: [PATCH 5/7] Rubocop fix --- lib/json-schema/attributes/type.rb | 2 +- lib/json-schema/attributes/type_v4.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/json-schema/attributes/type.rb b/lib/json-schema/attributes/type.rb index 2a418fba..76792408 100644 --- a/lib/json-schema/attributes/type.rb +++ b/lib/json-schema/attributes/type.rb @@ -24,7 +24,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options types.each_with_index do |type, type_index| if type.is_a?(String) - valid = data_valid_for_type?(data, type, nullable) + valid = data_valid_for_type?(data, type, nullable: nullable) elsif type.is_a?(Hash) && union # Validate as a schema schema = JSON::Schema.new(type, current_schema.uri, validator) diff --git a/lib/json-schema/attributes/type_v4.rb b/lib/json-schema/attributes/type_v4.rb index f05dd041..cc85186d 100644 --- a/lib/json-schema/attributes/type_v4.rb +++ b/lib/json-schema/attributes/type_v4.rb @@ -12,7 +12,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options union = false end - return if types.any? { |type| data_valid_for_type?(data, type, nullable) } + return if types.any? { |type| data_valid_for_type?(data, type, nullable: nullable) } types = types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ') message = format( From 290af5540139ca9f442acad8768725b59bad8ce6 Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Mon, 24 Jul 2023 13:50:45 +0530 Subject: [PATCH 6/7] Rubocop fix --- test/initialize_data_test.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/initialize_data_test.rb b/test/initialize_data_test.rb index 46d4f49f..ba1662bf 100644 --- a/test/initialize_data_test.rb +++ b/test/initialize_data_test.rb @@ -383,12 +383,11 @@ def test_parse_hash_with_instantiated_validator def test_nullable_param schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string', 'nullable' => true } } } data = { 'a' => nil } - + assert(JSON::Validator.validate(schema, data)) schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } } - + refute(JSON::Validator.validate(schema, data)) end - end From 8e565e9a93604da1fcc9e583577abc74908acfab Mon Sep 17 00:00:00 2001 From: Jahir Husain Date: Wed, 20 Sep 2023 20:31:09 +0530 Subject: [PATCH 7/7] Fix for flatten cases --- lib/json-schema/attribute.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json-schema/attribute.rb b/lib/json-schema/attribute.rb index 03fb10e0..29e1e572 100644 --- a/lib/json-schema/attribute.rb +++ b/lib/json-schema/attribute.rb @@ -36,6 +36,7 @@ def self.validation_errors(validator) def self.data_valid_for_type?(data, type, nullable: false) valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true } valid_classes = [valid_classes, NilClass] if nullable + valid_classes.flatten! if valid_classes.is_a?(Array) Array(valid_classes).any? { |c| data.is_a?(c) } end