From 00849bc1a2cda3df641a2626b60f2fa1085e83f1 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Fri, 1 Mar 2019 15:50:18 -0700 Subject: [PATCH] Dereference child schemas for schemas without ref If you have a subschema that says that it is expanded, it might actually have children that have not yet been expanded. If the subschema did not have a reference, we were not recursing any further. --- lib/json_schema/reference_expander.rb | 19 +++++++- test/json_schema/reference_expander_test.rb | 53 +++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/json_schema/reference_expander.rb b/lib/json_schema/reference_expander.rb index 5b06954..4872462 100644 --- a/lib/json_schema/reference_expander.rb +++ b/lib/json_schema/reference_expander.rb @@ -87,6 +87,18 @@ def build_schema_paths(uri, schema) def dereference(ref_schema, ref_stack) 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 + + 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(", ")}.} @@ -114,9 +126,12 @@ def dereference(ref_schema, ref_stack) # schema as the reference schema. next if ref_schema == subschema - next if subschema.expanded? - 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 diff --git a/test/json_schema/reference_expander_test.rb b/test/json_schema/reference_expander_test.rb index 5fe84c1..d70c53b 100644 --- a/test/json_schema/reference_expander_test.rb +++ b/test/json_schema/reference_expander_test.rb @@ -320,6 +320,59 @@ 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 "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",