Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation of required in nested object #57

Open
panSarin opened this issue Apr 1, 2016 · 11 comments
Open

Validation of required in nested object #57

panSarin opened this issue Apr 1, 2016 · 11 comments

Comments

@panSarin
Copy link

panSarin commented Apr 1, 2016

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)
@brandur
Copy link
Owner

brandur commented Apr 2, 2016

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.

@mrtibs2000
Copy link

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" } }

@brandur
Copy link
Owner

brandur commented Apr 12, 2016

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"]
}

@mrtibs2000
Copy link

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

@brandur
Copy link
Owner

brandur commented Apr 12, 2016

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?

@mrtibs2000
Copy link

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!!!

@brandur
Copy link
Owner

brandur commented Apr 13, 2016

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)

@panSarin
Copy link
Author

panSarin commented May 12, 2016

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?;]

@panSarin panSarin reopened this May 19, 2016
@brandur
Copy link
Owner

brandur commented May 24, 2016

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?

@ssgaur
Copy link

ssgaur commented Mar 7, 2018

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 ?

@brandur
Copy link
Owner

brandur commented Mar 7, 2018

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants