From 1001a5ae8d4f41632f41e653e777b558151fc70a Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Wed, 12 Jun 2024 22:40:03 +0200 Subject: [PATCH] fix issue with refs in other keywords (#137) * fix issue with refs in other keywords --- .../jsonschema/JsonSchema.java | 8 +++- .../jsonschema/FakeJsonSchemaFactory.java | 14 ++++++- .../jsonschema/JsonSubSchemaTest.java | 14 ++++++- core/pom.xml | 2 - .../core/DefaultJsonObjectSchema.java | 26 +++++++++++- .../jsonschema/core/DefaultJsonSubSchema.java | 17 ++++++-- .../jsonschema/core/EmptyJsonSchema.java | 14 ++++++- .../jsonschema/core/FalseJsonSchema.java | 14 ++++++- .../jsonschema/core/TrueJsonSchema.java | 14 ++++++- .../type/NamedJsonSchemaKeywordType.java | 4 +- .../keyword/type/SchemaArrayKeywordType.java | 8 +--- .../keyword/type/SubSchemaKeywordType.java | 2 +- .../core/vocab/core/RefKeyword.java | 15 +------ .../core/AbstractJsonValueSchemaTest.java | 14 ++++++- .../core/DefaultJsonObjectSchemaTest.java | 6 +-- .../core/DefaultJsonSubSchemaTest.java | 42 ++++++++++++++++++- pom.xml | 2 +- 17 files changed, 173 insertions(+), 43 deletions(-) diff --git a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/JsonSchema.java b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/JsonSchema.java index 16b71aee..d332c595 100644 --- a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/JsonSchema.java +++ b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/JsonSchema.java @@ -25,15 +25,21 @@ import io.github.sebastiantoepfer.ddd.common.Printable; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; public interface JsonSchema extends JsonValue, Printable { Validator validator(); Optional keywordByName(String name); - Optional asSubSchema(String name); + Optional subSchema(String name); + + Stream subSchemas(String name); + + Optional subSchema(JsonPointer pointer); default JsonSchema rootSchema() { return this; diff --git a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/FakeJsonSchemaFactory.java b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/FakeJsonSchemaFactory.java index a05855f3..dc0c2931 100644 --- a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/FakeJsonSchemaFactory.java +++ b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/FakeJsonSchemaFactory.java @@ -26,8 +26,10 @@ import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import io.github.sebastiantoepfer.jsonschema.spi.JsonSchemaFactory; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; public final class FakeJsonSchemaFactory implements JsonSchemaFactory { @@ -54,7 +56,17 @@ public JsonValue.ValueType getValueType() { } @Override - public Optional asSubSchema(String name) { + public Optional subSchema(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Stream subSchemas(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Optional subSchema(JsonPointer pointer) { throw new UnsupportedOperationException("Not supported yet."); } diff --git a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/JsonSubSchemaTest.java b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/JsonSubSchemaTest.java index b0ca587f..2e55ca3d 100644 --- a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/JsonSubSchemaTest.java +++ b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/JsonSubSchemaTest.java @@ -29,8 +29,10 @@ import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; class JsonSubSchemaTest { @@ -61,7 +63,17 @@ public JsonValue.ValueType getValueType() { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Stream subSchemas(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Optional subSchema(JsonPointer pointer) { throw new UnsupportedOperationException("Not supported yet."); } diff --git a/core/pom.xml b/core/pom.xml index 590b5e63..bc1ccafc 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -136,9 +136,7 @@ - **/tests/draft2020-12/maxContains.json **/tests/draft2020-12/maxItems.json **/tests/draft2020-12/maxLength.json diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchema.java index 60d88282..2122b6ef 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchema.java @@ -31,6 +31,7 @@ import io.github.sebastiantoepfer.jsonschema.Validator; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import jakarta.json.JsonObject; +import jakarta.json.JsonPointer; import java.util.Optional; import java.util.stream.Stream; @@ -61,9 +62,32 @@ private Stream keywords() { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { return Optional.ofNullable(asJsonObject().get(name)) .flatMap(new DefaultJsonSchemaFactory()::tryToCreateSchemaFrom) .map(subSchema -> new DefaultJsonSubSchema(this, subSchema)); } + + @Override + public Stream subSchemas(final String name) { + return asJsonObject() + .getJsonArray(name) + .stream() + .map(new DefaultJsonSchemaFactory()::tryToCreateSchemaFrom) + .flatMap(Optional::stream) + .map(subSchema -> new DefaultJsonSubSchema(this, subSchema)); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { + final Optional result; + if (pointer.containsValue(asJsonObject())) { + result = new DefaultJsonSchemaFactory() + .tryToCreateSchemaFrom(pointer.getValue(asJsonObject())) + .map(subSchema -> new DefaultJsonSubSchema(this, subSchema)); + } else { + result = Optional.empty(); + } + return result; + } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchema.java index 6646d464..639dc4f6 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchema.java @@ -32,6 +32,7 @@ import io.github.sebastiantoepfer.jsonschema.Validator; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import jakarta.json.JsonObject; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Objects; import java.util.Optional; @@ -92,10 +93,18 @@ private boolean isJsonObject() { } @Override - public Optional asSubSchema(final String name) { - return Optional.ofNullable(asJsonObject().get(name)) - .flatMap(new DefaultJsonSchemaFactory()::tryToCreateSchemaFrom) - .map(subSchema -> new DefaultJsonSubSchema(this, subSchema)); + public Optional subSchema(final String name) { + return schema.subSchema(name).map(sub -> new DefaultJsonSubSchema(this, sub)); + } + + @Override + public Stream subSchemas(final String name) { + return schema.subSchemas(name).map(sub -> new DefaultJsonSubSchema(this, sub)); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { + return schema.subSchema(pointer).map(sub -> new DefaultJsonSubSchema(this, sub)); } @Override diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/EmptyJsonSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/EmptyJsonSchema.java index 8252d265..e53919c6 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/EmptyJsonSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/EmptyJsonSchema.java @@ -28,8 +28,10 @@ import io.github.sebastiantoepfer.jsonschema.JsonSubSchema; import io.github.sebastiantoepfer.jsonschema.Validator; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; final class EmptyJsonSchema extends AbstractJsonValueSchema { @@ -48,7 +50,17 @@ public Optional keywordByName(final String name) { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { + return Optional.empty(); + } + + @Override + public Stream subSchemas(final String name) { + return Stream.empty(); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { return Optional.empty(); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/FalseJsonSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/FalseJsonSchema.java index 291a0c71..03bcebc5 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/FalseJsonSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/FalseJsonSchema.java @@ -28,8 +28,10 @@ import io.github.sebastiantoepfer.jsonschema.JsonSubSchema; import io.github.sebastiantoepfer.jsonschema.Validator; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; final class FalseJsonSchema extends AbstractJsonValueSchema { @@ -53,7 +55,17 @@ public Optional keywordByName(final String name) { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { return Optional.empty(); } + + @Override + public Stream subSchemas(final String name) { + return Stream.empty(); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/TrueJsonSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/TrueJsonSchema.java index d0244e1a..832c4db5 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/TrueJsonSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/TrueJsonSchema.java @@ -28,8 +28,10 @@ import io.github.sebastiantoepfer.jsonschema.JsonSubSchema; import io.github.sebastiantoepfer.jsonschema.Validator; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; final class TrueJsonSchema extends AbstractJsonValueSchema { @@ -48,7 +50,17 @@ public Optional keywordByName(final String name) { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { + return Optional.empty(); + } + + @Override + public Stream subSchemas(final String name) { + return Stream.empty(); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { return Optional.empty(); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/NamedJsonSchemaKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/NamedJsonSchemaKeywordType.java index 2cf6c2e0..397767d6 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/NamedJsonSchemaKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/NamedJsonSchemaKeywordType.java @@ -51,12 +51,12 @@ public String name() { @Override public Keyword createKeyword(final JsonSchema schema) { - final JsonSubSchema pseudoSchema = schema.asSubSchema(name).orElseThrow(IllegalArgumentException::new); + final JsonSubSchema pseudoSchema = schema.subSchema(name).orElseThrow(IllegalArgumentException::new); return pseudoSchema .asJsonObject() .keySet() .stream() - .map(n -> Map.entry(n, pseudoSchema.asSubSchema(n).orElseThrow(IllegalArgumentException::new))) + .map(n -> Map.entry(n, pseudoSchema.subSchema(n).orElseThrow(IllegalArgumentException::new))) .collect( collectingAndThen( toMap(Map.Entry::getKey, Map.Entry::getValue), diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SchemaArrayKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SchemaArrayKeywordType.java index 9e0ff95d..9dcbeb2b 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SchemaArrayKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SchemaArrayKeywordType.java @@ -27,7 +27,6 @@ import static java.util.stream.Collectors.toList; import io.github.sebastiantoepfer.jsonschema.JsonSchema; -import io.github.sebastiantoepfer.jsonschema.core.DefaultJsonSchemaFactory; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; import java.util.List; @@ -51,11 +50,6 @@ public String name() { @Override public Keyword createKeyword(final JsonSchema schema) { - return schema - .asJsonObject() - .getJsonArray(name) - .stream() - .map(new DefaultJsonSchemaFactory()::create) - .collect(collectingAndThen(toList(), keywordCreator)); + return schema.subSchemas(name).collect(collectingAndThen(toList(), keywordCreator)); } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SubSchemaKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SubSchemaKeywordType.java index 4b8cadc3..80cbd319 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SubSchemaKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/keyword/type/SubSchemaKeywordType.java @@ -47,6 +47,6 @@ public String name() { @Override public Keyword createKeyword(final JsonSchema schema) { - return schema.asSubSchema(name()).map(keywordCreator).orElseThrow(IllegalArgumentException::new); + return schema.subSchema(name()).map(keywordCreator).orElseThrow(IllegalArgumentException::new); } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/RefKeyword.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/RefKeyword.java index de709526..c31901b0 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/RefKeyword.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/RefKeyword.java @@ -25,11 +25,9 @@ import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.JsonSchema; -import io.github.sebastiantoepfer.jsonschema.JsonSchemas; import io.github.sebastiantoepfer.jsonschema.keyword.Applicator; import jakarta.json.Json; import jakarta.json.JsonPointer; -import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import java.io.IOException; import java.net.URI; @@ -82,23 +80,14 @@ private JsonSchema retrieveJsonSchema() { } else { json = retrieveSchemaFromLocalSchema(); } - return JsonSchemas.load(json); + return json; } catch (IOException ex) { throw new IllegalStateException("can not load schema!", ex); } } private JsonSchema retrieveSchemaFromLocalSchema() throws IOException { - final JsonPointer pointer = createPointer(); - if (pointer.containsValue(searchAnchor())) { - return JsonSchemas.load(pointer.getValue(searchAnchor())); - } else { - throw new IOException("can not find referenced value."); - } - } - - private JsonStructure searchAnchor() { - return schema.rootSchema().asJsonObject(); + return schema.rootSchema().subSchema(createPointer()).orElseThrow(); } private JsonPointer createPointer() { diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/AbstractJsonValueSchemaTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/AbstractJsonValueSchemaTest.java index e1fee821..145361cf 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/AbstractJsonValueSchemaTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/AbstractJsonValueSchemaTest.java @@ -33,8 +33,10 @@ import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import jakarta.json.JsonArray; import jakarta.json.JsonObject; +import jakarta.json.JsonPointer; import jakarta.json.JsonValue; import java.util.Optional; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; class AbstractJsonValueSchemaTest { @@ -81,7 +83,17 @@ public Optional keywordByName(final String name) { } @Override - public Optional asSubSchema(final String name) { + public Optional subSchema(final String name) { + return Optional.empty(); + } + + @Override + public Stream subSchemas(final String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Optional subSchema(final JsonPointer pointer) { return Optional.empty(); } } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchemaTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchemaTest.java index e484a110..d8b2e54f 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchemaTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonObjectSchemaTest.java @@ -114,7 +114,7 @@ void should_return_empty_for_non_existing_keyword() { @Test void should_return_empty_if_non_subschema_exists_under_the_given_name() { assertThat( - new DefaultJsonObjectSchema(Json.createObjectBuilder().add("test", "hallo").build()).asSubSchema( + new DefaultJsonObjectSchema(Json.createObjectBuilder().add("test", "hallo").build()).subSchema( "properties" ), isEmpty() @@ -124,7 +124,7 @@ void should_return_empty_if_non_subschema_exists_under_the_given_name() { @Test void should_return_subschema_if_subschema_exists_under_the_given_name() { assertThat( - new DefaultJsonObjectSchema(Json.createObjectBuilder().add("test", JsonValue.FALSE).build()).asSubSchema( + new DefaultJsonObjectSchema(Json.createObjectBuilder().add("test", JsonValue.FALSE).build()).subSchema( "test" ), isPresent() @@ -136,7 +136,7 @@ void should_return_empty_if_given_name_not_resolve_to_a_valid_schematype() { assertThat( new DefaultJsonObjectSchema( Json.createObjectBuilder().add("test", JsonValue.EMPTY_JSON_ARRAY).build() - ).asSubSchema("test"), + ).subSchema("test"), isEmpty() ); } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchemaTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchemaTest.java index 75c65dfd..d7a55369 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchemaTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/DefaultJsonSubSchemaTest.java @@ -27,6 +27,7 @@ import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; @@ -48,7 +49,7 @@ void should_return_owner() { } @Test - void should_return_subschema() { + void should_return_subschema_by_name() { final JsonSchema owner = new DefaultJsonSchemaFactory() .create( Json.createObjectBuilder().add("test", Json.createObjectBuilder().add("sub", JsonValue.TRUE)).build() @@ -57,7 +58,44 @@ void should_return_subschema() { new DefaultJsonSubSchema( owner, new DefaultJsonSchemaFactory().create(Json.createObjectBuilder().add("sub", JsonValue.TRUE).build()) - ).asSubSchema("sub"), + ).subSchema("sub"), + isPresent() + ); + } + + @Test + void should_return_subschemas() { + final JsonSchema owner = new DefaultJsonSchemaFactory() + .create( + Json.createObjectBuilder() + .add("test", Json.createObjectBuilder().add("subs", Json.createArrayBuilder().add(JsonValue.TRUE))) + .build() + ); + assertThat( + new DefaultJsonSubSchema( + owner, + new DefaultJsonSchemaFactory() + .create( + Json.createObjectBuilder().add("subs", Json.createArrayBuilder().add(JsonValue.TRUE)).build() + ) + ) + .subSchemas("subs") + .toList(), + hasSize(1) + ); + } + + @Test + void should_return_subschema_by_pointer() { + final JsonSchema owner = new DefaultJsonSchemaFactory() + .create( + Json.createObjectBuilder().add("test", Json.createObjectBuilder().add("sub", JsonValue.TRUE)).build() + ); + assertThat( + new DefaultJsonSubSchema( + owner, + new DefaultJsonSchemaFactory().create(Json.createObjectBuilder().add("sub", JsonValue.TRUE).build()) + ).subSchema(Json.createPointer("/sub")), isPresent() ); } diff --git a/pom.xml b/pom.xml index 79dc52f9..aa0ec438 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ - 2024-05-30T16:17:54Z + 2024-06-12T20:09:52Z 17