diff --git a/src/main/java/com/trickl/assertj/core/api/json/serialize/AbstractJsonObjectAssert.java b/src/main/java/com/trickl/assertj/core/api/json/serialize/AbstractJsonObjectAssert.java index f0e35b7..f72c110 100644 --- a/src/main/java/com/trickl/assertj/core/api/json/serialize/AbstractJsonObjectAssert.java +++ b/src/main/java/com/trickl/assertj/core/api/json/serialize/AbstractJsonObjectAssert.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; +import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; import com.trickl.assertj.core.api.json.JsonContainer; import java.io.BufferedWriter; import java.io.FileWriter; @@ -28,6 +29,10 @@ public abstract class AbstractJsonObjectAssert> extends AbstractAssert { + private static final String DEFAULT_JSON_DATA_FILE_EXT = ".example.json"; + + private static final String DEFAULT_SCHEMA_FILE_EXT = ".schema.json"; + private ObjectMapper objectMapper = new ObjectMapper(); private Path schemaResourcePath = null; @@ -40,6 +45,12 @@ public abstract class AbstractJsonObjectAssert selfType) { super(actual, selfType); } @@ -52,7 +63,7 @@ public AbstractJsonObjectAssert(JsonObject actual, Class selfType) { public S serializesAsExpected() { if (serializationResourcePath == null) { serializationResourcePath = - classAsResourcePathConvention(actual.getObject().getClass(), ".example.json"); + classAsResourcePathConvention(actual.getObject().getClass(), jsonDataFileExtension); } Path actualPath = null; @@ -77,7 +88,7 @@ public S serializesAsExpected() { public S deserializesAsExpected() { if (deserializationResourceUrl == null) { deserializationResourceUrl = - classAsResourceUrlConvention(actual.getObject().getClass(), ".example.json"); + classAsResourceUrlConvention(actual.getObject().getClass(), jsonDataFileExtension); } assertThat(deserialize(deserializationResourceUrl, actual.getObject().getClass())) @@ -93,7 +104,7 @@ public S deserializesAsExpected() { public S deserializesWithoutError() { if (deserializationResourceUrl == null) { deserializationResourceUrl = - classAsResourceUrlConvention(actual.getObject().getClass(), ".example.json"); + classAsResourceUrlConvention(actual.getObject().getClass(), jsonDataFileExtension); } assertThat(deserialize(deserializationResourceUrl, actual.getObject().getClass())) @@ -109,7 +120,8 @@ public S deserializesWithoutError() { public S schemaAsExpected() { if (schemaResourcePath == null) { schemaResourcePath = - classAsResourcePathConvention(actual.getObject().getClass(), ".schema.json"); + classAsResourcePathConvention(actual.getObject().getClass(), + schemaFileExtension); } Path actualPath = null; @@ -172,7 +184,22 @@ public S doNotCreateExpectedIfAbsent() { return myself; } - private T deserialize(URL value, Class clazz) { + public S withNoInlineSchemas() { + noInlineSchemas = true; + return myself; + } + + public S withJsonDataFileExtension(String extension) { + jsonDataFileExtension = extension; + return myself; + } + + public S withSchemaFileExtension(String extension) { + schemaFileExtension = extension; + return myself; + } + + protected T deserialize(URL value, Class clazz) { try { return objectMapper.readValue(value, (Class) clazz); } catch (IOException e) { @@ -180,7 +207,7 @@ private T deserialize(URL value, Class clazz) { } } - private String serialize(Object obj) { + protected String serialize(Object obj) { try { return objectMapper.writeValueAsString(obj); } catch (IOException e) { @@ -188,11 +215,16 @@ private String serialize(Object obj) { } } - private String schema(Object obj) { + protected String schema(Object obj) { try { - JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(objectMapper); - JsonSchema schema = schemaGen.generateSchema(actual.getObject().getClass()); - return objectMapper.writeValueAsString(schema); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + if (noInlineSchemas) { + visitor.setVisitorContext(new NoInlineSchemaVisitorContext()); + } + objectMapper.acceptJsonFormatVisitor(actual.getObject().getClass(), visitor); + JsonSchema schema = visitor.finalSchema(); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema); + } catch (IOException e) { throw new UncheckedIOException("Unable to generate JSON schema", e); } @@ -206,12 +238,12 @@ private JsonContainer safeJson(Path path) { } } - private URL classAsResourceUrlConvention(Class clazz, String extension) { + protected URL classAsResourceUrlConvention(Class clazz, String extension) { String resourceName = clazz.getSimpleName() + extension; return clazz.getResource(resourceName); } - private Path classAsResourcePathConvention(Class clazz, String extension) { + protected Path classAsResourcePathConvention(Class clazz, String extension) { String projectDirectory = projectDir; if (projectDirectory == null) { projectDirectory = getProjectDirectoryFromLocalClazz(clazz); diff --git a/src/main/java/com/trickl/assertj/core/api/json/serialize/NoInlineSchemaVisitorContext.java b/src/main/java/com/trickl/assertj/core/api/json/serialize/NoInlineSchemaVisitorContext.java new file mode 100644 index 0000000..36aa4ef --- /dev/null +++ b/src/main/java/com/trickl/assertj/core/api/json/serialize/NoInlineSchemaVisitorContext.java @@ -0,0 +1,14 @@ +package com.trickl.assertj.core.api.json.serialize; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext; + +public class NoInlineSchemaVisitorContext extends VisitorContext { + @Override + public String getSeenSchemaUri(JavaType javaType) { + if (javaType != null && !javaType.isPrimitive()) { + return javaTypeToUrn(javaType); + } + return null; + } +} \ No newline at end of file diff --git a/src/test/java/com/trickl/assertj/core/api/json/serialize/JsonObjectAssert_serializationAsExpected_Test.java b/src/test/java/com/trickl/assertj/core/api/json/serialize/JsonObjectAssert_serializationAsExpected_Test.java index 700e394..63bcaee 100644 --- a/src/test/java/com/trickl/assertj/core/api/json/serialize/JsonObjectAssert_serializationAsExpected_Test.java +++ b/src/test/java/com/trickl/assertj/core/api/json/serialize/JsonObjectAssert_serializationAsExpected_Test.java @@ -7,6 +7,8 @@ import java.util.Map; import com.trickl.assertj.examples.Example; +import com.trickl.assertj.examples.NestedExample; + import org.junit.Test; /** @@ -68,4 +70,20 @@ public void should_pass_on_schema_match() { assertThat(example) .schemaAsExpected(); } + + @Test + public void should_respect_no_inlining() { + NestedExample nested = new NestedExample(); + assertThat(nested) + .withNoInlineSchemas() + .schemaAsExpected(); + } + + @Test + public void should_respect_allow_inlining() { + NestedExample nested = new NestedExample(); + assertThat(nested) + .withSchemaFileExtension(".schema2.json") + .schemaAsExpected(); + } } diff --git a/src/test/java/com/trickl/assertj/examples/Example.java b/src/test/java/com/trickl/assertj/examples/Example.java index ee73940..663c2ae 100644 --- a/src/test/java/com/trickl/assertj/examples/Example.java +++ b/src/test/java/com/trickl/assertj/examples/Example.java @@ -6,7 +6,7 @@ @Data public class Example { - @JsonProperty("my-field") - @JsonPropertyDescription("Description of my field") - private String myField; + @JsonProperty("my-field") + @JsonPropertyDescription("Description of my field") + private String myField; } diff --git a/src/test/java/com/trickl/assertj/examples/NestedExample.java b/src/test/java/com/trickl/assertj/examples/NestedExample.java new file mode 100644 index 0000000..a04c2cd --- /dev/null +++ b/src/test/java/com/trickl/assertj/examples/NestedExample.java @@ -0,0 +1,13 @@ +package com.trickl.assertj.examples; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class NestedExample { + @JsonProperty("first-example") + private Example firstExample; + + @JsonProperty("second-example") + private Example secondExample; +} diff --git a/src/test/resources/com/trickl/assertj/examples/NestedExample.schema.json b/src/test/resources/com/trickl/assertj/examples/NestedExample.schema.json new file mode 100644 index 0000000..7ae0a88 --- /dev/null +++ b/src/test/resources/com/trickl/assertj/examples/NestedExample.schema.json @@ -0,0 +1,14 @@ +{ + "id": "urn:jsonschema:com:trickl:assertj:examples:NestedExample", + "type": "object", + "properties": { + "second-example": { + "type": "object", + "$ref": "urn:jsonschema:com:trickl:assertj:examples:Example" + }, + "first-example": { + "type": "object", + "$ref": "urn:jsonschema:com:trickl:assertj:examples:Example" + } + } +} \ No newline at end of file diff --git a/src/test/resources/com/trickl/assertj/examples/NestedExample.schema2.json b/src/test/resources/com/trickl/assertj/examples/NestedExample.schema2.json new file mode 100644 index 0000000..d383b0b --- /dev/null +++ b/src/test/resources/com/trickl/assertj/examples/NestedExample.schema2.json @@ -0,0 +1,20 @@ +{ + "id": "urn:jsonschema:com:trickl:assertj:examples:NestedExample", + "type": "object", + "properties": { + "second-example": { + "type": "object", + "$ref": "urn:jsonschema:com:trickl:assertj:examples:Example" + }, + "first-example": { + "id": "urn:jsonschema:com:trickl:assertj:examples:Example", + "type": "object", + "properties": { + "my-field": { + "description": "Description of my field", + "type": "string" + } + } + } + } +} \ No newline at end of file