diff --git a/.gitignore b/.gitignore index c3bde803..928d242b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ target *.ipr *.iws .idea +/bin/ diff --git a/pom.xml b/pom.xml index 25e8da84..d259401e 100644 --- a/pom.xml +++ b/pom.xml @@ -8,10 +8,10 @@ com.fasterxml.jackson.module jackson-module-jsonSchema jackson-module-jsonSchema - 2.9.3-SNAPSHOT + 4.0.0-SNAPSHOT bundle Add-on module for Jackson (http://jackson.codehaus.org) to support -JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 generation. +JSON Schema http://json-schema.org/ Currently v3 and start of v4 support. https://github.com/FasterXML/jackson-module-jsonSchema @@ -49,7 +49,19 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene javax.validation validation-api - 1.1.0.Final + 2.0.0.Final + + + org.hibernate + hibernate-validator + 6.0.4.Final + provided + + + org.glassfish + javax.el + 3.0.1-b08 + provided @@ -57,7 +69,12 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene junit test - + + org.hamcrest + hamcrest-all + 1.3 + test + diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java index 3461a314..015aad30 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchema.java @@ -1,12 +1,32 @@ package com.fasterxml.jackson.module.jsonSchema; -import com.fasterxml.jackson.annotation.*; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; -import com.fasterxml.jackson.module.jsonSchema.types.*; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.types.AnySchema; +import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; +import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ContainerTypeSchema; +import com.fasterxml.jackson.module.jsonSchema.types.IntegerSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NullSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; +import com.fasterxml.jackson.module.jsonSchema.types.UnionTypeSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ValueTypeSchema; /** * The type wraps the json schema specification at : @@ -33,7 +53,7 @@ * "id":{ * "type":"number", * "description":"Product identifier", - * "required":true + * "required":true * }, * "name":{ * "description":"Name of the product", @@ -73,6 +93,21 @@ @JsonTypeIdResolver(JsonSchemaIdResolver.class) public abstract class JsonSchema { + @JsonIgnore + protected JsonSchemaVersion version; + + protected JsonSchema() { + //jackson deserialization only + } + + protected JsonSchema(JsonSchemaVersion version) { + this.version = version; + } + + protected JsonSchema(JsonSchemaVersion version, boolean set$Schema) { + this.version = version; + } + /** * This attribute defines the current URI of this schema (this attribute is * effectively a "self" link). This URI MAY be relative or absolute. If the @@ -141,11 +176,15 @@ public abstract class JsonSchema */ private JsonSchema[] extendsextends; - /** - * This attribute indicates if the instance must have a value, and not be - * undefined. This is false by default, making the instance optional. - */ - @JsonProperty + /** + * This attribute indicates if the instance must have a value, and not be + * undefined. This is false by default, making the instance optional. + * Available in Draft V3 spec ONLY. + * + * @deprecated Since 2.9 - Use setRequired on ObjectSchema from Draft V4 onwards. + */ + @Deprecated + @JsonProperty private Boolean required = null; /** @@ -161,7 +200,10 @@ public abstract class JsonSchema */ private String description; - protected JsonSchema() { } + /** + * Map to hold items that are not part of the official spec but may want to be added. + */ + private Map nonStandardProperties = new LinkedHashMap<>(); /** * Attempt to return this JsonSchema as an {@link AnySchema} @@ -279,7 +321,11 @@ public ValueTypeSchema asValueTypeSchema() { return null; } - public String getId() { + public JsonSchemaVersion getVersion() { + return version; + } + + public String getId() { return id; } @@ -311,6 +357,15 @@ public String getDescription() { return description; } + @JsonAnyGetter + public Map getNonStandardProperties() { + return nonStandardProperties; + } + + public String getNonStandardProperty(String propertyName) { + return nonStandardProperties.get(propertyName); + } + @JsonIgnore public abstract JsonFormatTypes getType(); @@ -441,6 +496,9 @@ public boolean isValueTypeSchema() { public void set$schema(String $schema) { this.$schema = $schema; + if (version == null) { + this.version = JsonSchemaVersion.fromSchemaString($schema).orElse(null); + } } public void setDisallow(JsonSchema[] disallow) { @@ -456,6 +514,9 @@ public void setId(String id) { } public void setRequired(Boolean required) { + if (!JsonSchemaVersion.DRAFT_V3.equals(version)) { + throw new RuntimeException("You can only set the required boolean on Draft V3. You have: " + version); + } this.required = required; } @@ -467,6 +528,11 @@ public void setDescription(String description) { this.description = description; } + @JsonAnySetter + public void addNonStandardProperty(String key, String value) { + nonStandardProperties.put(key, value); + } + /** * Override this to add information specific to the property of bean * For example, bean validation annotations could be used to specify @@ -478,33 +544,34 @@ public void enrichWithBeanProperty(BeanProperty beanProperty) { } /** - * Create a schema which verifies only that an object is of the given format. - * @param format the format to expect - * @return the schema verifying the given format - */ - public static JsonSchema minimalForFormat(JsonFormatTypes format) + * Create a schema which verifies only that an object is of the given format. + * @param jsonVersion + * @param format the format to expect + * @return the schema verifying the given format + */ + public static JsonSchema minimalForFormat(JsonSchemaVersion jsonVersion, JsonFormatTypes format) { if (format != null) { switch (format) { case ARRAY: - return new ArraySchema(); + return new ArraySchema(jsonVersion); case OBJECT: - return new ObjectSchema(); + return new ObjectSchema(jsonVersion); case BOOLEAN: - return new BooleanSchema(); + return new BooleanSchema(jsonVersion); case INTEGER: - return new IntegerSchema(); + return new IntegerSchema(jsonVersion); case NUMBER: - return new NumberSchema(); + return new NumberSchema(jsonVersion); case STRING: - return new StringSchema(); + return new StringSchema(jsonVersion); case NULL: - return new NullSchema(); + return new NullSchema(jsonVersion); case ANY: default: } } - return new AnySchema(); + return new AnySchema(jsonVersion); } @Override @@ -522,7 +589,7 @@ protected boolean _equals(JsonSchema that) // 27-Apr-2015, tatu: Should not need to check type explicitly // && equals(getType(), getType()) - && equals(getRequired(), that.getRequired()) + && ((JsonSchemaVersion.DRAFT_V3.equals(version)) ? equals(getRequired(), that.getRequired()) : true) && equals(getReadonly(), that.getReadonly()) && equals(get$ref(), that.get$ref()) && equals(get$schema(), that.get$schema()) diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/JsonSchemaTitle.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/JsonSchemaTitle.java new file mode 100755 index 00000000..86c6df90 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/JsonSchemaTitle.java @@ -0,0 +1,13 @@ +package com.fasterxml.jackson.module.jsonSchema.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ METHOD, FIELD, PARAMETER, TYPE }) +@Retention(RUNTIME) +public @interface JsonSchemaTitle { + String value(); +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperties.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperties.java new file mode 100644 index 00000000..7622e49d --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperties.java @@ -0,0 +1,21 @@ +package com.fasterxml.jackson.module.jsonSchema.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Meant to add items to the schema that are not currently part of the schema. + * + * @author amerritt + */ +@Target({ METHOD, FIELD, PARAMETER, TYPE }) +@Retention(RUNTIME) +public @interface NonStandardProperties { + NonStandardProperty[] value(); +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperty.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperty.java new file mode 100644 index 00000000..5799ccb9 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/annotation/NonStandardProperty.java @@ -0,0 +1,24 @@ +package com.fasterxml.jackson.module.jsonSchema.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Meant to add items to the schema that are not currently part of the schema. + * + * @author amerritt + */ +@Target({ METHOD, FIELD, PARAMETER, TYPE }) +@Retention(RUNTIME) +@Repeatable(NonStandardProperties.class) +public @interface NonStandardProperty { + String name(); + String value(); +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/HyperSchemaFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/HyperSchemaFactoryWrapper.java index 86863871..3cd96fdf 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/HyperSchemaFactoryWrapper.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/HyperSchemaFactoryWrapper.java @@ -9,7 +9,12 @@ import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.fasterxml.jackson.module.jsonSchema.annotation.JsonHyperSchema; import com.fasterxml.jackson.module.jsonSchema.annotation.Link; -import com.fasterxml.jackson.module.jsonSchema.factories.*; +import com.fasterxml.jackson.module.jsonSchema.factories.ArrayVisitor; +import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitor; +import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.LinkDescriptionObject; import com.fasterxml.jackson.module.jsonSchema.types.ReferenceSchema; import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema; @@ -23,27 +28,37 @@ public class HyperSchemaFactoryWrapper extends SchemaFactoryWrapper { private boolean ignoreDefaults = true; + private JsonSchemaVersion version; private static class HyperSchemaFactoryWrapperFactory extends WrapperFactory { + public HyperSchemaFactoryWrapperFactory(JsonSchemaVersion version) { + super(version); + } + @Override public SchemaFactoryWrapper getWrapper(SerializerProvider p) { - return new HyperSchemaFactoryWrapper(p); + HyperSchemaFactoryWrapper hsfw = new HyperSchemaFactoryWrapper(); + hsfw.setProvider(p); + return hsfw; }; @Override public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) { - return new HyperSchemaFactoryWrapper(p) - .setVisitorContext(rvc); + HyperSchemaFactoryWrapper hsfw = new HyperSchemaFactoryWrapper(); + hsfw.setVisitorContext(rvc); + hsfw.setProvider(p); + return hsfw; } }; public HyperSchemaFactoryWrapper() { - super(new HyperSchemaFactoryWrapperFactory()); + this(JsonSchemaVersion.DRAFT_V3); } - public HyperSchemaFactoryWrapper(SerializerProvider p) { - super(p, new HyperSchemaFactoryWrapperFactory()); + public HyperSchemaFactoryWrapper(JsonSchemaVersion version) { + super(new HyperSchemaFactoryWrapperFactory(version)); + this.version = version; } @Override @@ -112,7 +127,7 @@ private JsonSchema fetchSchema(Class targetSchema) { if (visitorContext != null) { String seenSchemaUri = visitorContext.getSeenSchemaUri(targetType); if (seenSchemaUri != null) { - return new ReferenceSchema(seenSchemaUri); + return new ReferenceSchema(version, seenSchemaUri); } } HyperSchemaFactoryWrapper targetVisitor = new HyperSchemaFactoryWrapper(); diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/SchemaPropertyProcessorManagerFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/SchemaPropertyProcessorManagerFactoryWrapper.java new file mode 100644 index 00000000..accbcc3b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/SchemaPropertyProcessorManagerFactoryWrapper.java @@ -0,0 +1,93 @@ +package com.fasterxml.jackson.module.jsonSchema.customProperties; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitor; +import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitorDecorator; +import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManagerApi; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; + +/** + * This class provides a wrapper around the SchemaFactoryWrapper that delegates + * to the provided implementation of SchemaPropertyProcessorManagerApi to process + * each property of type object elements. + * + * @author amerritt + */ +public class SchemaPropertyProcessorManagerFactoryWrapper extends SchemaFactoryWrapper { + + private SchemaPropertyProcessorManagerApi schemaPropertyProcessorManager; + + public SchemaPropertyProcessorManagerFactoryWrapper(SchemaPropertyProcessorManagerApi schemaPropertyProcessorManager) { + this(schemaPropertyProcessorManager, JsonSchemaVersion.DRAFT_V4); + } + + public SchemaPropertyProcessorManagerFactoryWrapper(SchemaPropertyProcessorManagerApi schemaPropertyProcessorManager, JsonSchemaVersion version) { + super(new SchemaPropertyProcessorManagerFactory(schemaPropertyProcessorManager, version)); + this.schemaPropertyProcessorManager = schemaPropertyProcessorManager; + } + + @Override + public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { + ObjectVisitor objectVisitor = (ObjectVisitor) super.expectObjectFormat(convertedType); + schemaPropertyProcessorManager.process(objectVisitor.getSchema(), convertedType); + return new ObjectVisitorDecorator(objectVisitor) { + private JsonSchema getPropertySchema(BeanProperty writer) { + return ((ObjectSchema) getSchema()).getProperties().get(writer.getName()); + } + + @Override + public void optionalProperty(BeanProperty writer) throws JsonMappingException { + super.optionalProperty(writer); + schemaPropertyProcessorManager.process(getPropertySchema(writer), writer); + } + + @Override + public void property(BeanProperty writer) throws JsonMappingException { + super.property(writer); + schemaPropertyProcessorManager.process(getPropertySchema(writer), writer); + } + }; + } + + private static class SchemaPropertyProcessorManagerFactory extends WrapperFactory { + SchemaPropertyProcessorManagerApi schemaPropertyProcessorManager; + + SchemaPropertyProcessorManagerFactory(SchemaPropertyProcessorManagerApi schemaPropertyProcessorManager, JsonSchemaVersion version) { + super(version); + this.schemaPropertyProcessorManager = schemaPropertyProcessorManager; + } + + @Override + public SchemaFactoryWrapper getWrapper(SerializerProvider p) { + SchemaFactoryWrapper wrapper = new SchemaPropertyProcessorManagerFactoryWrapper(schemaPropertyProcessorManager, getVersion()); + wrapper.setProvider(p); + return wrapper; + } + + @Override + public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) { + SchemaFactoryWrapper wrapper = new SchemaPropertyProcessorManagerFactoryWrapper(schemaPropertyProcessorManager, getVersion()); + wrapper.setProvider(p); + wrapper.setVisitorContext(rvc); + return wrapper; + } + + @Override + public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc, ObjectSchema parent, Class type) { + SchemaFactoryWrapper wrapper = new SchemaPropertyProcessorManagerFactoryWrapper(schemaPropertyProcessorManager.createCopyForType(type), getVersion()); + wrapper.setProvider(p); + wrapper.setVisitorContext(rvc); + wrapper.setParent(parent); + return wrapper; + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaFactoryWrapper.java deleted file mode 100644 index 37847080..00000000 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaFactoryWrapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.fasterxml.jackson.module.jsonSchema.customProperties; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.module.jsonSchema.JsonSchema; -import com.fasterxml.jackson.module.jsonSchema.factories.*; - -/** - * Adds a title to every object schema, either root level or nested. Generally - * useful for writing additional properties to a schema. - * - * @author jphelan - */ -public class TitleSchemaFactoryWrapper extends SchemaFactoryWrapper -{ - private static class TitleSchemaFactoryWrapperFactory extends WrapperFactory { - @Override - public SchemaFactoryWrapper getWrapper(SerializerProvider p) { - SchemaFactoryWrapper wrapper = new TitleSchemaFactoryWrapper(); - if (p != null) { - wrapper.setProvider(p); - } - return wrapper; - }; - - @Override - public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) { - SchemaFactoryWrapper wrapper = new TitleSchemaFactoryWrapper(); - if (p != null) { - wrapper.setProvider(p); - } - wrapper.setVisitorContext(rvc); - return wrapper; - } - }; - - public TitleSchemaFactoryWrapper() { - super(new TitleSchemaFactoryWrapperFactory()); - } - - @Override - public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { - ObjectVisitor visitor = ((ObjectVisitor)super.expectObjectFormat(convertedType)); - - // could add other properties here - addTitle(visitor.getSchema(), convertedType); - - return visitor; - } - - @Override - public JsonArrayFormatVisitor expectArrayFormat(JavaType convertedType) { - ArrayVisitor visitor = ((ArrayVisitor)super.expectArrayFormat(convertedType)); - - // could add other properties here - addTitle(visitor.getSchema(), convertedType); - - return visitor; - } - - /** - * Adds writes the type as the title of the schema. - * - * @param schema The schema who's title to set. - * @param type The type of the object represented by the schema. - */ - private void addTitle(JsonSchema schema, JavaType type) - { - if (!schema.isSimpleTypeSchema()) { - throw new RuntimeException("given non simple type schema: " + schema.getType()); - } - schema.asSimpleTypeSchema().setTitle(type.getGenericSignature()); - } -} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaPropertyProcessorManagerFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaPropertyProcessorManagerFactoryWrapper.java new file mode 100644 index 00000000..4ef7e820 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/TitleSchemaPropertyProcessorManagerFactoryWrapper.java @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.module.jsonSchema.customProperties; + +import java.util.Arrays; + +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyProcessorTitle; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManager; + +/** + * Adds a title to any object annotated with JsonSchemaTitle, either root level or nested. + * + * @author amerritt + */ +public class TitleSchemaPropertyProcessorManagerFactoryWrapper extends SchemaPropertyProcessorManagerFactoryWrapper +{ + public TitleSchemaPropertyProcessorManagerFactoryWrapper() { + this(JsonSchemaVersion.DRAFT_V3); + } + + public TitleSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion version) { + super(new SchemaPropertyProcessorManager(Arrays.asList(new SchemaPropertyProcessorTitle())), version); + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java deleted file mode 100644 index 02f2882a..00000000 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.fasterxml.jackson.module.jsonSchema.customProperties; - -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.module.jsonSchema.JsonSchema; -import com.fasterxml.jackson.module.jsonSchema.factories.*; -import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; -import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; -import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; -import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; -import com.fasterxml.jackson.module.jsonSchema.validation.AnnotationConstraintResolver; -import com.fasterxml.jackson.module.jsonSchema.validation.ValidationConstraintResolver; - -/** - * @author cponomaryov - */ -public class ValidationSchemaFactoryWrapper extends SchemaFactoryWrapper { - - private ValidationConstraintResolver constraintResolver; - - private static class ValidationSchemaFactoryWrapperFactory extends WrapperFactory { - @Override - public SchemaFactoryWrapper getWrapper(SerializerProvider p) { - SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper(); - wrapper.setProvider(p); - return wrapper; - } - - @Override - public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) { - SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper(); - wrapper.setProvider(p); - wrapper.setVisitorContext(rvc); - return wrapper; - } - } - - public ValidationSchemaFactoryWrapper() { - this(new AnnotationConstraintResolver()); - } - - public ValidationSchemaFactoryWrapper(ValidationConstraintResolver constraintResolver) { - super(new ValidationSchemaFactoryWrapperFactory()); - this.constraintResolver = constraintResolver; - } - - @Override - public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { - return new ObjectVisitorDecorator((ObjectVisitor) super.expectObjectFormat(convertedType)) { - private JsonSchema getPropertySchema(BeanProperty writer) { - return ((ObjectSchema) getSchema()).getProperties().get(writer.getName()); - } - - @Override - public void optionalProperty(BeanProperty writer) throws JsonMappingException { - super.optionalProperty(writer); - addValidationConstraints(getPropertySchema(writer), writer); - } - - @Override - public void property(BeanProperty writer) throws JsonMappingException { - super.property(writer); - addValidationConstraints(getPropertySchema(writer), writer); - } - }; - } - - protected JsonSchema addValidationConstraints(JsonSchema schema, BeanProperty prop) { - { - Boolean required = constraintResolver.getRequired(prop); - if (required != null) { - schema.setRequired(required); - } - } - if (schema.isArraySchema()) { - ArraySchema arraySchema = schema.asArraySchema(); - arraySchema.setMaxItems(constraintResolver.getArrayMaxItems(prop)); - arraySchema.setMinItems(constraintResolver.getArrayMinItems(prop)); - } else if (schema.isNumberSchema()) { - NumberSchema numberSchema = schema.asNumberSchema(); - numberSchema.setMaximum(constraintResolver.getNumberMaximum(prop)); - numberSchema.setMinimum(constraintResolver.getNumberMinimum(prop)); - } else if (schema.isStringSchema()) { - StringSchema stringSchema = schema.asStringSchema(); - stringSchema.setMaxLength(constraintResolver.getStringMaxLength(prop)); - stringSchema.setMinLength(constraintResolver.getStringMinLength(prop)); - stringSchema.setPattern(constraintResolver.getStringPattern(prop)); - } - return schema; - } - -} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaPropertyProcessorManagerFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaPropertyProcessorManagerFactoryWrapper.java new file mode 100644 index 00000000..89e47b44 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaPropertyProcessorManagerFactoryWrapper.java @@ -0,0 +1,17 @@ +package com.fasterxml.jackson.module.jsonSchema.customProperties; + +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManagerConstraint; + +/** + * @author amerritt + */ +public class ValidationSchemaPropertyProcessorManagerFactoryWrapper extends SchemaPropertyProcessorManagerFactoryWrapper { + public ValidationSchemaPropertyProcessorManagerFactoryWrapper(Class type, Class... groups) { + super(new SchemaPropertyProcessorManagerConstraint(type, groups)); + } + + public ValidationSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion version, Class type, Class... groups) { + super(new SchemaPropertyProcessorManagerConstraint(type, groups), version); + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ArrayVisitor.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ArrayVisitor.java index f5b427c5..6eec6d83 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ArrayVisitor.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ArrayVisitor.java @@ -1,6 +1,8 @@ package com.fasterxml.jackson.module.jsonSchema.factories; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; @@ -19,10 +21,6 @@ public class ArrayVisitor extends JsonArrayFormatVisitor.Base private VisitorContext visitorContext; - public ArrayVisitor(SerializerProvider provider, ArraySchema schema) { - this(provider, schema, new WrapperFactory()); - } - public ArrayVisitor(SerializerProvider provider, ArraySchema schema, WrapperFactory wrapperFactory) { this.provider = provider; this.schema = schema; @@ -75,7 +73,7 @@ public void itemsFormat(JsonFormatVisitable handler, JavaType contentType) if (visitorContext != null) { String seenSchemaUri = visitorContext.getSeenSchemaUri(contentType); if (seenSchemaUri != null) { - schema.setItemsSchema(new ReferenceSchema(seenSchemaUri)); + schema.setItemsSchema(new ReferenceSchema(schema.getVersion(), seenSchemaUri)); return; } } @@ -89,7 +87,7 @@ public void itemsFormat(JsonFormatVisitable handler, JavaType contentType) @Override public void itemsFormat(JsonFormatTypes format) throws JsonMappingException { - schema.setItemsSchema(JsonSchema.minimalForFormat(format)); + schema.setItemsSchema(JsonSchema.minimalForFormat(schema.getVersion(), format)); } @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/FormatVisitorFactory.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/FormatVisitorFactory.java index 6a3a410d..f7b258c3 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/FormatVisitorFactory.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/FormatVisitorFactory.java @@ -1,9 +1,23 @@ package com.fasterxml.jackson.module.jsonSchema.factories; import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.*; - -import com.fasterxml.jackson.module.jsonSchema.types.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonAnyFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonBooleanFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNullFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import com.fasterxml.jackson.module.jsonSchema.types.AnySchema; +import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; +import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema; +import com.fasterxml.jackson.module.jsonSchema.types.IntegerSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NullSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; /** * Factory class used for constructing visitors for building various @@ -13,10 +27,6 @@ public class FormatVisitorFactory { private final WrapperFactory wrapperFactory; - public FormatVisitorFactory() { - this(new WrapperFactory()); - } - public FormatVisitorFactory(WrapperFactory wrapperFactory) { this.wrapperFactory = wrapperFactory; } diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/JsonSchemaFactory.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/JsonSchemaFactory.java index 4f550b48..df3c607b 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/JsonSchemaFactory.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/JsonSchemaFactory.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.module.jsonSchema.factories; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.AnySchema; import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema; @@ -7,39 +8,57 @@ import com.fasterxml.jackson.module.jsonSchema.types.NullSchema; import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema; import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; public class JsonSchemaFactory { - public AnySchema anySchema() { - return new AnySchema(); + private JsonSchemaVersion version; + + //NOTE: you could use the version to return different version but since Required went from a boolean to a List + // it would require maintaining to complete sets of classes. Not worth the time and effort at this point. + public JsonSchemaFactory(JsonSchemaVersion version) { + this.version = version; + } + + public AnySchema anySchema(ObjectSchema parent) { + return setParent(new AnySchema(version), parent); + } + + public ArraySchema arraySchema(ObjectSchema parent) { + return setParent(new ArraySchema(version), parent); + } + + public BooleanSchema booleanSchema(ObjectSchema parent) { + return setParent(new BooleanSchema(version), parent); } - public ArraySchema arraySchema() { - return new ArraySchema(); + public IntegerSchema integerSchema(ObjectSchema parent) { + return setParent(new IntegerSchema(version), parent); } - public BooleanSchema booleanSchema() { - return new BooleanSchema(); + public NullSchema nullSchema(ObjectSchema parent) { + return setParent(new NullSchema(version), parent); } - public IntegerSchema integerSchema() { - return new IntegerSchema(); + public NumberSchema numberSchema(ObjectSchema parent) { + return setParent(new NumberSchema(version), parent); } - public NullSchema nullSchema() { - return new NullSchema(); + public ObjectSchema objectSchema(ObjectSchema parent) { + return setParent(new ObjectSchema(version), parent); } - public NumberSchema numberSchema() { - return new NumberSchema(); + public StringSchema stringSchema(ObjectSchema parent) { + return setParent(new StringSchema(version), parent); } - public ObjectSchema objectSchema() { - return new ObjectSchema(); + public String getSchemaString() { + return version.getSchemaString(); } - public StringSchema stringSchema() { - return new StringSchema(); + private T setParent(T schema, ObjectSchema parent) { + schema.setParent(parent); + return schema; } } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/MapVisitor.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/MapVisitor.java index 05cee9a8..2be38058 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/MapVisitor.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/MapVisitor.java @@ -25,10 +25,6 @@ public class MapVisitor extends JsonMapFormatVisitor.Base private VisitorContext visitorContext; - public MapVisitor(SerializerProvider provider, ObjectSchema schema) { - this(provider, schema, new WrapperFactory()); - } - public MapVisitor(SerializerProvider provider, ObjectSchema schema, WrapperFactory wrapperFactory) { this.provider = provider; this.schema = schema; @@ -86,7 +82,7 @@ protected JsonSchema propertySchema(JsonFormatVisitable handler, JavaType proper if (visitorContext != null) { String seenSchemaUri = visitorContext.getSeenSchemaUri(propertyTypeHint); if (seenSchemaUri != null) { - return new ReferenceSchema(seenSchemaUri); + return new ReferenceSchema(wrapperFactory.getVersion(), seenSchemaUri); } } diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitor.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitor.java index ad7d67f4..5481f2a5 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitor.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitor.java @@ -1,6 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema.factories; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; @@ -16,14 +20,6 @@ public class ObjectVisitor extends JsonObjectFormatVisitor.Base private WrapperFactory wrapperFactory; private VisitorContext visitorContext; - /** - * @deprecated Since 2.4; call constructor that takes {@link WrapperFactory} - */ - @Deprecated - public ObjectVisitor(SerializerProvider provider, ObjectSchema schema) { - this(provider, schema, new WrapperFactory()); - } - public ObjectVisitor(SerializerProvider provider, ObjectSchema schema, WrapperFactory wrapperFactory) { this.provider = provider; this.schema = schema; @@ -105,10 +101,10 @@ protected JsonSchema propertySchema(BeanProperty prop) // check if we've seen this argument's sub-schema already and return a reference-schema if we have String seenSchemaUri = visitorContext.getSeenSchemaUri(prop.getType()); if (seenSchemaUri != null) { - return new ReferenceSchema(seenSchemaUri); + return new ReferenceSchema(wrapperFactory.getVersion(), seenSchemaUri, schema); } - SchemaFactoryWrapper visitor = wrapperFactory.getWrapper(getProvider(), visitorContext); + SchemaFactoryWrapper visitor = wrapperFactory.getWrapper(getProvider(), visitorContext, schema, prop.getType().getRawClass()); JsonSerializer ser = getSer(prop); if (ser != null) { JavaType type = prop.getType(); @@ -127,7 +123,7 @@ protected JsonSchema propertySchema(JsonFormatVisitable handler, JavaType proper if (visitorContext != null) { String seenSchemaUri = visitorContext.getSeenSchemaUri(propertyTypeHint); if (seenSchemaUri != null) { - return new ReferenceSchema(seenSchemaUri); + return new ReferenceSchema(wrapperFactory.getVersion(), seenSchemaUri, schema); } } diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/SchemaFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/SchemaFactoryWrapper.java index ac44ce67..95afee51 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/SchemaFactoryWrapper.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/SchemaFactoryWrapper.java @@ -3,9 +3,26 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonAnyFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonBooleanFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNullFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; -import com.fasterxml.jackson.module.jsonSchema.types.*; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.types.AnySchema; +import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; +import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema; +import com.fasterxml.jackson.module.jsonSchema.types.IntegerSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NullSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; /** * @author jphelan @@ -18,13 +35,15 @@ public class SchemaFactoryWrapper implements JsonFormatVisitorWrapper, Visitor protected SerializerProvider provider; protected JsonSchema schema; protected VisitorContext visitorContext; + protected ObjectSchema parent; + protected Class type; - public SchemaFactoryWrapper() { - this(null, new WrapperFactory()); + public SchemaFactoryWrapper(JsonSchemaVersion version) { + this(null, new WrapperFactory(version)); } - public SchemaFactoryWrapper(SerializerProvider p) { - this(p, new WrapperFactory()); + public SchemaFactoryWrapper(SerializerProvider p, JsonSchemaVersion version) { + this(p, new WrapperFactory(version)); } protected SchemaFactoryWrapper(WrapperFactory wrapperFactory) { @@ -33,7 +52,7 @@ protected SchemaFactoryWrapper(WrapperFactory wrapperFactory) { protected SchemaFactoryWrapper(SerializerProvider p, WrapperFactory wrapperFactory) { provider = p; - schemaProvider = new JsonSchemaFactory(); + schemaProvider = new JsonSchemaFactory(wrapperFactory.getVersion()); visitorFactory = new FormatVisitorFactory(wrapperFactory); } @@ -55,49 +74,52 @@ public void setProvider(SerializerProvider p) { @Override public JsonAnyFormatVisitor expectAnyFormat(JavaType convertedType) { - AnySchema s = schemaProvider.anySchema(); + AnySchema s = schemaProvider.anySchema(parent); this.schema = s; return visitorFactory.anyFormatVisitor(s); } @Override public JsonArrayFormatVisitor expectArrayFormat(JavaType convertedType) { - ArraySchema s = schemaProvider.arraySchema(); + ArraySchema s = schemaProvider.arraySchema(parent); this.schema = s; return visitorFactory.arrayFormatVisitor(provider, s, visitorContext); } @Override public JsonBooleanFormatVisitor expectBooleanFormat(JavaType convertedType) { - BooleanSchema s = schemaProvider.booleanSchema(); + BooleanSchema s = schemaProvider.booleanSchema(parent); this.schema = s; return visitorFactory.booleanFormatVisitor(s); } @Override public JsonIntegerFormatVisitor expectIntegerFormat(JavaType convertedType) { - IntegerSchema s = schemaProvider.integerSchema(); + IntegerSchema s = schemaProvider.integerSchema(parent); this.schema = s; return visitorFactory.integerFormatVisitor(s); } @Override public JsonNullFormatVisitor expectNullFormat(JavaType convertedType) { - NullSchema s = schemaProvider.nullSchema(); + NullSchema s = schemaProvider.nullSchema(parent); schema = s; return visitorFactory.nullFormatVisitor(s); } @Override public JsonNumberFormatVisitor expectNumberFormat(JavaType convertedType) { - NumberSchema s = schemaProvider.numberSchema(); + NumberSchema s = schemaProvider.numberSchema(parent); schema = s; return visitorFactory.numberFormatVisitor(s); } @Override public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { - ObjectSchema s = schemaProvider.objectSchema(); + ObjectSchema s = schemaProvider.objectSchema(parent); + if (parent == null) { + s.set$schema(schemaProvider.getSchemaString()); + } schema = s; // if we don't already have a recursive visitor context, create one @@ -116,7 +138,7 @@ public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) { @Override public JsonStringFormatVisitor expectStringFormat(JavaType convertedType) { - StringSchema s = schemaProvider.stringSchema(); + StringSchema s = schemaProvider.stringSchema(parent); schema = s; return visitorFactory.stringFormatVisitor(s); } @@ -129,7 +151,7 @@ public JsonMapFormatVisitor expectMapFormat(JavaType type) * concept of Map (distinct from Record or Object); so best * we can do is to consider it a vague kind-a Object... */ - ObjectSchema s = schemaProvider.objectSchema(); + ObjectSchema s = schemaProvider.objectSchema(parent); schema = s; return visitorFactory.mapFormatVisitor(provider, s, visitorContext); } @@ -140,6 +162,16 @@ public SchemaFactoryWrapper setVisitorContext(VisitorContext rvc) { return this; } + public Visitor setParent(ObjectSchema parent) { + this.parent = parent; + return this; + } + + public Visitor setType(Class type) { + this.type = type; + return this; + } + /* /********************************************************************* /* API diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/WrapperFactory.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/WrapperFactory.java index 805aef28..5fba1ed8 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/WrapperFactory.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/WrapperFactory.java @@ -1,6 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema.factories; +import java.util.Arrays; +import java.util.Optional; + import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; /** * Exists to supply {@link SchemaFactoryWrapper} or its subclasses @@ -9,13 +13,51 @@ */ public class WrapperFactory { + public enum JsonSchemaVersion { + DRAFT_V3("http://json-schema.org/draft-03/schema#"), DRAFT_V4("http://json-schema.org/draft-04/schema#"); + private String schemaString; + + JsonSchemaVersion(String schemaString) { + this.schemaString = schemaString; + } + + public static Optional fromSchemaString(String schemaString) { + if (schemaString == null) { + return Optional.empty(); + } + return Arrays.stream(JsonSchemaVersion.values()).filter(jsv -> schemaString.equals(jsv.schemaString)).findFirst(); + } + + public String getSchemaString() { + return schemaString; + } + }; + + private JsonSchemaVersion version = JsonSchemaVersion.DRAFT_V4; + + public WrapperFactory(JsonSchemaVersion version) { + this.version = version; + } + public SchemaFactoryWrapper getWrapper(SerializerProvider provider) { - return new SchemaFactoryWrapper(provider); + return new SchemaFactoryWrapper(provider, version); } public SchemaFactoryWrapper getWrapper(SerializerProvider provider, VisitorContext rvc) { - SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper(provider); + SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper(provider, version); wrapper.setVisitorContext(rvc); return wrapper; } + + public SchemaFactoryWrapper getWrapper(SerializerProvider provider, VisitorContext rvc, ObjectSchema parent, Class type) { + SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper(provider, version); + wrapper.setVisitorContext(rvc); + wrapper.setParent(parent); + wrapper.setType(type); + return wrapper; + } + + public JsonSchemaVersion getVersion() { + return version; + } } \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyAnnotationProcessor.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyAnnotationProcessor.java new file mode 100644 index 00000000..15864fa6 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyAnnotationProcessor.java @@ -0,0 +1,51 @@ +package com.fasterxml.jackson.module.jsonSchema.property; + +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public abstract class SchemaPropertyAnnotationProcessor implements SchemaPropertyProcessor +{ + protected Class annotationClass; + + @SuppressWarnings("unchecked") + public SchemaPropertyAnnotationProcessor() { + annotationClass = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } + + @Override + public void process(JsonSchema schema, BeanProperty prop) { + T annotation = prop.getAnnotation(annotationClass); + processAnnotation(schema, annotation); + } + + + @Override + public void process(JsonSchema schema, JavaType type) { + T annotation = type.getRawClass().getAnnotation(annotationClass); + processAnnotation(schema, annotation); + } + + protected void processAnnotation(JsonSchema schema, T annotation) { + if (Objects.nonNull(annotation)) { + processNonNullAnnotation(schema, annotation); + } + } + + /** + * Method called when it finds the annotation. Will not be called if annotation is not found. + * + * @param schema - current schema + * @param annotation - will not be null + */ + protected abstract void processNonNullAnnotation(JsonSchema schema, T annotation); +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessor.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessor.java new file mode 100644 index 00000000..756b722b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessor.java @@ -0,0 +1,16 @@ +package com.fasterxml.jackson.module.jsonSchema.property; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public interface SchemaPropertyProcessor { + void process(JsonSchema schema, BeanProperty prop); + + void process(JsonSchema schema, JavaType type); +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessorTitle.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessorTitle.java new file mode 100644 index 00000000..e91f0991 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/SchemaPropertyProcessorTitle.java @@ -0,0 +1,21 @@ +package com.fasterxml.jackson.module.jsonSchema.property; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.annotation.JsonSchemaTitle; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorTitle extends SchemaPropertyAnnotationProcessor +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, JsonSchemaTitle titleAnnotation) { + if (schema.isSimpleTypeSchema()) { + String title = titleAnnotation.value(); + schema.asSimpleTypeSchema().setTitle(title); + } + + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraint.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraint.java new file mode 100644 index 00000000..64494c00 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraint.java @@ -0,0 +1,49 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyAnnotationProcessor; + +/** + * @author amerritt + * + * @since 4.0 + */ +public abstract class SchemaPropertyProcessorConstraint extends SchemaPropertyAnnotationProcessor { + Map> propertyConstraints; + String propertyName; + + public void setPropertyConstraints(Map> propertyConstraints) { + this.propertyConstraints = propertyConstraints; + } + + @Override + public void process(JsonSchema schema, JavaType type) { + //Currently not processing constraints on the main class + } + + @Override + public void process(JsonSchema schema, BeanProperty prop) { + propertyName = prop.getName(); + processAnnotation(schema, getAnnotation(prop, annotationClass)); + } + + @SuppressWarnings("unchecked") + T getAnnotation(BeanProperty prop, Class type) { + if (propertyConstraints != null) { + return (T)emptyIfNull(propertyConstraints.get(prop.getName())).stream().filter(a -> type.isInstance(a)).findFirst().orElse(null); + } else { + return prop.getAnnotation(type); + } + } + + public static List emptyIfNull(final List list) { + return list == null ? new ArrayList() : list; + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMax.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMax.java new file mode 100644 index 00000000..4d79db3e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMax.java @@ -0,0 +1,24 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import java.math.BigDecimal; + +import javax.validation.constraints.DecimalMax; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintDecimalMax extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, DecimalMax decimalMaxAnnotation) { + if (schema.isNumberSchema()) { + NumberSchema numberSchema = schema.asNumberSchema(); + numberSchema.setMaximum(new BigDecimal(decimalMaxAnnotation.value()).doubleValue()); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMin.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMin.java new file mode 100644 index 00000000..d8c92ca3 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintDecimalMin.java @@ -0,0 +1,25 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import java.math.BigDecimal; + +import javax.validation.constraints.DecimalMin; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintDecimalMin extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, DecimalMin decimalMinAnnotation) { + if (schema.isNumberSchema()) { + NumberSchema numberSchema = schema.asNumberSchema(); + numberSchema.setMinimum(new BigDecimal(decimalMinAnnotation.value()).doubleValue()); + } + } + +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMax.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMax.java new file mode 100644 index 00000000..92fa54d9 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMax.java @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import javax.validation.constraints.Max; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintMax extends SchemaPropertyProcessorConstraint +{ + + @Override + protected void processNonNullAnnotation(JsonSchema schema, Max maxAnnotation) { + if (schema.isNumberSchema()) { + NumberSchema numberSchema = schema.asNumberSchema(); + numberSchema.setMaximum((double)maxAnnotation.value()); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMin.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMin.java new file mode 100644 index 00000000..967f1d6d --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintMin.java @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import javax.validation.constraints.Min; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintMin extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, Min minAnnotation) { + if (schema.isNumberSchema()) { + NumberSchema numberSchema = schema.asNumberSchema(); + numberSchema.setMinimum((double)minAnnotation.value()); + } + } + +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintNotNull.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintNotNull.java new file mode 100644 index 00000000..d60c889b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintNotNull.java @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintNotNull extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, NotNull annotation) { + if (schema.isSimpleTypeSchema()) { + SimpleTypeSchema sts = schema.asSimpleTypeSchema(); + if (JsonSchemaVersion.DRAFT_V3.equals(schema.getVersion())) { + schema.setRequired(true); + } else { + ObjectSchema parent = sts.getParent(); + parent.getRequiredPropertyNames().add(propertyName); + } + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintPattern.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintPattern.java new file mode 100644 index 00000000..fadd4dd9 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintPattern.java @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import javax.validation.constraints.Pattern; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintPattern extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, Pattern patternAnnotation) { + if (schema.isStringSchema()) { + StringSchema stringSchema = schema.asStringSchema(); + stringSchema.setPattern(patternAnnotation.regexp()); + } + } + +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintSize.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintSize.java new file mode 100644 index 00000000..9bf36230 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/constraint/SchemaPropertyProcessorConstraintSize.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.module.jsonSchema.property.constraint; + +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorConstraintSize extends SchemaPropertyProcessorConstraint +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, Size sizeAnnotation) { + if (schema.isArraySchema()) { + ArraySchema arraySchema = schema.asArraySchema(); + arraySchema.setMaxItems(getMaxSize(sizeAnnotation)); + arraySchema.setMinItems(getMinSize(sizeAnnotation)); + } else if (schema.isStringSchema()) { + StringSchema stringSchema = schema.asStringSchema(); + stringSchema.setMaxLength(getMaxSize(sizeAnnotation)); + stringSchema.setMinLength(getMinSize(sizeAnnotation)); + } + } + + private Integer getMaxSize(Size ann) { + if (ann != null) { + int value = ann.max(); + if (value != Integer.MAX_VALUE) { + return value; + } + } + return null; + } + + private Integer getMinSize(Size ann) { + if (ann != null) { + int value = ann.min(); + if (value != 0) { + return value; + } + } + return null; + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManager.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManager.java new file mode 100644 index 00000000..0435fde0 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManager.java @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.module.jsonSchema.property.manager; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyProcessor; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorManager implements SchemaPropertyProcessorManagerApi +{ + private List processors = new ArrayList<>(); + + public SchemaPropertyProcessorManager() { + + } + + public SchemaPropertyProcessorManager(List processors) { + this.processors = processors; + } + + @Override + public void registerSchemaPropertyProcessor(SchemaPropertyProcessor processor) { + getProcessors().add(processor); + } + + @Override + public void process(JsonSchema schema, BeanProperty prop) { + getProcessors().forEach(processor -> processor.process(schema, prop)); + } + + @Override + public void process(JsonSchema schema, JavaType type) { + getProcessors().forEach(processor -> processor.process(schema, type)); + } + + @Override + public List getProcessors() { + return processors; + } + + @Override + public void setProcessors(List processors) { + this.processors = processors; + } + + /* (non-Javadoc) + * Not doing anything special per new type for this so we just return the same instance. + * @see com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManagerApi#createCopyForType(java.lang.Class) + */ + @Override + public SchemaPropertyProcessorManagerApi createCopyForType(Class type) { + return this; + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerApi.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerApi.java new file mode 100644 index 00000000..dfa4c755 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerApi.java @@ -0,0 +1,17 @@ +package com.fasterxml.jackson.module.jsonSchema.property.manager; + +import java.util.List; + +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyProcessor; + +public interface SchemaPropertyProcessorManagerApi extends SchemaPropertyProcessor { + + void registerSchemaPropertyProcessor(SchemaPropertyProcessor processor); + + List getProcessors(); + + void setProcessors(List processors); + + SchemaPropertyProcessorManagerApi createCopyForType(Class type); + +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerConstraint.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerConstraint.java new file mode 100644 index 00000000..f975367b --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/manager/SchemaPropertyProcessorManagerConstraint.java @@ -0,0 +1,103 @@ +package com.fasterxml.jackson.module.jsonSchema.property.manager; + +import static java.util.stream.Collectors.toMap; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.metadata.BeanDescriptor; +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.PropertyDescriptor; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyProcessor; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraint; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintDecimalMax; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintDecimalMin; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintMax; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintMin; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintPattern; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintNotNull; +import com.fasterxml.jackson.module.jsonSchema.property.constraint.SchemaPropertyProcessorConstraintSize; + +/** + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorManagerConstraint extends SchemaPropertyProcessorManager { + private Class[] groups = new Class[0]; + + private Map> propertyConstraints; + + public SchemaPropertyProcessorManagerConstraint(Class type, Class... groups) { + this.groups = groups; + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintNotNull()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintSize()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintPattern()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintMin()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintDecimalMin()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintMax()); + registerSchemaPropertyProcessor(new SchemaPropertyProcessorConstraintDecimalMax()); + init(type, groups); + } + + protected SchemaPropertyProcessorManagerConstraint(Class type, List processors, Class... groups) { + super(processors); + this.groups = groups; + init(type, groups); + } + + @Override + public SchemaPropertyProcessorManagerApi createCopyForType(Class type) { + return new SchemaPropertyProcessorManagerConstraint(type, this.getProcessors(), groups); + } + + /** + * Get all the constraints for the type for the given groups. These will then be passed + * to the constraint processors so they don't each have to look them up every time. + * + * @param type - Current class to get constraints from. + * @param groups - Active groups for constraints. + */ + private void init(Class type, Class... groups) { + final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + BeanDescriptor beanDescriptor = validator.getConstraintsForClass(type); + propertyConstraints = beanDescriptor.getConstrainedProperties().stream().collect(toMap(pd -> pd.getPropertyName(), propertyDescriptor -> processPropertDescriptor(propertyDescriptor, groups))); + } + + @Override + public void process(JsonSchema schema, BeanProperty prop) { + getProcessors().forEach(processor -> { + if (processor instanceof SchemaPropertyProcessorConstraint) { + ((SchemaPropertyProcessorConstraint)processor).setPropertyConstraints(propertyConstraints); + } + processor.process(schema, prop); + }); + } + + protected List processPropertDescriptor(PropertyDescriptor propertyDescriptor, Class... groups) { + Set> descriptorsForGroup = propertyDescriptor.findConstraints().unorderedAndMatchingGroups(groups).getConstraintDescriptors(); + List propertyConstraintAnnotations = new ArrayList<>(); + for (ConstraintDescriptor constraintDescriptor : descriptorsForGroup) { + processNestedDescriptors(constraintDescriptor, propertyConstraintAnnotations); + } + return propertyConstraintAnnotations; + } + + protected void processNestedDescriptors(ConstraintDescriptor constraintDescriptor, List propertyConstraintAnnotations) { + Set> composingConstraints = constraintDescriptor.getComposingConstraints(); + if (composingConstraints != null && composingConstraints.size() > 0) { + for (ConstraintDescriptor constraintDescriptor2 : composingConstraints) { + processNestedDescriptors(constraintDescriptor2, propertyConstraintAnnotations); + } + } + propertyConstraintAnnotations.add(constraintDescriptor.getAnnotation()); + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperties.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperties.java new file mode 100644 index 00000000..50316222 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperties.java @@ -0,0 +1,22 @@ +package com.fasterxml.jackson.module.jsonSchema.property.nonstandard; + +import java.util.Arrays; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.annotation.NonStandardProperties; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyAnnotationProcessor; + +/** + * Used to add items to the schema that may not yet or even may never be part of the standard. + * + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorNonStandardProperties extends SchemaPropertyAnnotationProcessor +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, NonStandardProperties nonStandardPropertiesAnnotation) { + Arrays.stream(nonStandardPropertiesAnnotation.value()).forEach(nonStandardPropertyAnnotation -> schema.addNonStandardProperty(nonStandardPropertyAnnotation.name(), nonStandardPropertyAnnotation.value())); + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperty.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperty.java new file mode 100644 index 00000000..b354a9ff --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/property/nonstandard/SchemaPropertyProcessorNonStandardProperty.java @@ -0,0 +1,20 @@ +package com.fasterxml.jackson.module.jsonSchema.property.nonstandard; + +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.annotation.NonStandardProperty; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyAnnotationProcessor; + +/** + * Used to add items to the schema that may not yet or even may never be part of the standard. + * + * @author amerritt + * + * @since 4.0 + */ +public class SchemaPropertyProcessorNonStandardProperty extends SchemaPropertyAnnotationProcessor +{ + @Override + protected void processNonNullAnnotation(JsonSchema schema, NonStandardProperty nonStandardPropertyAnnotation) { + schema.addNonStandardProperty(nonStandardPropertyAnnotation.name(), nonStandardPropertyAnnotation.value()); + } +} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/AnySchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/AnySchema.java index aa43b865..4f5d2554 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/AnySchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/AnySchema.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} of type any @@ -9,9 +10,16 @@ */ public class AnySchema extends SimpleTypeSchema { - public AnySchema() { } + protected AnySchema() { + //jackson deserialization only + super(); + } - @Override + public AnySchema(JsonSchemaVersion version) { + super(version); + } + + @Override public AnySchema asAnySchema() { return this; } @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ArraySchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ArraySchema.java index 1f54ee4d..d6f1e57c 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ArraySchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ArraySchema.java @@ -2,16 +2,16 @@ import java.io.IOException; -import com.fasterxml.jackson.annotation.*; - -import com.fasterxml.jackson.core.*; - -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /* * This attribute defines the allowed items in an instance array, and @@ -20,9 +20,18 @@ */ public class ArraySchema extends ContainerTypeSchema { - /** - * see {@link AdditionalItems} - */ + private ArraySchema() { + //jackson deserialization only + super(); + } + + public ArraySchema(JsonSchemaVersion version) { + super(version); + } + + /** + * see {@link AdditionalItems} + */ @JsonProperty protected ArraySchema.AdditionalItems additionalItems; @@ -155,7 +164,7 @@ public static class ArrayItems extends ArraySchema.Items { @JsonProperty private JsonSchema[] jsonSchemas; - public ArrayItems(JsonSchema[] jsonSchemas) { + public ArrayItems(JsonSchema[] jsonSchemas) { this.jsonSchemas = jsonSchemas; } diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/BooleanSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/BooleanSchema.java index db3896bb..c0879780 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/BooleanSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/BooleanSchema.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} of type boolean @@ -10,7 +11,16 @@ */ public class BooleanSchema extends ValueTypeSchema { - @Override + protected BooleanSchema() { + //jackson deserialization only + super(); + } + + public BooleanSchema(JsonSchemaVersion version) { + super(version); + } + + @Override public boolean isBooleanSchema() { return true; } @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ContainerTypeSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ContainerTypeSchema.java index 51dfb2a8..874099f5 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ContainerTypeSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ContainerTypeSchema.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class encapsulates the functionality of container type {@link JsonSchema} @@ -13,15 +14,24 @@ */ public abstract class ContainerTypeSchema extends SimpleTypeSchema { - /** - * This provides an enumeration of all possible values that are valid - for the instance property. This MUST be an array, and each item in - the array represents a possible value for the instance value. If - this attribute is defined, the instance value MUST be one of the - values in the array in order for the schema to be valid. Comparison - of enum values uses the same algorithm as defined in "uniqueItems" - (Section 5.15). - */ + protected ContainerTypeSchema() { + //jackson deserialization only + super(); + } + + protected ContainerTypeSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This provides an enumeration of all possible values that are valid + for the instance property. This MUST be an array, and each item in + the array represents a possible value for the instance value. If + this attribute is defined, the instance value MUST be one of the + values in the array in order for the schema to be valid. Comparison + of enum values uses the same algorithm as defined in "uniqueItems" + (Section 5.15). + */ @JsonProperty(value = "enum", required = true) protected Set enums = Collections.emptySet(); diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/HyperSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/HyperSchema.java index 198816b9..09fbc7ba 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/HyperSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/HyperSchema.java @@ -1,10 +1,11 @@ package com.fasterxml.jackson.module.jsonSchema.types; +import java.lang.annotation.Annotation; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; - -import java.lang.annotation.Annotation; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents the HyperSchema portion of a {@link JsonSchema} @@ -13,11 +14,20 @@ */ public class HyperSchema extends JsonSchema { - /** - * This attribute indicates that the instance property SHOULD NOT be - changed. Attempts by a user agent to modify the value of this - property are expected to be rejected by a server. - */ + protected HyperSchema() { + //jackson deserialization only + super(); + } + + protected HyperSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This attribute indicates that the instance property SHOULD NOT be + changed. Attempts by a user agent to modify the value of this + property are expected to be rejected by a server. + */ @JsonProperty protected String readOnly; diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/IntegerSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/IntegerSchema.java index ce9c2ab9..18884f6a 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/IntegerSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/IntegerSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} as an integer type @@ -11,11 +12,20 @@ */ public class IntegerSchema extends NumberSchema { - /** - * This attribute defines what value the number instance must be - divisible by with no remainder (the result of the division must be an - integer.) The value of this attribute SHOULD NOT be 0. - */ + protected IntegerSchema() { + //jackson deserialization only + super(); + } + + public IntegerSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This attribute defines what value the number instance must be + divisible by with no remainder (the result of the division must be an + integer.) The value of this attribute SHOULD NOT be 0. + */ private Integer divisibleBy; @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NullSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NullSchema.java index 9ae8d090..77e810c3 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NullSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NullSchema.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} as a null type @@ -9,7 +10,16 @@ */ public class NullSchema extends SimpleTypeSchema { - @Override + protected NullSchema() { + //jackson deserialization only + super(); + } + + public NullSchema(JsonSchemaVersion version) { + super(version); + } + + @Override public NullSchema asNullSchema() { return this; } @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NumberSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NumberSchema.java index b42d35d3..9ba771aa 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NumberSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/NumberSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} as a number type @@ -10,11 +11,20 @@ */ public class NumberSchema extends ValueTypeSchema { - /** - * This attribute indicates if the value of the instance (if the - instance is a number) can not equal the number defined by the - "maximum" attribute. - */ + protected NumberSchema() { + //jackson deserialization only + super(); + } + + public NumberSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This attribute indicates if the value of the instance (if the + instance is a number) can not equal the number defined by the + "maximum" attribute. + */ @JsonProperty private Boolean exclusiveMaximum; diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ObjectSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ObjectSchema.java index 4624f069..97077fe6 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ObjectSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ObjectSchema.java @@ -1,5 +1,12 @@ package com.fasterxml.jackson.module.jsonSchema.types; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; @@ -7,11 +14,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; - -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This type represents a {@link JsonSchema} as an object type @@ -36,7 +39,7 @@ public class ObjectSchema extends ContainerTypeSchema * name as a property in this attribute's object, then the instance must be * valid against the attribute's property value */ - @JsonProperty + @JsonProperty private Map dependencies; /** @@ -65,14 +68,38 @@ public class ObjectSchema extends ContainerTypeSchema @JsonProperty private Map properties; - public ObjectSchema() + /** + * This will include the names of the properties that are required. + */ + @JsonProperty("required") + private Set requiredPropertyNames = new HashSet<>(); + + private boolean isDeserializing = false; + protected ObjectSchema() { + //jackson deserialization only + super(); + isDeserializing = true; + } + + public ObjectSchema(JsonSchemaVersion version) + { + this(version, false); + } + + public ObjectSchema(JsonSchemaVersion version, boolean set$Schema) { + super(version); + if (set$Schema) { + set$schema(version.getSchemaString()); + } dependencies = new LinkedHashMap(); patternProperties = new LinkedHashMap(); properties = new LinkedHashMap(); } public boolean addSchemaDependency(String depender, JsonSchema parentMustMatch) { + //NOTE: dependencies shouldn't have $schema + parentMustMatch.set$schema(null); dependencies.put(depender, parentMustMatch); return dependencies.get(depender).equals(parentMustMatch); } @@ -118,9 +145,20 @@ public Map getProperties() { return properties; } + public Set getRequiredPropertyNames() { + if (JsonSchemaVersion.DRAFT_V3.equals(version)) { + requiredPropertyNames.clear(); //make sure this does show in version 3 or it would collide + return Collections.unmodifiableSet(requiredPropertyNames); + } + return requiredPropertyNames; + } + public void putOptionalProperty(BeanProperty property, JsonSchema jsonSchema) { jsonSchema.enrichWithBeanProperty(property); properties.put(property.getName(), jsonSchema); + if (jsonSchema.isObjectSchema() && version != null) { + setParentInfo(this, version); + } } public void putOptionalProperty(String name, JsonSchema jsonSchema) { @@ -132,13 +170,15 @@ public JsonSchema putPatternProperty(String regex, JsonSchema value) { } public JsonSchema putProperty(BeanProperty property, JsonSchema value) { - value.setRequired(true); - value.enrichWithBeanProperty(property); - return properties.put(property.getName(), value); + return putProperty(property.getName(), value); } public JsonSchema putProperty(String name, JsonSchema value) { - value.setRequired(true); + if (JsonSchemaVersion.DRAFT_V3.equals(version)) { + value.setRequired(true); + } else { + requiredPropertyNames.add(name); + } return properties.put(name, value); } @@ -161,9 +201,31 @@ public void setPatternProperties(Map patternProperties) { public void setProperties(Map properties) { this.properties = properties; + //NOTE: probably a cleaner way to do this with a custom deserializer but short on time ATM + if (isDeserializing && version != null) { + setParentInfo(this, version); + isDeserializing = false; + } } - @Override + private void setParentInfo(ObjectSchema parent, JsonSchemaVersion version) { + if (parent.properties != null) { + parent.properties.entrySet().stream().filter(e -> e.getValue().isObjectSchema()).map(e -> e.getValue().asObjectSchema()).forEach(os -> { + setParentInfo(os, version); + os.setParent(parent); + os.version = version; + }); + } + } + + public void setRequiredPropertyNames(Set required) { + if (!isDeserializing && !JsonSchemaVersion.DRAFT_V4.equals(version)) { + throw new RuntimeException("You can only set required field name set on Draft V4. You have: " + version); + } + this.requiredPropertyNames = required; + } + + @Override public boolean equals(Object obj) { if (obj == this) return true; diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ReferenceSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ReferenceSchema.java index 9437d3d4..5d905816 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ReferenceSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ReferenceSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This type represents an JSON reference to a {@link com.fasterxml.jackson.module.jsonSchema.JsonSchema}. @@ -14,8 +15,19 @@ public class ReferenceSchema extends SimpleTypeSchema @JsonProperty protected String $ref; - public ReferenceSchema(String ref) { + protected ReferenceSchema() { + //jackson deserialization only + super(); + } + + public ReferenceSchema(JsonSchemaVersion version, String ref) { + this(version, ref, null); + } + + public ReferenceSchema(JsonSchemaVersion version, String ref, ObjectSchema parent) { + super(version); this.$ref = ref; + this.parent = parent; } @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/SimpleTypeSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/SimpleTypeSchema.java index fc43d263..00a95bf5 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/SimpleTypeSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/SimpleTypeSchema.java @@ -1,6 +1,8 @@ package com.fasterxml.jackson.module.jsonSchema.types; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class encapsulates the functionality of {@link JsonSchema} simple types @@ -8,10 +10,19 @@ */ public abstract class SimpleTypeSchema extends JsonSchema { - /** - * This attribute defines the default value of the instance when the - * instance is undefined. - */ + protected SimpleTypeSchema() { + //jackson deserialization only + super(); + } + + protected SimpleTypeSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This attribute defines the default value of the instance when the + * instance is undefined. + */ protected String defaultdefault; /** @@ -31,6 +42,9 @@ public abstract class SimpleTypeSchema extends JsonSchema */ protected LinkDescriptionObject[] links; + @JsonIgnore + protected ObjectSchema parent; + @Override public SimpleTypeSchema asSimpleTypeSchema() { return this; @@ -56,7 +70,15 @@ public void setLinks(LinkDescriptionObject[] links) { this.links = links; } - @Override + public ObjectSchema getParent() { + return parent; + } + + public void setParent(ObjectSchema parent) { + this.parent = parent; + } + + @Override public boolean isSimpleTypeSchema() { return true; } diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/StringSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/StringSchema.java index c6d2cd03..b4810770 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/StringSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/StringSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This represents a {@link JsonSchema} as a String @@ -11,6 +12,15 @@ */ public class StringSchema extends ValueTypeSchema { + protected StringSchema() { + //jackson deserialization only + super(); + } + + public StringSchema(JsonSchemaVersion version) { + super(version); + } + /** this defines the maximum length of the string. */ @JsonProperty private Integer maxLength; diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/UnionTypeSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/UnionTypeSchema.java index fe3a74e9..3d7e72ad 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/UnionTypeSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/UnionTypeSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} as a Union Type Schema: @@ -15,7 +16,16 @@ */ public class UnionTypeSchema extends JsonSchema { - @JsonProperty + protected UnionTypeSchema() { + //jackson deserialization only + super(); + } + + protected UnionTypeSchema(JsonSchemaVersion version) { + super(version); + } + + @JsonProperty protected ValueTypeSchema[] elements; @Override diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ValueTypeSchema.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ValueTypeSchema.java index 2e8fa6e9..154cae48 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ValueTypeSchema.java +++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/types/ValueTypeSchema.java @@ -1,11 +1,13 @@ package com.fasterxml.jackson.module.jsonSchema.types; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * This class represents a {@link JsonSchema} @@ -13,15 +15,24 @@ */ public abstract class ValueTypeSchema extends SimpleTypeSchema { - /** - * This provides an enumeration of all possible values that are valid - for the instance property. This MUST be an array, and each item in - the array represents a possible value for the instance value. If - this attribute is defined, the instance value MUST be one of the - values in the array in order for the schema to be valid. Comparison - of enum values uses the same algorithm as defined in "uniqueItems" - (Section 5.15). - */ + protected ValueTypeSchema() { + //jackson deserialization only + super(); + } + + protected ValueTypeSchema(JsonSchemaVersion version) { + super(version); + } + + /** + * This provides an enumeration of all possible values that are valid + for the instance property. This MUST be an array, and each item in + the array represents a possible value for the instance value. If + this attribute is defined, the instance value MUST be one of the + values in the array in order for the schema to be valid. Comparison + of enum values uses the same algorithm as defined in "uniqueItems" + (Section 5.15). + */ @JsonProperty(value = "enum") @JsonDeserialize(as = LinkedHashSet.class) protected Set enums = new LinkedHashSet(); diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java deleted file mode 100644 index 3546a148..00000000 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.fasterxml.jackson.module.jsonSchema.validation; - -import com.fasterxml.jackson.databind.BeanProperty; - -import javax.validation.constraints.*; -import java.math.BigDecimal; - -/** - * @author cponomaryov - * - * @since 2.5 - */ -public class AnnotationConstraintResolver - extends ValidationConstraintResolver -{ - private Integer getMaxSize(BeanProperty prop) { - Size ann = getSizeAnnotation(prop); - if (ann != null) { - int value = ann.max(); - if (value != Integer.MAX_VALUE) { - return value; - } - } - return null; - } - - private Integer getMinSize(BeanProperty prop) { - Size ann = getSizeAnnotation(prop); - if (ann != null) { - int value = ann.min(); - if (value != 0) { - return value; - } - } - return null; - } - - @Override - public Integer getArrayMaxItems(BeanProperty prop) { - return getMaxSize(prop); - } - - @Override - public Integer getArrayMinItems(BeanProperty prop) { - return getMinSize(prop); - } - - @Override - public Double getNumberMaximum(BeanProperty prop) { - Max maxAnnotation = prop.getAnnotation(Max.class); - if (maxAnnotation != null) { - return (double) maxAnnotation.value(); - } - DecimalMax decimalMaxAnnotation = prop.getAnnotation(DecimalMax.class); - return decimalMaxAnnotation != null ? new BigDecimal(decimalMaxAnnotation.value()).doubleValue() : null; - } - - @Override - public Double getNumberMinimum(BeanProperty prop) { - Min minAnnotation = prop.getAnnotation(Min.class); - if (minAnnotation != null) { - return (double) minAnnotation.value(); - } - DecimalMin decimalMinAnnotation = prop.getAnnotation(DecimalMin.class); - return decimalMinAnnotation != null ? new BigDecimal(decimalMinAnnotation.value()).doubleValue() : null; - } - - @Override - public Integer getStringMaxLength(BeanProperty prop) { - return getMaxSize(prop); - } - - @Override - public Integer getStringMinLength(BeanProperty prop) { - return getMinSize(prop); - } - - @Override - public String getStringPattern(final BeanProperty prop) { - Pattern patternAnnotation = prop.getAnnotation(Pattern.class); - if (patternAnnotation != null) { - return patternAnnotation.regexp(); - } - return null; - } - - @Override - public Boolean getRequired(BeanProperty prop) { - NotNull notNull = prop.getAnnotation(NotNull.class); - return notNull != null ? true : null; - } - - private Size getSizeAnnotation(BeanProperty prop) { - return prop.getAnnotation(Size.class); - } -} diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java deleted file mode 100644 index 8a539f2e..00000000 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.fasterxml.jackson.module.jsonSchema.validation; - -import com.fasterxml.jackson.databind.BeanProperty; - -/** - * Note: implementations should - * - * @author cponomaryov - * - * @since 2.5 NOTE: changed from interface (2.5 - 2.7) to abstract class in 2.8 - */ -public abstract class ValidationConstraintResolver -{ - public abstract Integer getArrayMaxItems(BeanProperty prop); - - public abstract Integer getArrayMinItems(BeanProperty prop); - - public abstract Double getNumberMaximum(BeanProperty prop); - - public abstract Double getNumberMinimum(BeanProperty prop); - - public abstract Integer getStringMaxLength(BeanProperty prop); - - public abstract Integer getStringMinLength(BeanProperty prop); - - public abstract String getStringPattern(BeanProperty prop); - - /** - * @since 2.7 - */ - public abstract Boolean getRequired(BeanProperty prop); -} diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java index d5da97e4..d00bd1ff 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/CustomSchemaReadTest.java @@ -4,12 +4,20 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; public class CustomSchemaReadTest extends SchemaTestBase { @JsonTypeIdResolver(MyResolver.class) public static class MySchema extends ObjectSchema { + protected MySchema() { + super(); + } + + public MySchema(JsonSchemaVersion version) { + super(version); + } } static class MyResolver extends JsonSchemaIdResolver @@ -42,7 +50,43 @@ public JavaType typeFromId(DatabindContext ctxt, String id) { // [module-jsonSchema#67] public void testSchema() throws Exception { - String input = "{ \"type\" : \"CUSTOM\" , \"id\" : \"7a2e8538-196b-423e-b714-13515848ec0c\" , \"description\" : \"My Schema\" , \"title\" : \"my-json-schema\" , \"properties\" : { \"myarray\" : { \"type\" : \"array\" , \"required\" : true , \"title\" : \"my property #2\" , \"items\" : { \"type\" : \"string\"} , \"maxItems\" : 5} , \"mystring\" : { \"type\" : \"string\" , \"required\" : true , \"title\" : \"my property #1\" , \"format\" : \"regex\" , \"pattern\" : \"\\\\w+\"} , \"myobject\" : { \"type\" : \"object\" , \"required\" : true , \"title\" : \"my property #3\" , \"properties\" : { \"subprop\" : { \"type\" : \"string\" , \"required\" : true , \"title\" : \"sub property #1\" , \"format\" : \"regex\" , \"pattern\" : \"\\\\w{3}\"}}}}}"; + String input = "{\n" + + " \"type\":\"CUSTOM\",\n" + + " \"id\":\"7a2e8538-196b-423e-b714-13515848ec0c\",\n" + + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + + " \"description\":\"My Schema\",\n" + + " \"title\":\"my-json-schema\",\n" + + " \"properties\":{\n" + + " \"myarray\":{\n" + + " \"type\":\"array\",\n" + + " \"title\":\"my property #2\",\n" + + " \"items\":{\n" + + " \"type\":\"string\"\n" + + " },\n" + + " \"maxItems\":5\n" + + " },\n" + + " \"mystring\":{\n" + + " \"type\":\"string\",\n" + + " \"title\":\"my property #1\",\n" + + " \"format\":\"regex\",\n" + + " \"pattern\":\"w+\"\n" + + " },\n" + + " \"myobject\":{\n" + + " \"type\":\"object\",\n" + + " \"title\":\"my property #3\",\n" + + " \"properties\":{\n" + + " \"subprop\":{\n" + + " \"type\":\"string\",\n" + + " \"title\":\"sub property #1\",\n" + + " \"format\":\"regex\",\n" + + " \"pattern\":\"w{3}\"\n" + + " }\n" + + " },\n" + + " \"required\":[\"subprop\"]" + + " }\n" + + " },\n" + + " \"required\":[\"myarray\", \"mystring\", \"myobject\"]" + + "}"; ObjectMapper mapper = new ObjectMapper(); @@ -50,6 +94,7 @@ public void testSchema() throws Exception MySchema schema = mapper.readValue(input, MySchema.class); // fails String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema); + System.err.println(json); assertNotNull(json); } } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumGenerationTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumGenerationTest.java index 1be11c64..58c43882 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumGenerationTest.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumGenerationTest.java @@ -1,10 +1,14 @@ package com.fasterxml.jackson.module.jsonSchema; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class EnumGenerationTest extends SchemaTestBase { @@ -38,7 +42,7 @@ public enum EnumViaJsonValue { */ private final ObjectMapper MAPPER = new ObjectMapper(); - JsonSchemaGenerator SCHEMA_GEN = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator SCHEMA_GEN = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); public void testEnumDefault() throws Exception { @@ -55,7 +59,7 @@ public void testEnumWithToString() throws Exception final ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); - JsonSchemaGenerator generator = new JsonSchemaGenerator(mapper); + JsonSchemaGenerator generator = new JsonSchemaGenerator(mapper, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(LetterBean.class); @SuppressWarnings("unchecked") Map result = (Map) mapper.convertValue(jsonSchema, Map.class); @@ -76,6 +80,7 @@ private Map expectedAsMap(final boolean useToString) { put("type", "object"); put("id", "urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:EnumGenerationTest:LetterBean"); + put("$schema", JsonSchemaVersion.DRAFT_V4.getSchemaString()); put("properties", new LinkedHashMap() { { diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumSchemaTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumSchemaTest.java index 3d1c9bf5..8097f3fa 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumSchemaTest.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/EnumSchemaTest.java @@ -3,9 +3,10 @@ import java.util.Iterator; import java.util.Set; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; import com.fasterxml.jackson.module.jsonSchema.types.ValueTypeSchema; @@ -22,7 +23,7 @@ static class MyClass { public void testEnumArrayDeserialization() throws Exception { ObjectMapper mapper = new ObjectMapper(); - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); mapper.acceptJsonFormatVisitor(mapper.constructType(MyEnum[].class), visitor); JsonSchema schema = visitor.finalSchema(); @@ -53,12 +54,11 @@ public void testEnumArrayDeserializationOrdering() throws Exception { final String jsonSchema = "{\n" + " \"type\": \"object\",\n" + " \"id\": \"https://foo.bar/wibble\",\n" + - " \"$schema\": \"http://json-schema.org/draft-03/schema#\",\n" + + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + " \"properties\": {\n" + " \"testOptions\": {\n" + " \"type\": \"array\",\n" + " \"id\": \"testOptions\",\n" + - " \"required\":true,\n" + " \"items\": {\n" + " \"type\": \"string\",\n" + " \"enum\": [\n" + @@ -70,7 +70,8 @@ public void testEnumArrayDeserializationOrdering() throws Exception { " },\n" + " \"minItems\": 1\n" + " }\n" + - " }\n" + + " },\n" + + " \"required\":[\"testOptions\"]" + "}"; ObjectMapper mapper = new ObjectMapper(); diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java similarity index 77% rename from src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java rename to src/test/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java index 1aab7d91..b9710a8e 100644 --- a/src/main/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/JsonSchemaGenerator.java @@ -1,8 +1,12 @@ package com.fasterxml.jackson.module.jsonSchema; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; /** * Convenience class that wraps JSON Schema generation functionality. @@ -29,14 +33,14 @@ public class JsonSchemaGenerator */ private final SchemaFactoryWrapper _visitor; - public JsonSchemaGenerator(ObjectMapper mapper) { - this(mapper, (WrapperFactory) null); + public JsonSchemaGenerator(ObjectMapper mapper, JsonSchemaVersion version) { + this(mapper, (WrapperFactory)null, version); } - public JsonSchemaGenerator(ObjectMapper mapper, WrapperFactory wrapperFactory) { + public JsonSchemaGenerator(ObjectMapper mapper, WrapperFactory wrapperFactory, JsonSchemaVersion version) { _mapper = mapper; _writer = mapper.writer(); - _wrapperFactory = (wrapperFactory == null) ? new WrapperFactory() : wrapperFactory; + _wrapperFactory = (wrapperFactory == null) ? new WrapperFactory(version) : wrapperFactory; _visitor = null; } @@ -53,17 +57,17 @@ public JsonSchemaGenerator(ObjectMapper mapper, SchemaFactoryWrapper visitor) { /** * @since 2.6 */ - public JsonSchemaGenerator(ObjectWriter w) { - this(w, (WrapperFactory) null); + public JsonSchemaGenerator(ObjectWriter w, JsonSchemaVersion version) { + this(w, (WrapperFactory)null, version); } /** * @since 2.6 */ - public JsonSchemaGenerator(ObjectWriter w, WrapperFactory wrapperFactory) { + public JsonSchemaGenerator(ObjectWriter w, WrapperFactory wrapperFactory, JsonSchemaVersion version) { _mapper = null; _writer = w; - _wrapperFactory = (wrapperFactory == null) ? new WrapperFactory() : wrapperFactory; + _wrapperFactory = (wrapperFactory == null) ? new WrapperFactory(version) : wrapperFactory; _visitor = null; } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/SchemaPropertyProcessorManagerFactoryWrapperTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/SchemaPropertyProcessorManagerFactoryWrapperTest.java new file mode 100644 index 00000000..b63068ba --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/SchemaPropertyProcessorManagerFactoryWrapperTest.java @@ -0,0 +1,677 @@ +package com.fasterxml.jackson.module.jsonSchema; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; + +import javax.validation.Constraint; +import javax.validation.OverridesAttribute; +import javax.validation.Payload; +import javax.validation.Valid; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import javax.validation.groups.Default; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.annotation.JsonSchemaTitle; +import com.fasterxml.jackson.module.jsonSchema.annotation.NonStandardProperty; +import com.fasterxml.jackson.module.jsonSchema.customProperties.SchemaPropertyProcessorManagerFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.customProperties.ValidationSchemaPropertyProcessorManagerFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.property.SchemaPropertyProcessorTitle; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManager; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManagerApi; +import com.fasterxml.jackson.module.jsonSchema.property.manager.SchemaPropertyProcessorManagerConstraint; +import com.fasterxml.jackson.module.jsonSchema.property.nonstandard.SchemaPropertyProcessorNonStandardProperties; +import com.fasterxml.jackson.module.jsonSchema.property.nonstandard.SchemaPropertyProcessorNonStandardProperty; +import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; +import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; +import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema; +import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; + +/** + * @author cponomaryov + */ +public class SchemaPropertyProcessorManagerFactoryWrapperTest extends SchemaTestBase { + @Size(min = 2, max = 80) + @MyPattern(message = "") + @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Constraint(validatedBy = {}) + public @interface MemberNumber { + @OverridesAttribute.List({ // + @OverridesAttribute(constraint = Size.class, name = "message"), // + @OverridesAttribute(constraint = MyPattern.class, name = "message")}) + public String message(); + Class[] groups() default {}; + Class[] payload() default {}; + } + + @Pattern(regexp = "[\\p{Alnum}]*") + @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Constraint(validatedBy = {}) + public @interface MyPattern { + @OverridesAttribute(constraint = Pattern.class, name = "message") + public String message(); + Class[] groups() default {}; + Class[] payload() default {}; + } + + public interface GroupA { + } + + public interface GroupB { + } + + public interface None { + } + + public static class Internal { + enum Animals { + DOG, CAT + }; + + @NotNull(groups = {Default.class, GroupA.class}) + private Animals animal; + + @JsonSchemaTitle("This name is optional.") + String nameOptional; + + @NotNull(groups = {Default.class, GroupB.class}) + String nameMandatory; + + public Animals getAnimal() { + return animal; + } + + public void setAnimal(Animals animal) { + this.animal = animal; + } + + public String getNameOptional() { + return nameOptional; + } + + public void setNameOptional(String nameOptional) { + this.nameOptional = nameOptional; + } + + public String getNameMandatory() { + return nameMandatory; + } + + public void setNameMandatory(String nameMandatory) { + this.nameMandatory = nameMandatory; + } + } + + public static class ValidationBean { + + @Valid + @NotNull + Internal internal; + + @Valid + @NotNull + Internal internal2; + + @MemberNumber(message = "Invalid member num.", groups = {Default.class, GroupA.class}) + private String memberNumber; + + /* + /********************************************************** + /* Array fields + /********************************************************** + */ + + private List listWithoutConstraints; + + @Size(min = 1) + private List listWithMinSize; + + @Size(max = 2) + private List listWithMaxSize; + + @Size(min = 3, max = 4) + private List listWithMinAndMaxSize; + + /* + /********************************************************** + /* Number fields + /********************************************************** + */ + + private int numberWithoutConstraints; + + @Min(5) + private int numberWithMin; + + @DecimalMin("5.5") + private int numberWithDecimalMin; + + @Max(6) + private int numberWithMax; + + @DecimalMax("6.5") + private int numberWithDecimalMax; + + @Min(7) + @Max(8) + private int numberWithMinAndMax; + + @Min(9) + @DecimalMax("9.5") + private int numberWithMinAndDecimalMax; + + @DecimalMin("10.5") + @Max(11) + private int numberWithDecimalMinAndMax; + + @DecimalMin("11.5") + @DecimalMax("12.5") + private int numberWithDecimalMinAndDecimalMax; + + /* + /********************************************************** + /* String fields + /********************************************************** + */ + + private String stringWithoutConstraints; + + @Size(min = 13) + private String stringWithMinSize; + + @Size(max = 14) + private String stringWithMaxSize; + + @Size(min = 15, max = 16) + private String stringWithMinAndMaxSize; + + @Pattern(regexp = "[a-z]+") + private String stringWithPattern; + + /* + /********************************************************** + /* Nullable and not nullable fields + /********************************************************** + */ + + private String nullable; + + @NotNull + private String notNullable; + + public Internal getInternal() { + return internal; + } + + public void setInternal(Internal internal) { + this.internal = internal; + } + + public Internal getInternal2() { + return internal2; + } + + public void setInterna2l(Internal internal2) { + this.internal2 = internal2; + } + + public String getMemberNumber() { + return memberNumber; + } + + public void setMemberNumber(String memberNumber) { + this.memberNumber = memberNumber; + } + + public List getListWithoutConstraints() { + return listWithoutConstraints; + } + + public void setListWithoutConstraints(List listWithoutConstraints) { + this.listWithoutConstraints = listWithoutConstraints; + } + + public List getListWithMinSize() { + return listWithMinSize; + } + + public void setListWithMinSize(List listWithMinSize) { + this.listWithMinSize = listWithMinSize; + } + + public List getListWithMaxSize() { + return listWithMaxSize; + } + + public void setListWithMaxSize(List listWithMaxSize) { + this.listWithMaxSize = listWithMaxSize; + } + + public List getListWithMinAndMaxSize() { + return listWithMinAndMaxSize; + } + + public void setListWithMinAndMaxSize(List listWithMinAndMaxSize) { + this.listWithMinAndMaxSize = listWithMinAndMaxSize; + } + + public int getNumberWithoutConstraints() { + return numberWithoutConstraints; + } + + public void setNumberWithoutConstraints(int numberWithoutConstraints) { + this.numberWithoutConstraints = numberWithoutConstraints; + } + + public int getNumberWithMin() { + return numberWithMin; + } + + public void setNumberWithMin(int numberWithMin) { + this.numberWithMin = numberWithMin; + } + + public int getNumberWithDecimalMin() { + return numberWithDecimalMin; + } + + public void setNumberWithDecimalMin(int numberWithDecimalMin) { + this.numberWithDecimalMin = numberWithDecimalMin; + } + + public int getNumberWithMax() { + return numberWithMax; + } + + public void setNumberWithMax(int numberWithMax) { + this.numberWithMax = numberWithMax; + } + + public int getNumberWithDecimalMax() { + return numberWithDecimalMax; + } + + public void setNumberWithDecimalMax(int numberWithDecimalMax) { + this.numberWithDecimalMax = numberWithDecimalMax; + } + + public int getNumberWithMinAndMax() { + return numberWithMinAndMax; + } + + public void setNumberWithMinAndMax(int numberWithMinAndMax) { + this.numberWithMinAndMax = numberWithMinAndMax; + } + + public int getNumberWithMinAndDecimalMax() { + return numberWithMinAndDecimalMax; + } + + public void setNumberWithMinAndDecimalMax(int numberWithMinAndDecimalMax) { + this.numberWithMinAndDecimalMax = numberWithMinAndDecimalMax; + } + + public int getNumberWithDecimalMinAndMax() { + return numberWithDecimalMinAndMax; + } + + public void setNumberWithDecimalMinAndMax(int numberWithDecimalMinAndMax) { + this.numberWithDecimalMinAndMax = numberWithDecimalMinAndMax; + } + + public int getNumberWithDecimalMinAndDecimalMax() { + return numberWithDecimalMinAndDecimalMax; + } + + public void setNumberWithDecimalMinAndDecimalMax(int numberWithDecimalMinAndDecimalMax) { + this.numberWithDecimalMinAndDecimalMax = numberWithDecimalMinAndDecimalMax; + } + + public String getStringWithoutConstraints() { + return stringWithoutConstraints; + } + + public void setStringWithoutConstraints(String stringWithoutConstraints) { + this.stringWithoutConstraints = stringWithoutConstraints; + } + + public String getStringWithMinSize() { + return stringWithMinSize; + } + + public void setStringWithMinSize(String stringWithMinSize) { + this.stringWithMinSize = stringWithMinSize; + } + + public String getStringWithMaxSize() { + return stringWithMaxSize; + } + + public void setStringWithMaxSize(String stringWithMaxSize) { + this.stringWithMaxSize = stringWithMaxSize; + } + + public String getStringWithMinAndMaxSize() { + return stringWithMinAndMaxSize; + } + + public void setStringWithMinAndMaxSize(String stringWithMinAndMaxSize) { + this.stringWithMinAndMaxSize = stringWithMinAndMaxSize; + } + + public String getStringWithPattern() { + return stringWithPattern; + } + + public void setStringWithPattern(final String stringWithPattern) { + this.stringWithPattern = stringWithPattern; + } + + public String getNullable() { + return nullable; + } + + public void setNullable(String nullable) { + this.nullable = nullable; + } + + public String getNotNullable() { + return notNullable; + } + + public void setNotNullable(String notNullable) { + this.notNullable = notNullable; + } + } + + /* + /********************************************************** + /* Unit tests, success + /********************************************************** + */ + + private Object[][] listTestData() { + return new Object[][] {{"listWithoutConstraints", null, null}, + {"listWithMinSize", 1, null}, + {"listWithMaxSize", null, 2}, + {"listWithMinAndMaxSize", 3, 4}}; + } + + private Object[][] numberTestData() { + return new Object[][] {{"numberWithoutConstraints", null, null}, + {"numberWithMin", 5d, null}, + {"numberWithDecimalMin", 5.5, null}, + {"numberWithMax", null, 6d}, + {"numberWithDecimalMax", null, 6.5}, + {"numberWithMinAndMax", 7d, 8d}, + {"numberWithMinAndDecimalMax", 9d, 9.5}, + {"numberWithDecimalMinAndMax", 10.5, 11d}, + {"numberWithDecimalMinAndDecimalMax", 11.5, 12.5}}; + } + + private Object[][] stringTestData() { + return new Object[][] {{"stringWithoutConstraints", null, null}, + {"stringWithMinSize", 13, null}, + {"stringWithMaxSize", null, 14}, + {"stringWithMinAndMaxSize", 15, 16}}; + } + + private Object[][] stringPatternTestData() { + return new Object[][] {{"stringWithPattern", "[a-z]+"}, + {"stringWithoutConstraints", null}}; + } + + private Object[][] notNullTestData() { + return new Object[][] { + {"nullable", false}, + {"notNullable", true}}; + } + + /** + * Test set validation constraints + */ + @Test + public void testAddingValidationConstraints() throws Exception { + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(ValidationBean.class); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + for (Object[] testCase : listTestData()) { + JsonSchema propertySchema = properties.get(testCase[0]); + assertNotNull(propertySchema); + assertTrue(propertySchema.isArraySchema()); + ArraySchema arraySchema = propertySchema.asArraySchema(); + assertEquals(testCase[1], arraySchema.getMinItems()); + assertEquals(testCase[2], arraySchema.getMaxItems()); + } + for (Object[] testCase : numberTestData()) { + JsonSchema propertySchema = properties.get(testCase[0]); + assertNotNull(propertySchema); + assertTrue(propertySchema.isNumberSchema()); + NumberSchema numberSchema = propertySchema.asNumberSchema(); + assertEquals(testCase[1], numberSchema.getMinimum()); + assertEquals(testCase[2], numberSchema.getMaximum()); + } + for (Object[] testCase : stringTestData()) { + JsonSchema propertySchema = properties.get(testCase[0]); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertEquals(testCase[1], stringSchema.getMinLength()); + assertEquals(testCase[2], stringSchema.getMaxLength()); + } + for (Object[] testCase : stringPatternTestData()) { + JsonSchema propertySchema = properties.get(testCase[0]); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertEquals(testCase[1], stringSchema.getPattern()); + } + for (Object[] testCase : notNullTestData()) { + JsonSchema propertySchema = properties.get(testCase[0]); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertEquals(testCase[1], stringSchema.getParent().getRequiredPropertyNames().contains(testCase[0])); + } + } + + @Test + public void testAddingValidationConstraints_InternalRequired() throws Exception { + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(ValidationBean.class); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + JsonSchema propertySchema = properties.get("internal"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isObjectSchema()); + ObjectSchema intObjectSchema = propertySchema.asObjectSchema(); + assertThat(intObjectSchema.getRequiredPropertyNames(), containsInAnyOrder("animal", "nameMandatory")); + } + + @Test + public void testAddingValidationConstraints_CustomAnnotation() throws Exception { + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(ValidationBean.class); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + JsonSchema propertySchema = properties.get("memberNumber"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertEquals(new Integer("2"), stringSchema.getMinLength()); + assertEquals(new Integer("80"), stringSchema.getMaxLength()); + assertEquals("[\\p{Alnum}]*", stringSchema.getPattern()); + } + + @Test + public void testAddingValidationConstraints_LimitedByGroup() throws Exception { + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(ValidationBean.class, GroupB.class); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + JsonSchema propertySchema = properties.get("memberNumber"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertNull(stringSchema.getMinLength()); + assertNull(stringSchema.getMaxLength()); + assertNull(stringSchema.getPattern()); + + propertySchema = properties.get("internal"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isObjectSchema()); + ObjectSchema intObjectSchema = propertySchema.asObjectSchema(); + assertThat(intObjectSchema.getRequiredPropertyNames(), containsInAnyOrder("nameMandatory")); + } + + @Test + public void testAddingValidationConstraints_NoValidation() throws Exception { + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(ValidationBean.class, None.class); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + JsonSchema propertySchema = properties.get("memberNumber"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isStringSchema()); + StringSchema stringSchema = propertySchema.asStringSchema(); + assertNull(stringSchema.getMinLength()); + assertNull(stringSchema.getMaxLength()); + assertNull(stringSchema.getPattern()); + + propertySchema = properties.get("internal"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isObjectSchema()); + ObjectSchema intObjectSchema = propertySchema.asObjectSchema(); + assertThat(intObjectSchema.getRequiredPropertyNames(), is(empty())); + } + + @Test + public void testAddingValidationConstraints_CustomPropertyManager() throws Exception { + SchemaPropertyProcessorManagerApi customPropertyProcessorManager = new SchemaPropertyProcessorManagerConstraint(ValidationBean.class, Default.class); + customPropertyProcessorManager.registerSchemaPropertyProcessor(new SchemaPropertyProcessorTitle()); + SchemaPropertyProcessorManagerFactoryWrapper visitor = new SchemaPropertyProcessorManagerFactoryWrapper(customPropertyProcessorManager); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + + JsonSchema propertySchema = properties.get("internal"); + assertNotNull(propertySchema); + assertTrue(propertySchema.isObjectSchema()); + ObjectSchema intObjectSchema = propertySchema.asObjectSchema(); + properties = intObjectSchema.getProperties(); + assertNotNull(properties); + propertySchema = properties.get("nameOptional"); + assertTrue(propertySchema.isSimpleTypeSchema()); + SimpleTypeSchema stSchema = propertySchema.asSimpleTypeSchema(); + assertThat(stSchema.getTitle(), is(equalTo("This name is optional."))); + } + + @NonStandardProperty(name = "test", value = "testValue") + public static class InternalNonStandard { + @NonStandardProperty(name = "test2", value = "testValue2") + @NonStandardProperty(name = "test3", value = "testValue3") + @JsonSchemaTitle("This name is optional.") + String nameOptional; + + public String getNameOptional() { + return nameOptional; + } + + public void setNameOptional(String nameOptional) { + this.nameOptional = nameOptional; + } + } + + @Test + public void testAddingValidationConstraints_NonStandardProperty() throws Exception { + SchemaPropertyProcessorManagerApi customPropertyProcessorManager = new SchemaPropertyProcessorManager(); + customPropertyProcessorManager.registerSchemaPropertyProcessor(new SchemaPropertyProcessorNonStandardProperty()); + customPropertyProcessorManager.registerSchemaPropertyProcessor(new SchemaPropertyProcessorNonStandardProperties()); + SchemaPropertyProcessorManagerFactoryWrapper visitor = new SchemaPropertyProcessorManagerFactoryWrapper(customPropertyProcessorManager); + ObjectMapper mapper = new ObjectMapper(); + + mapper.acceptJsonFormatVisitor(InternalNonStandard.class, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertEquals("testValue", jsonSchema.getNonStandardProperty("test")); + + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + + JsonSchema propertySchema = properties.get("nameOptional"); + assertNotNull(propertySchema); + assertEquals("testValue2", propertySchema.getNonStandardProperty("test2")); + assertEquals("testValue3", propertySchema.getNonStandardProperty("test3")); + } + + void printJsonSchema(JsonSchema jsonSchema) throws JsonProcessingException { + System.err.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema)); + } +} diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestCyclic.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestCyclic.java index f6058952..bb3fbf90 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestCyclic.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestCyclic.java @@ -1,11 +1,12 @@ package com.fasterxml.jackson.module.jsonSchema; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.databind.ObjectMapper; - import java.util.List; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; + public class TestCyclic extends SchemaTestBase { // [Issue#4] @@ -36,12 +37,13 @@ public class InnerLoop { // [Issue#4] public void testSimpleCyclic() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema = generator.generateSchema(Loop.class); String json = MAPPER.writeValueAsString(schema); String EXP = "{\"type\":\"object\"," + "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:Loop\"," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "\"properties\":{\"next\":{\"type\":\"object\"," + "\"$ref\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:Loop\"}" + ",\"name\":{\"type\":\"string\"}}}"; @@ -50,12 +52,13 @@ public void testSimpleCyclic() throws Exception public void testListCyclic() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema = generator.generateSchema(ListLoop.class); String json = MAPPER.writeValueAsString(schema); String EXP = "{\"type\":\"object\"," + "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:ListLoop\"," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "\"properties\":{\"list\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"$ref\":\"" + "urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:ListLoop\"}}}}"; @@ -64,12 +67,13 @@ public void testListCyclic() throws Exception public void testMapCyclic() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema = generator.generateSchema(MapLoop.class); String json = MAPPER.writeValueAsString(schema); String EXP = "{\"type\":\"object\"," + "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:MapLoop\"," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "\"properties\":{\"map\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"object\"," + "\"$ref\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:MapLoop\"}}}}"; @@ -78,12 +82,13 @@ public void testMapCyclic() throws Exception public void testInnerOuterCyclic() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema = generator.generateSchema(OuterLoop.class); String json = MAPPER.writeValueAsString(schema); String EXP = "{\"type\":\"object\"," + "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:OuterLoop\"," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "\"properties\":{\"inner\":{\"type\":\"object\"," + "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestCyclic:InnerLoop\"," + "\"properties\":{\"outer\":{\"type\":\"object\"," + diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestEquals.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestEquals.java index 8fe86f32..b5ffd450 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestEquals.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestEquals.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.module.jsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.NullSchema; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; @@ -8,12 +9,26 @@ */ public class TestEquals extends SchemaTestBase { - public void testEquals() throws Exception { - ObjectSchema schema1 = new ObjectSchema(); - ObjectSchema schema2 = new ObjectSchema(); - schema2.getProperties().put("property1", new NullSchema()); + public void testEquals() throws Exception { + ObjectSchema schema1 = new ObjectSchema(JsonSchemaVersion.DRAFT_V4, true); + ObjectSchema schema2 = new ObjectSchema(JsonSchemaVersion.DRAFT_V4, true); + + assertTrue(schema1.equals(schema2)); + } + + public void testNotEquals_ExtraProperty() throws Exception { + ObjectSchema schema1 = new ObjectSchema(JsonSchemaVersion.DRAFT_V4, true); + ObjectSchema schema2 = new ObjectSchema(JsonSchemaVersion.DRAFT_V4, true); + schema2.getProperties().put("property1", new NullSchema(JsonSchemaVersion.DRAFT_V4)); - assertTrue(!schema1.equals(schema2)); + assertFalse(schema1.equals(schema2)); + } + + public void testNotEquals_DifferentVersions() throws Exception { + ObjectSchema schema1 = new ObjectSchema(JsonSchemaVersion.DRAFT_V3, true); + ObjectSchema schema2 = new ObjectSchema(JsonSchemaVersion.DRAFT_V4, true); + + assertFalse(schema1.equals(schema2)); } } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestGenerateJsonSchema.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestGenerateJsonSchema.java index a36e962f..da81d1d4 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestGenerateJsonSchema.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestGenerateJsonSchema.java @@ -1,5 +1,13 @@ package com.fasterxml.jackson.module.jsonSchema; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertThat; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JavaType; @@ -8,13 +16,10 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema.Items; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - @SuppressWarnings("serial") public class TestGenerateJsonSchema extends SchemaTestBase @@ -135,7 +140,7 @@ public void setProperty2(String property2) { * Test simple generation */ public void testGeneratingJsonSchema() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(SimpleBean.class); assertNotNull(jsonSchema); @@ -153,19 +158,16 @@ public void testGeneratingJsonSchema() throws Exception { JsonSchema prop1 = properties.get("property1"); assertNotNull(prop1); assertTrue(prop1.isIntegerSchema()); - assertNull(prop1.getRequired()); assertNull(prop1.getReadonly()); JsonSchema prop2 = properties.get("property2"); assertNotNull(prop2); assertTrue(prop2.isStringSchema()); - assertNull(prop2.getRequired()); assertNull(prop2.getReadonly()); JsonSchema prop3 = properties.get("property3"); assertNotNull(prop3); assertTrue(prop3.isArraySchema()); - assertNull(prop3.getRequired()); assertNull(prop3.getReadonly()); Items items = prop3.asArraySchema().getItems(); assertTrue(items.isSingleItems()); @@ -176,7 +178,6 @@ public void testGeneratingJsonSchema() throws Exception { JsonSchema prop4 = properties.get("property4"); assertNotNull(prop4); assertTrue(prop4.isArraySchema()); - assertNull(prop4.getRequired()); assertNull(prop4.getReadonly()); items = prop4.asArraySchema().getItems(); assertTrue(items.isSingleItems()); @@ -186,15 +187,15 @@ public void testGeneratingJsonSchema() throws Exception { JsonSchema prop5 = properties.get("property5"); assertNotNull(prop5); - assertTrue(prop5.getRequired()); assertNull(prop5.getReadonly()); + assertThat(object.getRequiredPropertyNames(), containsInAnyOrder("property5")); } public void testGeneratingJsonSchemaWithFilters() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.setFilterProvider(secretFilterProvider); - JsonSchemaGenerator generator = new JsonSchemaGenerator(mapper); + JsonSchemaGenerator generator = new JsonSchemaGenerator(mapper, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(FilteredBean.class); assertNotNull(jsonSchema); assertTrue(jsonSchema.isObjectSchema()); @@ -212,15 +213,16 @@ public void testGeneratingJsonSchemaWithFilters() throws Exception { * Additional unit test for verifying that schema object itself can be * properly serialized */ + @SuppressWarnings("unchecked") public void testSchemaSerialization() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(SimpleBean.class); Map result = writeAndMap(MAPPER, jsonSchema); assertNotNull(result); // no need to check out full structure, just basics... assertEquals("object", result.get("type")); // only add 'required' if it is true... - assertNull(result.get("required")); + assertThat((List)result.get("required"), containsInAnyOrder("property5")); assertNotNull(result.get("properties")); } @@ -228,7 +230,7 @@ public void testSchemaSerialization() throws Exception { * Test for [JACKSON-454] */ public void testThatObjectsHaveNoItems() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(TrivialBean.class); Map result = writeAndMap(MAPPER, jsonSchema); // can we count on ordering being stable? I think this is true with current ObjectNode impl @@ -238,7 +240,7 @@ public void testThatObjectsHaveNoItems() throws Exception { @SuppressWarnings({"unchecked", "rawtypes"}) public void testSchemaId() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(BeanWithId.class); Map result = writeAndMap(MAPPER, jsonSchema); @@ -246,6 +248,7 @@ public void testSchemaId() throws Exception { { put("type", "object"); put("id", "urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:BeanWithId"); + put("$schema", JsonSchemaVersion.DRAFT_V4.getSchemaString()); put("properties", new HashMap() { { @@ -262,17 +265,17 @@ public void testSchemaId() throws Exception { } public void testSimpleMap() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(StringMap.class); Map result = writeAndMap(MAPPER, jsonSchema); assertNotNull(result); String mapSchemaStr = MAPPER.writeValueAsString(jsonSchema); - assertEquals("{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}", mapSchemaStr); + assertEquals("{'type':'object','additionalProperties':{'type':'string'}}", mapSchemaStr.replaceAll("\"", "'")); } public void testSinglePropertyDependency() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(SimpleBean.class); ((ObjectSchema) jsonSchema).addSimpleDependency("property1", "property2"); @@ -280,18 +283,20 @@ public void testSinglePropertyDependency() throws Exception { assertNotNull(result); String schemaString = MAPPER.writeValueAsString(jsonSchema); - assertEquals("{\"type\":\"object\"," + - "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean\"," + - "\"dependencies\":{\"property1\":[\"property2\"]}," + - "\"properties\":{\"property1\":{\"type\":\"integer\"}" + - ",\"property2\":{\"type\":\"string\"}," + - "\"property3\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}," + - "\"property4\":{\"type\":\"array\",\"items\":{\"type\":\"number\"}}," + - "\"property5\":{\"type\":\"string\",\"required\":true}}}", schemaString); + assertEquals("{'type':'object'," + + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + + "'required':['property5']," + + "'dependencies':{'property1':['property2']}," + + "'properties':{'property1':{'type':'integer'}" + + ",'property2':{'type':'string'}," + + "'property3':{'type':'array','items':{'type':'string'}}," + + "'property4':{'type':'array','items':{'type':'number'}}," + + "'property5':{'type':'string'}}}", schemaString.replaceAll("\"", "'")); } public void testMultiplePropertyDependencies() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(SimpleBean.class); ((ObjectSchema) jsonSchema).addSimpleDependency("property1", "property2"); ((ObjectSchema) jsonSchema).addSimpleDependency("property1", "property3"); @@ -302,18 +307,20 @@ public void testMultiplePropertyDependencies() throws Exception { assertNotNull(result); String schemaString = MAPPER.writeValueAsString(jsonSchema); - assertEquals("{\"type\":\"object\"," + - "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean\"," + - "\"dependencies\":{\"property1\":[\"property2\",\"property3\"],\"property2\":[\"property3\"]}," + - "\"properties\":{\"property1\":{\"type\":\"integer\"}" + - ",\"property2\":{\"type\":\"string\"}," + - "\"property3\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}," + - "\"property4\":{\"type\":\"array\",\"items\":{\"type\":\"number\"}}," + - "\"property5\":{\"type\":\"string\",\"required\":true}}}", schemaString); + assertEquals("{'type':'object'," + + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + + "'required':['property5']," + + "'dependencies':{'property1':['property2','property3'],'property2':['property3']}," + + "'properties':{'property1':{'type':'integer'}" + + ",'property2':{'type':'string'}," + + "'property3':{'type':'array','items':{'type':'string'}}," + + "'property4':{'type':'array','items':{'type':'number'}}," + + "'property5':{'type':'string'}}}", schemaString.replaceAll("\"", "'")); } public void testSchemaPropertyDependency() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); // Given this dependency schema JsonSchema schemaPropertyDependency = generator.generateSchema(DependencySchema.class); @@ -327,18 +334,20 @@ public void testSchemaPropertyDependency() throws Exception { // Test the generated value. String schemaString = MAPPER.writeValueAsString(simpleBeanSchema); - assertEquals("{\"type\":\"object\"," + - "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean\"," + - "\"dependencies\":{\"property1\":{\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema\",\"properties\":{\"property2\":{\"type\":\"string\",\"required\":true}}}}," + - "\"properties\":{\"property1\":{\"type\":\"integer\"}" + - ",\"property2\":{\"type\":\"string\"}," + - "\"property3\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}," + - "\"property4\":{\"type\":\"array\",\"items\":{\"type\":\"number\"}}," + - "\"property5\":{\"type\":\"string\",\"required\":true}}}", schemaString); + assertEquals("{'type':'object'," + + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + + "'required':['property5']," + + "'dependencies':{'property1':{'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema','required':['property2'],'properties':{'property2':{'type':'string'}}}}," + + "'properties':{'property1':{'type':'integer'}" + + ",'property2':{'type':'string'}," + + "'property3':{'type':'array','items':{'type':'string'}}," + + "'property4':{'type':'array','items':{'type':'number'}}," + + "'property5':{'type':'string'}}}", schemaString.replaceAll("\"", "'")); } public void testSchemaPropertyDependencies() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); // Given this dependency schema JsonSchema schemaPropertyDependency = generator.generateSchema(DependencySchema.class); @@ -355,19 +364,20 @@ public void testSchemaPropertyDependencies() throws Exception { String schemaString = MAPPER.writeValueAsString(simpleBeanSchema); assertEquals( "{" + - "\"type\":\"object\"," + - "\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean\"," + - "\"dependencies\":{" + - "\"property1\":{\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema\",\"properties\":{\"property2\":{\"type\":\"string\",\"required\":true}}}," + - "\"property3\":{\"id\":\"urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema\",\"properties\":{\"property2\":{\"type\":\"string\",\"required\":true}}}}," + - "\"properties\":{" + - "\"property1\":{\"type\":\"integer\"}" + - ",\"property2\":{\"type\":\"string\"}," + - "\"property3\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}," + - "\"property4\":{\"type\":\"array\",\"items\":{\"type\":\"number\"}}," + - "\"property5\":{\"type\":\"string\",\"required\":true}" + - "}" + - "}", schemaString); + "'type':'object'," + + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:SimpleBean'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + + "'required':['property5']," + + "'dependencies':{" + + "'property1':{'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema','required':['property2'],'properties':{'property2':{'type':'string'}}}," + + "'property3':{'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestGenerateJsonSchema:DependencySchema','required':['property2'],'properties':{'property2':{'type':'string'}}}}," + + "'properties':{" + + "'property1':{'type':'integer'}" + + ",'property2':{'type':'string'}," + + "'property3':{'type':'array','items':{'type':'string'}}," + + "'property4':{'type':'array','items':{'type':'number'}}," + + "'property5':{'type':'string'}" + "}" + + "}", schemaString.replaceAll("\"", "'")); } /* @@ -379,7 +389,7 @@ public void testSchemaPropertyDependencies() throws Exception { public void testInvalidCall() throws Exception { // not ok to pass null try { - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); MAPPER.acceptJsonFormatVisitor((JavaType) null, visitor); fail("Should have failed"); } catch (IllegalArgumentException iae) { diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJDKTypes.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJDKTypes.java index 2c39ec5e..6b31cec3 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJDKTypes.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJDKTypes.java @@ -1,8 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema; -import java.math.*; +import java.math.BigDecimal; +import java.math.BigInteger; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class TestJDKTypes extends SchemaTestBase { @@ -13,7 +15,7 @@ public class TestJDKTypes extends SchemaTestBase */ public void testSimpleNumbers() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema; schema = generator.generateSchema(Long.class); diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJsonValue.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJsonValue.java index 9d958c00..065a70b9 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJsonValue.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestJsonValue.java @@ -4,10 +4,9 @@ import java.util.Map; import com.fasterxml.jackson.annotation.JsonValue; - import com.fasterxml.jackson.databind.ObjectMapper; - import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; @@ -48,12 +47,12 @@ public void testJsonValueAnnotation() throws Exception { ObjectMapper mapper = new ObjectMapper(); - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); mapper.acceptJsonFormatVisitor(mapper.constructType(Leaf.class), visitor); JsonSchema schemaExp = visitor.finalSchema(); assertNotNull(schemaExp); - visitor = new SchemaFactoryWrapper(); + visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); mapper.acceptJsonFormatVisitor(mapper.constructType(ContainerWithAsValue.class), visitor); JsonSchema schemaAct = visitor.finalSchema(); assertNotNull(schemaAct); @@ -78,7 +77,7 @@ public void testJsonValueForCollection() throws Exception { ObjectMapper mapper = new ObjectMapper(); - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); mapper.acceptJsonFormatVisitor(mapper.constructType(Issue34Bean.class), visitor); JsonSchema schema = visitor.finalSchema(); assertNotNull(schema); @@ -90,7 +89,7 @@ public void testJsonValueForCollection() throws Exception // For [module-jsonSchema#100] public void testMapViaJsonValue() throws Exception { ObjectMapper m = new ObjectMapper(); - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); m.acceptJsonFormatVisitor(m.constructType(MapViaJsonValue.class), visitor); JsonSchema schema = visitor.finalSchema(); assertType(schema, ObjectSchema.class); diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestPropertyOrderInSchema.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestPropertyOrderInSchema.java index d397838b..675d1949 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestPropertyOrderInSchema.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestPropertyOrderInSchema.java @@ -1,8 +1,8 @@ package com.fasterxml.jackson.module.jsonSchema; import com.fasterxml.jackson.annotation.JsonPropertyOrder; - import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class TestPropertyOrderInSchema extends SchemaTestBase { @@ -79,14 +79,14 @@ public void setA(String a) { public void testAnnotationOrder() throws Exception { ObjectMapper MAPPER = objectMapper(); - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(Bean.class); assertEquals(jsonSchema.asObjectSchema().getProperties().keySet().toString(), "[c, b, a]"); } public void testAlphabeticOrder() throws Exception { final ObjectMapper MAPPER = objectMapper(); - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(Bean2.class); assertEquals(jsonSchema.asObjectSchema().getProperties().keySet().toString(), "[a, b, c]"); } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestReadJsonSchema.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestReadJsonSchema.java index 0617f791..f6ca713c 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestReadJsonSchema.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestReadJsonSchema.java @@ -1,17 +1,22 @@ package com.fasterxml.jackson.module.jsonSchema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializable; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; -import java.io.IOException; -import java.util.*; - /** * Trivial test to ensure {@link JsonSchema} can be also deserialized */ @@ -112,7 +117,7 @@ public void testMapTypes() throws Exception { } public void testAdditionalItems() throws Exception { - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(SchemableArrays.class), visitor); JsonSchema jsonSchema = visitor.finalSchema(); assertNotNull(jsonSchema); @@ -142,9 +147,10 @@ public void testStructuredEnumTypes() throws Exception { public void _testSimple(Class type) throws Exception { // Create a schema - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(type), visitor); JsonSchema jsonSchema = visitor.finalSchema(); + assertNotNull(jsonSchema); _testSimple(type.getSimpleName(), jsonSchema); @@ -202,7 +208,7 @@ public void testOneOf() throws Exception " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + " \"description\": \"schema for an fstab entry\",\n" + " \"type\": \"object\",\n" + - //" \"required\": [ \"storage\" ],\n" + + " \"required\": [ \"storage\" ],\n" + " \"properties\": {\n" + " \"storage\": {\n" + " \"type\": \"object\",\n" + @@ -236,4 +242,20 @@ public void testOneOf() throws Exception assertNotNull(schema.getProperties().get("storage").asObjectSchema().getOneOf()); assertEquals(4,schema.getProperties().get("storage").asObjectSchema().getOneOf().size()); } + + /** + * Verifies that additional properties that aren't part of the standard are + * deserialized properly. + */ + public void testDeserializeNonStandardProperties() throws Exception { + String schemaStr = "{\"type\":\"object\",\"properties\":{\"mapSizes\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"number\"}}},\"additionalProperties\":false,\"validationMessage\":\"Validation Message\"}"; + JsonSchema schema = MAPPER.readValue(schemaStr, JsonSchema.class); + String newSchemaStr = MAPPER.writeValueAsString(schema); + + assertEquals(schemaStr.replaceAll("\\s", "").length(), newSchemaStr.replaceAll("\\s", "").length()); + + JsonNode node = MAPPER.readTree(schemaStr); + JsonNode finalNode = MAPPER.readTree(newSchemaStr); + assertEquals(node, finalNode); + } } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestTypeGeneration.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestTypeGeneration.java index 64512807..a479dbe1 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestTypeGeneration.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestTypeGeneration.java @@ -1,9 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema; -import com.fasterxml.jackson.databind.ObjectMapper; - import java.util.Date; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; + public class TestTypeGeneration extends SchemaTestBase { static class Issue14Bean @@ -22,11 +23,12 @@ static class Issue14Bean // [Issue#14]: multiple type attributes public void testCorrectType() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema jsonSchema = generator.generateSchema(Issue14Bean.class); String json = MAPPER.writeValueAsString(jsonSchema).replace('"', '\''); final String EXP = "{'type':'object'," + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestTypeGeneration:Issue14Bean'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "'properties':{'date':{'type':'integer','format':'utc-millisec'}}}"; assertEquals(EXP, json); } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestUnwrapping.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestUnwrapping.java index 4073ed0f..b2544ce7 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestUnwrapping.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestUnwrapping.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class TestUnwrapping extends SchemaTestBase { @@ -27,7 +28,7 @@ static class Name { public void testUnwrapping() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema = generator.generateSchema(UnwrappingRoot.class); String json = MAPPER.writeValueAsString(schema).replace('"', '\''); @@ -35,6 +36,7 @@ public void testUnwrapping() throws Exception //System.err.println("JSON -> "+MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(schema)); String EXP = "{'type':'object'," + "'id':'urn:jsonschema:com:fasterxml:jackson:module:jsonSchema:TestUnwrapping:UnwrappingRoot'," + + "'$schema':'" + JsonSchemaVersion.DRAFT_V4.getSchemaString() + "'," + "'properties':{'age':{'type':'integer'},'name.first':{'type':'string'},'name.last':{'type':'string'}}}"; assertEquals(EXP, json); } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVersions.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVersions.java new file mode 100644 index 00000000..9ec0e9d7 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVersions.java @@ -0,0 +1,137 @@ +package com.fasterxml.jackson.module.jsonSchema; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Map; + +import javax.validation.constraints.NotNull; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.customProperties.ValidationSchemaPropertyProcessorManagerFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; +import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; + +/** + * @author amerritt + */ +public class TestVersions extends SchemaTestBase { + public static class Internal { + @NotNull + String nameMandatory; + + public String getNameMandatory() { + return nameMandatory; + } + + public void setNameMandatory(String nameMandatory) { + this.nameMandatory = nameMandatory; + } + } + + @Test + public void testAddingValidationConstraints_DefaultV4() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + assertThat(objectSchema.getRequiredPropertyNames(), containsInAnyOrder("nameMandatory")); + } + + @Test(expected = RuntimeException.class) + public void testAddingValidationConstraints_DefaultV4_ExceptionOnGet() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + objectSchema.getRequiredPropertyNames(); //Should blow up... + } + + @Test + public void testAddingValidationConstraints_V4() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion.DRAFT_V4, testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + assertThat(objectSchema.getRequiredPropertyNames(), containsInAnyOrder("nameMandatory")); + } + + @Test(expected = RuntimeException.class) + public void testAddingValidationConstraints_V4_ExceptionOnGet() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion.DRAFT_V4, testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + objectSchema.getRequiredPropertyNames(); //Should blow up... + } + + @Test + public void testAddingValidationConstraints_V3() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion.DRAFT_V3, testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + printJsonSchema(objectSchema); + Map properties = objectSchema.getProperties(); + assertNotNull(properties); + + JsonSchema propertySchema = properties.get("nameMandatory"); + assertNotNull(propertySchema); + assertThat(propertySchema.getRequired(), is(true)); + + } + + @Test(expected = RuntimeException.class) + public void testAddingValidationConstraints_V3_ExceptionOnGet() throws Exception { + Class testType = Internal.class; + + ValidationSchemaPropertyProcessorManagerFactoryWrapper visitor = new ValidationSchemaPropertyProcessorManagerFactoryWrapper(JsonSchemaVersion.DRAFT_V3, testType); + ObjectMapper mapper = new ObjectMapper(); + mapper.acceptJsonFormatVisitor(testType, visitor); + JsonSchema jsonSchema = visitor.finalSchema(); + + assertNotNull("schema should not be null.", jsonSchema); + assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); + ObjectSchema objectSchema = jsonSchema.asObjectSchema(); + objectSchema.getRequiredPropertyNames(); //should blow up... + } + + + void printJsonSchema(JsonSchema jsonSchema) throws JsonProcessingException { + System.err.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema)); + } +} diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVisitorContext.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVisitorContext.java index 647750f6..a28e3cfe 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVisitorContext.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TestVisitorContext.java @@ -1,7 +1,9 @@ package com.fasterxml.jackson.module.jsonSchema; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class TestVisitorContext extends SchemaTestBase { @@ -92,7 +94,7 @@ public void testSchemaGeneration() throws Exception { private String generateSchema(Class clazz) throws Exception { MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true); - SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(); + SchemaFactoryWrapper visitor = new SchemaFactoryWrapper(JsonSchemaVersion.DRAFT_V4); MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(clazz), visitor); return MAPPER.writeValueAsString(visitor.finalSchema()); } diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TitleSchemaFactoryWrapperTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TitleSchemaFactoryWrapperTest.java index 612408c1..84a01ed7 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/TitleSchemaFactoryWrapperTest.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/TitleSchemaFactoryWrapperTest.java @@ -1,9 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema; -import junit.framework.TestCase; - import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.jsonSchema.customProperties.TitleSchemaFactoryWrapper; +import com.fasterxml.jackson.module.jsonSchema.annotation.JsonSchemaTitle; +import com.fasterxml.jackson.module.jsonSchema.customProperties.TitleSchemaPropertyProcessorManagerFactoryWrapper; + +import junit.framework.TestCase; public class TitleSchemaFactoryWrapperTest extends TestCase{ @@ -11,20 +12,24 @@ public class Pet { public String genus; } + @JsonSchemaTitle("Person") public class Person { public String name; public String hat; + @JsonSchemaTitle("Pet") public Pet pet; } public void testAddingTitle() throws Exception { - TitleSchemaFactoryWrapper visitor = new TitleSchemaFactoryWrapper(); + TitleSchemaPropertyProcessorManagerFactoryWrapper visitor = new TitleSchemaPropertyProcessorManagerFactoryWrapper(); ObjectMapper mapper = new ObjectMapper(); mapper.acceptJsonFormatVisitor(Person.class, visitor); JsonSchema schema = visitor.finalSchema(); + System.err.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(schema)); + assertTrue("schema should be an objectSchema.", schema.isObjectSchema()); String title = schema.asObjectSchema().getTitle(); assertNotNull(title); diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java deleted file mode 100644 index 2f25605d..00000000 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java +++ /dev/null @@ -1,360 +0,0 @@ -package com.fasterxml.jackson.module.jsonSchema; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.jsonSchema.customProperties.ValidationSchemaFactoryWrapper; -import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema; -import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema; -import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; - -import javax.validation.constraints.*; -import java.util.List; -import java.util.Map; - -/** - * @author cponomaryov - */ -public class ValidationSchemaFactoryWrapperTest extends SchemaTestBase { - - public static class ValidationBean { - - /* - /********************************************************** - /* Array fields - /********************************************************** - */ - - private List listWithoutConstraints; - - @Size(min = 1) - private List listWithMinSize; - - @Size(max = 2) - private List listWithMaxSize; - - @Size(min = 3, max = 4) - private List listWithMinAndMaxSize; - - /* - /********************************************************** - /* Number fields - /********************************************************** - */ - - private int numberWithoutConstraints; - - @Min(5) - private int numberWithMin; - - @DecimalMin("5.5") - private int numberWithDecimalMin; - - @Max(6) - private int numberWithMax; - - @DecimalMax("6.5") - private int numberWithDecimalMax; - - @Min(7) - @Max(8) - private int numberWithMinAndMax; - - @Min(9) - @DecimalMax("9.5") - private int numberWithMinAndDecimalMax; - - @DecimalMin("10.5") - @Max(11) - private int numberWithDecimalMinAndMax; - - @DecimalMin("11.5") - @DecimalMax("12.5") - private int numberWithDecimalMinAndDecimalMax; - - /* - /********************************************************** - /* String fields - /********************************************************** - */ - - private String stringWithoutConstraints; - - @Size(min = 13) - private String stringWithMinSize; - - @Size(max = 14) - private String stringWithMaxSize; - - @Size(min = 15, max = 16) - private String stringWithMinAndMaxSize; - - @Pattern(regexp = "[a-z]+") - private String stringWithPattern; - - /* - /********************************************************** - /* Nullable and not nullable fields - /********************************************************** - */ - - private String nullable; - - @NotNull - private String notNullable; - - public List getListWithoutConstraints() { - return listWithoutConstraints; - } - - public void setListWithoutConstraints(List listWithoutConstraints) { - this.listWithoutConstraints = listWithoutConstraints; - } - - public List getListWithMinSize() { - return listWithMinSize; - } - - public void setListWithMinSize(List listWithMinSize) { - this.listWithMinSize = listWithMinSize; - } - - public List getListWithMaxSize() { - return listWithMaxSize; - } - - public void setListWithMaxSize(List listWithMaxSize) { - this.listWithMaxSize = listWithMaxSize; - } - - public List getListWithMinAndMaxSize() { - return listWithMinAndMaxSize; - } - - public void setListWithMinAndMaxSize(List listWithMinAndMaxSize) { - this.listWithMinAndMaxSize = listWithMinAndMaxSize; - } - - public int getNumberWithoutConstraints() { - return numberWithoutConstraints; - } - - public void setNumberWithoutConstraints(int numberWithoutConstraints) { - this.numberWithoutConstraints = numberWithoutConstraints; - } - - public int getNumberWithMin() { - return numberWithMin; - } - - public void setNumberWithMin(int numberWithMin) { - this.numberWithMin = numberWithMin; - } - - public int getNumberWithDecimalMin() { - return numberWithDecimalMin; - } - - public void setNumberWithDecimalMin(int numberWithDecimalMin) { - this.numberWithDecimalMin = numberWithDecimalMin; - } - - public int getNumberWithMax() { - return numberWithMax; - } - - public void setNumberWithMax(int numberWithMax) { - this.numberWithMax = numberWithMax; - } - - public int getNumberWithDecimalMax() { - return numberWithDecimalMax; - } - - public void setNumberWithDecimalMax(int numberWithDecimalMax) { - this.numberWithDecimalMax = numberWithDecimalMax; - } - - public int getNumberWithMinAndMax() { - return numberWithMinAndMax; - } - - public void setNumberWithMinAndMax(int numberWithMinAndMax) { - this.numberWithMinAndMax = numberWithMinAndMax; - } - - public int getNumberWithMinAndDecimalMax() { - return numberWithMinAndDecimalMax; - } - - public void setNumberWithMinAndDecimalMax(int numberWithMinAndDecimalMax) { - this.numberWithMinAndDecimalMax = numberWithMinAndDecimalMax; - } - - public int getNumberWithDecimalMinAndMax() { - return numberWithDecimalMinAndMax; - } - - public void setNumberWithDecimalMinAndMax(int numberWithDecimalMinAndMax) { - this.numberWithDecimalMinAndMax = numberWithDecimalMinAndMax; - } - - public int getNumberWithDecimalMinAndDecimalMax() { - return numberWithDecimalMinAndDecimalMax; - } - - public void setNumberWithDecimalMinAndDecimalMax(int numberWithDecimalMinAndDecimalMax) { - this.numberWithDecimalMinAndDecimalMax = numberWithDecimalMinAndDecimalMax; - } - - public String getStringWithoutConstraints() { - return stringWithoutConstraints; - } - - public void setStringWithoutConstraints(String stringWithoutConstraints) { - this.stringWithoutConstraints = stringWithoutConstraints; - } - - public String getStringWithMinSize() { - return stringWithMinSize; - } - - public void setStringWithMinSize(String stringWithMinSize) { - this.stringWithMinSize = stringWithMinSize; - } - - public String getStringWithMaxSize() { - return stringWithMaxSize; - } - - public void setStringWithMaxSize(String stringWithMaxSize) { - this.stringWithMaxSize = stringWithMaxSize; - } - - public String getStringWithMinAndMaxSize() { - return stringWithMinAndMaxSize; - } - - public void setStringWithMinAndMaxSize(String stringWithMinAndMaxSize) { - this.stringWithMinAndMaxSize = stringWithMinAndMaxSize; - } - - public String getStringWithPattern() { - return stringWithPattern; - } - - public void setStringWithPattern(final String stringWithPattern) { - this.stringWithPattern = stringWithPattern; - } - - public String getNullable() { - return nullable; - } - - public void setNullable(String nullable) { - this.nullable = nullable; - } - - public String getNotNullable() { - return notNullable; - } - - public void setNotNullable(String notNullable) { - this.notNullable = notNullable; - } - } - - /* - /********************************************************** - /* Unit tests, success - /********************************************************** - */ - - private Object[][] listTestData() { - return new Object[][] {{"listWithoutConstraints", null, null}, - {"listWithMinSize", 1, null}, - {"listWithMaxSize", null, 2}, - {"listWithMinAndMaxSize", 3, 4}}; - } - - private Object[][] numberTestData() { - return new Object[][] {{"numberWithoutConstraints", null, null}, - {"numberWithMin", 5d, null}, - {"numberWithDecimalMin", 5.5, null}, - {"numberWithMax", null, 6d}, - {"numberWithDecimalMax", null, 6.5}, - {"numberWithMinAndMax", 7d, 8d}, - {"numberWithMinAndDecimalMax", 9d, 9.5}, - {"numberWithDecimalMinAndMax", 10.5, 11d}, - {"numberWithDecimalMinAndDecimalMax", 11.5, 12.5}}; - } - - private Object[][] stringTestData() { - return new Object[][] {{"stringWithoutConstraints", null, null}, - {"stringWithMinSize", 13, null}, - {"stringWithMaxSize", null, 14}, - {"stringWithMinAndMaxSize", 15, 16}}; - } - - private Object[][] stringPatternTestData() { - return new Object[][] {{"stringWithPattern", "[a-z]+"}, - {"stringWithoutConstraints", null}}; - } - - private Object[][] notNullTestData() { - return new Object[][] { - {"nullable", null}, - {"notNullable", true}}; - } - - /** - * Test set validation constraints - */ - public void testAddingValidationConstraints() throws Exception { - ValidationSchemaFactoryWrapper visitor = new ValidationSchemaFactoryWrapper(); - ObjectMapper mapper = new ObjectMapper(); - - mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor); - JsonSchema jsonSchema = visitor.finalSchema(); - - assertNotNull("schema should not be null.", jsonSchema); - assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema()); - Map properties = jsonSchema.asObjectSchema().getProperties(); - assertNotNull(properties); - for (Object[] testCase : listTestData()) { - JsonSchema propertySchema = properties.get(testCase[0]); - assertNotNull(propertySchema); - assertTrue(propertySchema.isArraySchema()); - ArraySchema arraySchema = propertySchema.asArraySchema(); - assertEquals(testCase[1], arraySchema.getMinItems()); - assertEquals(testCase[2], arraySchema.getMaxItems()); - } - for (Object[] testCase : numberTestData()) { - JsonSchema propertySchema = properties.get(testCase[0]); - assertNotNull(propertySchema); - assertTrue(propertySchema.isNumberSchema()); - NumberSchema numberSchema = propertySchema.asNumberSchema(); - assertEquals(testCase[1], numberSchema.getMinimum()); - assertEquals(testCase[2], numberSchema.getMaximum()); - } - for (Object[] testCase : stringTestData()) { - JsonSchema propertySchema = properties.get(testCase[0]); - assertNotNull(propertySchema); - assertTrue(propertySchema.isStringSchema()); - StringSchema stringSchema = propertySchema.asStringSchema(); - assertEquals(testCase[1], stringSchema.getMinLength()); - assertEquals(testCase[2], stringSchema.getMaxLength()); - } - for (Object[] testCase : stringPatternTestData()) { - JsonSchema propertySchema = properties.get(testCase[0]); - assertNotNull(propertySchema); - assertTrue(propertySchema.isStringSchema()); - StringSchema stringSchema = propertySchema.asStringSchema(); - assertEquals(testCase[1], stringSchema.getPattern()); - } - for (Object[] testCase : notNullTestData()) { - JsonSchema propertySchema = properties.get(testCase[0]); - assertNotNull(propertySchema); - assertEquals(testCase[1], propertySchema.getRequired()); - } - } - -} diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/MapTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/MapTest.java index 857c051a..d81e88ed 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/MapTest.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/MapTest.java @@ -3,10 +3,10 @@ import java.util.Map; 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.TestBase; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; // for [module-jsonSchema#89] public class MapTest extends TestBase @@ -33,7 +33,7 @@ public Map getCounts() { public void testSimpleMapKeyType89() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema; schema = generator.generateSchema(MapBean.class); diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/TestBinaryType.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/TestBinaryType.java index 9c58a78c..3c72b137 100644 --- a/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/TestBinaryType.java +++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/failing/TestBinaryType.java @@ -1,10 +1,10 @@ package com.fasterxml.jackson.module.jsonSchema.failing; 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.SchemaTestBase; +import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory.JsonSchemaVersion; public class TestBinaryType extends SchemaTestBase { @@ -15,7 +15,7 @@ public class TestBinaryType extends SchemaTestBase */ public void testBinaryType() throws Exception { - JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER); + JsonSchemaGenerator generator = new JsonSchemaGenerator(MAPPER, JsonSchemaVersion.DRAFT_V4); JsonSchema schema; schema = generator.generateSchema(byte[].class);