oneOf / nullable ref behavior
97jaz opened this issue · 5 comments
When I have a property like:
"foo": {
"oneOf": [
{ "$ref": "file:/bar.json#" },
{ "type": "null" }
]
}
and I try to match against valid data, I'm getting an error telling me More than one subschema in "oneOf" matched
. Which would seem to be impossible, unless the $ref
is implicitly taken to be nullable. But I didn't think it's supposed to be.
@97jaz I dug into this and it looks like the "file:/bar.json#"
is not being expanded.
@brandur I will work on a fix, as this is very likely a problem introduced with the changes in #106 and #108. Update: probably not a regression, but I have a failing test, so will work on a fix anyway :)
In the meanwhile, @97jaz, I think your best bet is to pin the json_schema
gem version to 0.20.1 for your project.
@97jaz Can you confirm the exact format of your $ref
? I'm not used to seeing a :
in the value. (Update: on second thoughts, I'm assuming this is a URI that refers to the file system)
I have the following test (I put it in test/json_schema/reference_expander_test.rb
).
Based on your description of the problem, @97jaz, I would expect this to fail, but it is passing.
Would you help figure out what is different between the defined fixtures and your use case, in order to get a failing test?
it "works" do
sample1 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"foo" => {
"oneOf" => [
{"type" => "null"},
{"$ref" => "http://json-schema.org/b.json#"}
]
}
}
}
schema1 = JsonSchema::Parser.new.parse!(sample1)
schema1.uri = "http://json-schema.org/a.json"
sample2 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"bar" => {
"type" => "string",
"maxLength" => 3
}
}
}
schema2 = JsonSchema::Parser.new.parse!(sample2)
schema2.uri = "http://json-schema.org/b.json"
# Initialize a store and add our schema to it.
store = JsonSchema::DocumentStore.new
store.add_schema(schema1)
store.add_schema(schema2)
expander = JsonSchema::ReferenceExpander.new
expander.expand(schema1, store: store)
ok, errors = schema1.validate({"foo" => nil})
assert ok, "with nil"
ok, errors = schema1.validate({"foo" => {"bar" => "abc"}})
assert ok, "with object"
ok, errors = schema1.validate({"foo" => {"bar" => "abcd"}})
refute ok, "with invalid value in object"
end
Ok, I experimented with adding another layer of indirection, and was able to make a failing test.
This suggests that this is not a regression introduced in #106 or #107, but rather an edge case not covered by the current test suite. @97jaz, if this is the case, I don't think pinning the gem to 0.20.1 will help.
it "debugging brandur/json_schema#100" do
sample1 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"foo" => {
"$ref" => "http://json-schema.org/b.json#"
}
}
}
schema1 = JsonSchema::Parser.new.parse!(sample1)
schema1.uri = "http://json-schema.org/a.json"
sample2 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"bar" => {
"oneOf" => [
{"type" => "null"},
{"$ref" => "http://json-schema.org/c.json#"}
]
}
},
}
schema2 = JsonSchema::Parser.new.parse!(sample2)
schema2.uri = "http://json-schema.org/b.json"
sample3 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"baz" => {
"type" => "integer"
}
}
}
schema3 = JsonSchema::Parser.new.parse!(sample3)
schema3.uri = "http://json-schema.org/c.json"
# Initialize a store and add our schema to it.
store = JsonSchema::DocumentStore.new
store.add_schema(schema1)
store.add_schema(schema2)
store.add_schema(schema3)
expander = JsonSchema::ReferenceExpander.new
expander.expand(schema1, store: store)
ok, errors = schema1.validate({"foo" => {"bar" => nil}})
assert ok, "should be valid with nil (#{errors.inspect})"
ok, errors = schema1.validate({"foo" => {"bar" => {"baz" => 1}}})
assert ok, "should be valid with object (#{errors.inspect})"
end
If you eyeball schema1 just before the assertions, you'll see:
schema1.properties["foo"].properties["bar"].inspect_schema
=> {:@expanded=>"true", :@one_of=>[{:@expanded=>"true", :@type=>["\"null\""]}, "http://json-schema.org/c.json# [COLLAPSED] [ORIGINAL]"]}
I found another edge case 🎉 (JSON Schema, the gift that keeps giving 😂)
Here's the failing test that I put together:
it "expands a local reference within the link of a remote schema" do
sample1 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"properties" => {
"foo" => {
"$ref" => "http://json-schema.org/b.json#"
}
}
}
schema1 = JsonSchema::Parser.new.parse!(sample1)
schema1.uri = "http://json-schema.org/a.json"
sample2 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
"type" => "object",
"definitions" => {
"bar" => {
"type" => "string",
"maxLength" => 4
}
},
"links" => [
{
"schema" => {
"properties" => {
"baz" => {
"$ref" => "#/definitions/bar"
}
}
}
}
]
}
schema2 = JsonSchema::Parser.new.parse!(sample2)
schema2.uri = "http://json-schema.org/b.json"
# Initialize a store and add our schema to it.
store = JsonSchema::DocumentStore.new
store.add_schema(schema1)
store.add_schema(schema2)
expander = JsonSchema::ReferenceExpander.new
expander.expand!(schema1, store: store)
assert_equal 4, schema1.properties["foo"].links[0].schema.properties["baz"].max_length