Validation of required in nested object
panSarin opened this issue · 11 comments
Hello . I wonder if I do something bad, or that is #TODO
Basically i have 2 definitons
1st is the definition of response that will return Hash in JSON
2nd is the defintion of elements in Hash - that definition containst required section that define which fields in each object in hash should be displayed there. And basically if i pass my whole schema to compare it with whole response from API that validation of required won't work. I debugged json_schema gem a little , and checked that schema object in
def validate_data(schema, data, errors, path)
schema.required # => nil.
end
So do i do something wrong in my .json schema , or maybe i can;'t use validation on such lvl, and i have to use it like
schema.definitions[nested_object].validate!(response['single_object'].with_indifferent_access)
Hi @panSarin!
The required
field should work on any schema level. It's possible there's a bug here, but not one that I've heard about before.
Is it possible for you to assemble a schema/data sample that demonstrates the problem? I could probably give you a more clear answer then.
Hi,
I'm also having trouble with nested validations, although I'm not sure I would describe the problem as @panSarin . I have the following schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id" : "http://localhost:3000/schemas/address.json",
"definitions": {
"address": {
"type": "object",
"properties": {
"line1": { "type": "string" },
"line2": { "type": "string" },
"line3": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip_code": { "type": "string" },
"phone_number": { "type": "string" }
},
"required": ["zip_code"]
},
"type": "object",
"properties": {
"from_address": { "$ref": "#/definitions/address" },
"to_address": { "$ref": "#/definitions/address" }
},
"required": ["from_address", "to_address"]
}
The following data is validated as correct by the gem, but it is missing the zip_code
"from_address" : { "line1" : "test" }, "to_address" : { "line1" : "test" } }
Hi @mrtibs2000,
I think you may have forgotten a curly brace somewhere in your schema above (specifically, the closing brace for definitions
). When I put both your schema and data into files, I correctly see the data come out as invalid:
$ bundle exec validate-schema schema.json data.json
data.json#/from_address: failed schema #/properties/from_address: "zip_code" wasn't supplied.
data.json#/to_address: failed schema #/properties/to_address: "zip_code" wasn't supplied.
Here's the schema.json
I used (corrected from yours above):
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id" : "http://localhost:3000/schemas/address.json",
"definitions": {
"address": {
"type": "object",
"properties": {
"line1": { "type": "string" },
"line2": { "type": "string" },
"line3": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip_code": { "type": "string" },
"phone_number": { "type": "string" }
},
"required": ["zip_code"]
}
},
"type": "object",
"properties": {
"from_address": { "$ref": "#/definitions/address" },
"to_address": { "$ref": "#/definitions/address" }
},
"required": ["from_address", "to_address"]
}
Hi @brandur ,
Thanks for looking into this. I did have the right schema, I just didn't paste it properly here. The strange part is that when I have a Ruby script, it does work fine. It doesn't behave the same when I execute this from a Rails app. Here's what I have in my controller:
before_action :parse_json, :validate_json
def parse_json
begin
@json_data = JSON.parse(request.body.string)
rescue
render json: { errors: ["unable to parse json: #{request.body.string}"] }.to_json, status: 400
end
end
def validate_json
schema_data = JSON.parse(File.read(Rails.root.join('app').join('schema').join('price.json').to_s))
schema = JsonSchema.parse!(schema_data)
errors = schema.validate(@json_data)
if !errors[0]
render json: { errors: errors[1].map { |error| error.to_s } }.to_json, status: 422
end
end
Hey @mrtibs2000,
Based on the fact that the gem seems to be doing the right thing, it seems reasonable to conclude here that the inputs may not be making it there as we would hope. Can you throw a debugger into that validate_json
method and just make sure that the schema and data are the values that you'd expect?
Sorry to have wasted your time. I forgot to include this in my code:
schema.expand_references!
I would say close the issue, but I'm not sure if the original problem was resolved.
Thanks!!!
Oops, yep that one is a bit of a gotcha! Good to hear you fixed it though,
thanks :)
On Tuesday, April 12, 2016, Tiberiu Motoc notifications@github.com wrote:
Sorry to have wasted your time. I forgot to include this in my code:
schema.expand_references!
Thanks!!!—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#57 (comment)
https://gist.github.com/panSarin/6a7efcdd503a315d21fcf38213d560cc
So lets say that is my JSON (required 'test' field wont occur in response so it should raise error during validation)
And if my response looks like:
"user_settings"=>
{
"id"=>"573486adfc0022fae6000037",
"ios_landing_page"=>"Hello",
"allowed_touch_login"=>false,
"units"=>[{"id"=>"5734860000e", "facility"=>"Facility"}]
}
Problem is that if i run
RSpec::Matchers.define :match_schema do |schema_file_name, object_name|
match do |response|
schema_path = "#{Dir.pwd}/jsons/#{schema_file_name}.json"
schema_data = JSON.parse(File.read(schema_path))
schema = JsonSchema.parse! schema_data
schema.expand_references!
schema.definitions[object_name].validate!(response.with_indifferent_access) # raise errors if not valid
true # returns true if errors weren't raised so test pass
end
end
on this response + object, it will pass because it doesnt check nested objects schema.
I had to add something like:
EmptyResultError = Class.new(StandardError)
RSpec::Matchers.define :match_nested_schema do |schema_file_name, object_name, nested_object_name, response_key=nil|
match do |response|
schema_path = "#{Dir.pwd}/jsons/#{schema_file_name}.json"
schema_data = JSON.parse(File.read(schema_path))
schema = JsonSchema.parse! schema_data
schema.expand_references!
aggregated_object = schema.definitions[object_name]
aggregated_object.validate!(response.with_indifferent_access) # raise errors if not valid
response_key = aggregated_object.properties.keys.first if response_key.nil?
elements = response[response_key]
raise EmptyResultError if elements.nil?
elements.each do |nested_object|
schema.definitions[nested_object_name].validate!(nested_object.with_indifferent_access)
end
true # returns true if errors weren't raised so test pass
end
end
so i can call it with passing which nested element i want to validate too.
So is it a bug , or i am missing something?;]
Hey @panSarin, you definitely shouldn't have to hack it that far. The gem will validate nested properties just fine, so something must be a little off here.
One thing I noticed in your schema is that although you have the test
property in your required
array, it doesn't have a definition in your properties
list. I wonder if something like that could be throwing it off?
resource_group_schema = {
"type":"object",
"$schema":"http://json-schema.org/draft-03/schema",
"properties" :
{
"name":
{
"type":"string",
"maxLength":64,
"required":True
},
"filters": {
"type":"array",
"required":True,
"properties": {
"attribute_name": {
"type":"string",
"required":True,
"enum": ["a", "b"]
},
}
}
},
"additionalProperties":False
}
Above valdiation for filters are not taking effect. If I do not send attribute_name still validation is success while it must fail What is worng here I am not able to understand with Draft3Validation ?
@ssgaur Hey, there should probably be a check on this somewhere in here, but given that draft 4 had been the overwhelmingly preferred spec for so long when I wrote this, the project should largely be considered only usable for draft 4 and up.
Given that draft 3 is quite dated at this point, it probably makes sense for you to write your schemas targeting a newer spec anyway. In 4, required
switched to an array, and that's what you'll need to change here in order to produce the validation error you're looking for.