Skip to content

Commit

Permalink
Merge pull request #110 from kytrinyx/derefence-child-without-ref
Browse files Browse the repository at this point in the history
Dereference schema children for subschemas without a reference
  • Loading branch information
brandur committed Mar 12, 2019
2 parents c141555 + 5deb676 commit fd62919
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 12 deletions.
51 changes: 39 additions & 12 deletions lib/json_schema/reference_expander.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,25 @@ def build_schema_paths(uri, schema)
end
end

def dereference(ref_schema, ref_stack)
def dereference(ref_schema, ref_stack, parent_ref: nil)
ref = ref_schema.reference

# Some schemas don't have a reference, but do
# have children. If that's the case, we need to
# dereference the subschemas.
if !ref
schema_children(ref_schema) do |subschema|
next unless subschema.reference

if !subschema.reference.uri && parent_ref
subschema.reference = JsonReference::Reference.new("#{parent_ref.uri}#{subschema.reference.pointer}")
end

dereference(subschema, ref_stack)
end
return true
end

# detects a reference cycle
if ref_stack.include?(ref)
message = %{Reference loop detected: #{ref_stack.sort.join(", ")}.}
Expand All @@ -110,22 +126,33 @@ def dereference(ref_schema, ref_stack)
# references.
if ref.uri
schema_children(new_schema) do |subschema|
next if subschema.expanded?
next unless subschema.reference

# Don't bother if the subschema points to the same
# schema as the reference schema.
next if ref_schema == subschema

if !subschema.reference.uri
# the subschema's ref is local to the file that the
# subschema is in; however since there's no URI
# the 'resolve_pointer' method would try to look it up
# within @schema. So: manually reconstruct the reference to
# use the URI of the parent ref.
subschema.reference = JsonReference::Reference.new("#{ref.uri}#{subschema.reference.pointer}")
if subschema.reference
# If the subschema has a reference, then
# we don't need to recurse if the schema is
# already expanded.
next if subschema.expanded?

if !subschema.reference.uri
# the subschema's ref is local to the file that the
# subschema is in; however since there's no URI
# the 'resolve_pointer' method would try to look it up
# within @schema. So: manually reconstruct the reference to
# use the URI of the parent ref.
subschema.reference = JsonReference::Reference.new("#{ref.uri}#{subschema.reference.pointer}")
end
end
dereference(subschema, ref_stack)

# If we're recursing into a schema via a global reference, then if
# the current subschema doesn't have a reference, we have no way of
# figuring out what schema we're in. The resolve_pointer method will
# default to looking it up in the initial schema. Instead, we're
# passing the parent ref here, so we can grab the URI
# later if needed.
dereference(subschema, ref_stack, parent_ref: ref)
end
end

Expand Down
98 changes: 98 additions & 0 deletions test/json_schema/reference_expander_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,104 @@
assert_equal 3, schema1.properties["foo"].properties["omg"].max_length
end

it "it handles oneOf with nested references to an external 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",
"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" => "string",
"maxLength" => 3
}
}
}
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)

assert_equal 3, schema1.properties["foo"].properties["bar"].one_of[1].properties["baz"].max_length
end

it "it handles oneOf with nested references to a local 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" => {
"baz" => {
"type" => "string",
"maxLength" => 3
}
},
"properties" => {
"bar" => {
"oneOf" => [
{"type" => "null"},
{"$ref" => "#/definitions/baz"}
]
}
},
}
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 3, schema1.properties["foo"].properties["bar"].one_of[1].max_length
end

it "expands a schema with a reference to an external schema with a nested local property reference" do
sample1 = {
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
Expand Down

0 comments on commit fd62919

Please sign in to comment.