From 76b75aece033b33a8cf67fd83601a99e94cb6819 Mon Sep 17 00:00:00 2001 From: Carsten Wickner Date: Wed, 1 Apr 2020 00:17:06 +0200 Subject: [PATCH 1/4] feat: allow accessing container item annotations --- .../jsonschema/generator/FieldScope.java | 29 ++++++++++++ .../jsonschema/generator/MemberScope.java | 20 +++++++++ .../jsonschema/generator/MethodScope.java | 45 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java index e79c9dae..7248bb5a 100644 --- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java +++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java @@ -21,6 +21,8 @@ import com.fasterxml.classmate.members.ResolvedField; import com.fasterxml.classmate.members.ResolvedMethod; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; import java.util.stream.Stream; @@ -67,6 +69,11 @@ public FieldScope withOverriddenName(String overriddenName) { this.isFakeContainerItemScope(), this.getContext()); } + @Override + public FieldScope asFakeContainerItemScope() { + return (FieldScope) super.asFakeContainerItemScope(); + } + /** * Returns the name to be used to reference this field in its parent's "properties". * @@ -117,6 +124,18 @@ public boolean hasGetter() { return this.findGetter() != null; } + @Override + public A getContainerItemAnnotation(Class annotationClass) { + AnnotatedType annotatedType = this.getRawMember().getAnnotatedType(); + if (annotatedType instanceof AnnotatedParameterizedType) { + AnnotatedType[] typeArguments = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments(); + if (typeArguments.length > 0) { + return typeArguments[0].getAnnotation(annotationClass); + } + } + return null; + } + @Override public A getAnnotationConsideringFieldAndGetter(Class annotationClass) { A annotation = this.getAnnotation(annotationClass); @@ -126,4 +145,14 @@ public A getAnnotationConsideringFieldAndGetter(Class } return annotation; } + + @Override + public A getContainerItemAnnotationConsideringFieldAndGetter(Class annotationClass) { + A annotation = this.getContainerItemAnnotation(annotationClass); + if (annotation == null) { + MemberScope associatedGetter = this.findGetter(); + annotation = associatedGetter == null ? null : associatedGetter.getContainerItemAnnotation(annotationClass); + } + return annotation; + } } diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java index 7469f8ee..144e8508 100644 --- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java +++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java @@ -263,6 +263,16 @@ public A getAnnotation(Class annotationClass) { return this.member.get(annotationClass); } + /** + * Return the annotation of the given type on the member's container item (i.e. first type parameter if there is one), if such an annotation is + * present on either the field or its getter. + * + * @param type of annotation + * @param annotationClass type of annotation + * @return annotation instance (or {@code null} if no annotation of the given type is present) + */ + public abstract A getContainerItemAnnotation(Class annotationClass); + /** * Return the annotation of the given type on the member, if such an annotation is present on either the field or its getter. * @@ -272,6 +282,16 @@ public A getAnnotation(Class annotationClass) { */ public abstract A getAnnotationConsideringFieldAndGetter(Class annotationClass); + /** + * Return the annotation of the given type on the member's container item (i.e. first type parameter if there is one), if such an annotation is + * present on either the field or its getter. + * + * @param type of annotation + * @param annotationClass type of annotation + * @return annotation instance (or {@code null} if no annotation of the given type is present) + */ + public abstract A getContainerItemAnnotationConsideringFieldAndGetter(Class annotationClass); + /** * Returns the name to be used to reference this member in its parent's "properties". * diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java index 1735cc8c..537093f7 100644 --- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java +++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java @@ -20,6 +20,8 @@ import com.fasterxml.classmate.ResolvedTypeWithMembers; import com.fasterxml.classmate.members.ResolvedMethod; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; import java.lang.reflect.Method; import java.util.List; import java.util.stream.Collectors; @@ -69,6 +71,11 @@ public MethodScope withOverriddenName(String overriddenName) { this.isFakeContainerItemScope(), this.getContext()); } + @Override + public MethodScope asFakeContainerItemScope() { + return (MethodScope) super.asFakeContainerItemScope(); + } + /** * Indicating whether the method is declared as {@code void}, i.e. has no return value. * @@ -142,6 +149,34 @@ public boolean isGetter() { return this.findGetterField() != null; } + /** + * Return the annotation of the given type on the method or its return type, if such an annotation is present. + * + * @param type of annotation to look-up + * @param annotationClass annotation class to look up instance on member for + * @return annotation instance (or {@code null} if no annotation of the given type is present + */ + @Override + public A getAnnotation(Class annotationClass) { + A annotation = super.getAnnotation(annotationClass); + if (annotation == null) { + annotation = this.getRawMember().getAnnotatedReturnType().getAnnotation(annotationClass); + } + return annotation; + } + + @Override + public A getContainerItemAnnotation(Class annotationClass) { + AnnotatedType annotatedReturnType = this.getRawMember().getAnnotatedReturnType(); + if (annotatedReturnType instanceof AnnotatedParameterizedType) { + AnnotatedType[] typeArguments = ((AnnotatedParameterizedType) annotatedReturnType).getAnnotatedActualTypeArguments(); + if (typeArguments.length > 0) { + return typeArguments[0].getAnnotation(annotationClass); + } + } + return null; + } + @Override public A getAnnotationConsideringFieldAndGetter(Class annotationClass) { A annotation = this.getAnnotation(annotationClass); @@ -152,6 +187,16 @@ public A getAnnotationConsideringFieldAndGetter(Class return annotation; } + @Override + public A getContainerItemAnnotationConsideringFieldAndGetter(Class annotationClass) { + A annotation = this.getContainerItemAnnotation(annotationClass); + if (annotation == null) { + MemberScope associatedField = this.findGetterField(); + annotation = associatedField == null ? null : associatedField.getContainerItemAnnotation(annotationClass); + } + return annotation; + } + /** * Returns the name to be used to reference this method in its parent's "properties". * From 3163dbc6530406ff1033b8635f63570d133f3f50 Mon Sep 17 00:00:00 2001 From: Carsten Wickner Date: Wed, 1 Apr 2020 00:17:55 +0200 Subject: [PATCH 2/4] feat: consider container item annotations --- .../validation/JavaxValidationModule.java | 38 +++++++++---------- .../javax/validation/IntegrationTest.java | 6 +-- .../validation/integration-test-result.json | 12 ++++-- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java index 9252aa28..901a7f78 100644 --- a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java +++ b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java @@ -130,32 +130,30 @@ private void applyToConfigPart(SchemaGeneratorConfigPart configPart) { } /** - * Retrieves the annotation instance of the given type, either from the field it self or (if not present) from its getter. + * Retrieves the annotation instance of the given type, either from the field itself or (if not present) from its getter. + *
+ * If the given field/method represents only a container item of the actual declared type, that container item's annotations are being checked. * * @param
type of annotation * @param member field or method to retrieve annotation instance from (or from a field's getter or getter method's field) * @param annotationClass type of annotation * @param validationGroupsLookup how to look-up the associated validation groups of an annotation instance * @return annotation instance (or {@code null}) - * @see MemberScope#getAnnotation(Class) - * @see FieldScope#findGetter() - * @see MethodScope#findGetterField() + * @see MemberScope#getAnnotationConsideringFieldAndGetter(Class) + * @see MemberScope#getContainerItemAnnotationConsideringFieldAndGetter(Class) */ protected A getAnnotationFromFieldOrGetter(MemberScope member, Class annotationClass, Function[]> validationGroupsLookup) { - A annotation = member.getAnnotationConsideringFieldAndGetter(annotationClass); - if (annotation != null) { + A annotation; + if (member.isFakeContainerItemScope()) { + annotation = member.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass); + } else { + annotation = member.getAnnotationConsideringFieldAndGetter(annotationClass); + } + if (annotation != null && this.validationGroups != null) { Class[] associatedGroups = validationGroupsLookup.apply(annotation); - /* - * the annotation is deemed applicable in one of the following three cases: - * 1. Validation groups are specifically ignored (i.e. forValidationGroups() was never called or with null as only parameter) - * 2. No validation groups are specified on the annotation. - * 3. Some validation group(s) are specified on the annotation and at least one of them was provided via forValidationGroups(). - */ - if (this.validationGroups != null && associatedGroups.length > 0 - && Collections.disjoint(this.validationGroups, Arrays.asList(associatedGroups))) { - // ignore the looked-up annotation as it is not associated with one of the desired validation groups - annotation = null; + if (associatedGroups.length > 0 && Collections.disjoint(this.validationGroups, Arrays.asList(associatedGroups))) { + return null; } } return annotation; @@ -202,7 +200,7 @@ protected boolean isRequired(MemberScope member) { * @see Size */ protected Integer resolveArrayMinItems(MemberScope member) { - if (member.isContainerType() && !member.isFakeContainerItemScope()) { + if (member.isContainerType()) { Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups); if (sizeAnnotation != null && sizeAnnotation.min() > 0) { // minimum length greater than the default 0 was specified @@ -223,7 +221,7 @@ protected Integer resolveArrayMinItems(MemberScope member) { * @see Size */ protected Integer resolveArrayMaxItems(MemberScope member) { - if (member.isContainerType() && !member.isFakeContainerItemScope()) { + if (member.isContainerType()) { Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups); if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) { // maximum length below the default 2147483647 was specified @@ -243,7 +241,7 @@ protected Integer resolveArrayMaxItems(MemberScope member) { * @see NotBlank */ protected Integer resolveStringMinLength(MemberScope member) { - if (member.getType().isInstanceOf(CharSequence.class) && !member.isFakeContainerItemScope()) { + if (member.getType().isInstanceOf(CharSequence.class)) { Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups); if (sizeAnnotation != null && sizeAnnotation.min() > 0) { // minimum length greater than the default 0 was specified @@ -265,7 +263,7 @@ protected Integer resolveStringMinLength(MemberScope member) { * @see Size */ protected Integer resolveStringMaxLength(MemberScope member) { - if (member.getType().isInstanceOf(CharSequence.class) && !member.isFakeContainerItemScope()) { + if (member.getType().isInstanceOf(CharSequence.class)) { Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups); if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) { // maximum length below the default 2147483647 was specified diff --git a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java index 6a2bf265..8d2071b0 100644 --- a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java +++ b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/IntegrationTest.java @@ -90,11 +90,11 @@ static class TestClass { public Object nullObject; @NotNull - public List notNullList; + public List<@Min(2) @Max(2048) Integer> notNullList; @NotEmpty - public List notEmptyList; + public List<@DecimalMin(value = "0", inclusive = false) @DecimalMax(value = "1", inclusive = false) Double> notEmptyList; @Size(min = 3, max = 25) - public List sizeRangeList; + public List<@NotEmpty @Size(max = 100) String> sizeRangeList; @NotNull @Email(regexp = ".+@.+\\..+") diff --git a/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json b/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json index b3f5d248..49945170 100644 --- a/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json +++ b/jsonschema-module-javax-validation/src/test/resources/com/github/victools/jsonschema/module/javax/validation/integration-test-result.json @@ -19,7 +19,9 @@ "minItems": 1, "type": "array", "items": { - "type": "string" + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1 } }, "notEmptyPatternText": { @@ -35,7 +37,9 @@ "notNullList": { "type": "array", "items": { - "type": "string" + "type": "integer", + "minimum": 2, + "maximum": 2048 } }, "nullObject": {}, @@ -44,7 +48,9 @@ "maxItems": 25, "type": ["array", "null"], "items": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 100 } }, "sizeRangeText": { From 2ce4f2a16129be28c5b2e79da511bc6068432eb4 Mon Sep 17 00:00:00 2001 From: Carsten Wickner Date: Wed, 1 Apr 2020 00:18:20 +0200 Subject: [PATCH 3/4] chore: add test to ensure no exception is thrown for container item scope --- .../module/swagger15/IntegrationTest.java | 3 +- .../module/swagger15/SwaggerModuleTest.java | 87 +++++++++++-------- .../swagger15/integration-test-result.json | 5 +- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/IntegrationTest.java b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/IntegrationTest.java index b012b25d..94f99e2a 100644 --- a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/IntegrationTest.java +++ b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/IntegrationTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Scanner; import org.junit.Test; import org.skyscreamer.jsonassert.JSONAssert; @@ -80,7 +81,7 @@ static class TestClass { public Object hiddenField; @ApiModelProperty(name = "fieldWithOverriddenName") - public boolean originalFieldName; + public List originalFieldName; @ApiModelProperty(value = "field description", allowableValues = "A, B, C, D") public String fieldWithDescriptionAndAllowableValues; diff --git a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/SwaggerModuleTest.java b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/SwaggerModuleTest.java index cfbb5f5f..92f28ff3 100644 --- a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/SwaggerModuleTest.java +++ b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/SwaggerModuleTest.java @@ -27,6 +27,7 @@ import io.swagger.annotations.ApiModelProperty; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.List; import java.util.function.Predicate; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -219,19 +220,24 @@ public void testPropertyNameOverrideResolver(String fieldName, String expectedNa Object parametersForTestTitleResolver() { return new Object[][]{ - {"unannotatedField", null}, - {"exampleWithEmptyApiModel", null}, - {"exampleWithApiModelTitle", "example title"} + {"unannotatedField", false, null}, + {"exampleWithEmptyApiModel", false, null}, + {"exampleWithApiModelTitle", false, "example title"}, + {"listExampleWithApiModelTitle", false, null}, + {"listExampleWithApiModelTitle", true, "example title"} }; } @Test @Parameters - public void testTitleResolver(String fieldName, String expectedTitle) { + public void testTitleResolver(String fieldName, boolean asContainerItem, String expectedTitle) { new SwaggerModule().applyToConfigBuilder(this.configBuilder); TestType testType = new TestType(TestClassForDescription.class); FieldScope field = testType.getMemberField(fieldName); + if (asContainerItem) { + field = field.asFakeContainerItemScope(); + } ArgumentCaptor> captor = ArgumentCaptor.forClass(ConfigFunction.class); Mockito.verify(this.typesInGeneralConfigPart).withTitleResolver(captor.capture()); @@ -241,25 +247,30 @@ public void testTitleResolver(String fieldName, String expectedTitle) { Object parametersForTestDescriptionResolver() { return new Object[][]{ - {"unannotatedField", null, null}, - {"annotatedWithoutValueField", null, null}, - {"annotatedWithoutValueGetterField", null, null}, - {"annotatedField", "annotation value 1", null}, - {"annotatedGetterField", "annotation value 2", null}, - {"exampleWithEmptyApiModel", null, null}, - {"exampleWithApiModelDescription", null, "type description"}, - {"exampleWithTwoDescriptions", "property description", "type description"}, - {"exampleWithApiModelTitle", null, null} + {"unannotatedField", false, null, null}, + {"annotatedWithoutValueField", false, null, null}, + {"annotatedWithoutValueGetterField", false, null, null}, + {"annotatedField", false, "annotation value 1", null}, + {"annotatedGetterField", false, "annotation value 2", null}, + {"exampleWithEmptyApiModel", false, null, null}, + {"exampleWithApiModelDescription", false, null, "type description"}, + {"arrayExampleWithApiModelDescription", false, null, null}, + {"arrayExampleWithApiModelDescription", true, null, "type description"}, + {"exampleWithTwoDescriptions", false, "property description", "type description"}, + {"exampleWithApiModelTitle", false, null, null} }; } @Test @Parameters - public void testDescriptionResolver(String fieldName, String expectedMemberDescription, String expectedTypeDescription) { + public void testDescriptionResolver(String fieldName, boolean asContainerItem, String expectedMemberDescription, String expectedTypeDescription) { new SwaggerModule().applyToConfigBuilder(this.configBuilder); TestType testType = new TestType(TestClassForDescription.class); FieldScope field = testType.getMemberField(fieldName); + if (asContainerItem) { + field = field.asFakeContainerItemScope(); + } ArgumentCaptor> memberCaptor = ArgumentCaptor.forClass(ConfigFunction.class); Mockito.verify(this.fieldConfigPart).withDescriptionResolver(memberCaptor.capture()); @@ -307,34 +318,39 @@ public void testDescriptionResolverWithNoApiModelDescription(String fieldName, S Object parametersForTestNumberMinMaxResolvers() { return new Object[][]{ - {"unannotatedInt", null, null, null, null}, - {"minMinusHundredLong", "-100", null, null, null}, - {"minMinusHundredOnGetterLong", "-100", null, null, null}, - {"maxFiftyShort", null, null, "50", null}, - {"maxFiftyOnGetterShort", null, null, "50", null}, - {"tenToTwentyInclusiveDouble", "10.1", null, "20.2", null}, - {"tenToTwentyInclusiveOnGetterDouble", "10.1", null, "20.2", null}, - {"tenToTwentyExclusiveDecimal", null, "10.1", null, "20.2"}, - {"tenToTwentyExclusiveOnGetterDecimal", null, "10.1", null, "20.2"}, - {"positiveByte", null, BigDecimal.ZERO, null, null}, - {"positiveOnGetterByte", null, BigDecimal.ZERO, null, null}, - {"positiveOrZeroBigInteger", BigDecimal.ZERO, null, null, null}, - {"positiveOrZeroOnGetterBigInteger", BigDecimal.ZERO, null, null, null}, - {"negativeDecimal", null, null, null, BigDecimal.ZERO}, - {"negativeOnGetterDecimal", null, null, null, BigDecimal.ZERO}, - {"negativeOrZeroLong", null, null, BigDecimal.ZERO, null}, - {"negativeOrZeroOnGetterLong", null, null, BigDecimal.ZERO, null} + {"unannotatedInt", false, null, null, null, null}, + {"unannotatedIntArray", false, null, null, null, null}, + {"unannotatedIntArray", true, null, null, null, null}, + {"minMinusHundredLong", false, "-100", null, null, null}, + {"minMinusHundredOnGetterLong", false, "-100", null, null, null}, + {"maxFiftyShort", false, null, null, "50", null}, + {"maxFiftyOnGetterShort", false, null, null, "50", null}, + {"tenToTwentyInclusiveDouble", false, "10.1", null, "20.2", null}, + {"tenToTwentyInclusiveOnGetterDouble", false, "10.1", null, "20.2", null}, + {"tenToTwentyExclusiveDecimal", false, null, "10.1", null, "20.2"}, + {"tenToTwentyExclusiveOnGetterDecimal", false, null, "10.1", null, "20.2"}, + {"positiveByte", false, null, BigDecimal.ZERO, null, null}, + {"positiveOnGetterByte", false, null, BigDecimal.ZERO, null, null}, + {"positiveOrZeroBigInteger", false, BigDecimal.ZERO, null, null, null}, + {"positiveOrZeroOnGetterBigInteger", false, BigDecimal.ZERO, null, null, null}, + {"negativeDecimal", false, null, null, null, BigDecimal.ZERO}, + {"negativeOnGetterDecimal", false, null, null, null, BigDecimal.ZERO}, + {"negativeOrZeroLong", false, null, null, BigDecimal.ZERO, null}, + {"negativeOrZeroOnGetterLong", false, null, null, BigDecimal.ZERO, null} }; } @Test @Parameters - public void testNumberMinMaxResolvers(String fieldName, BigDecimal expectedMinInclusive, BigDecimal expectedMinExclusive, + public void testNumberMinMaxResolvers(String fieldName, boolean asContainerItem, BigDecimal expectedMinInclusive, BigDecimal expectedMinExclusive, BigDecimal expectedMaxInclusive, BigDecimal expectedMaxExclusive) throws Exception { new SwaggerModule().applyToConfigBuilder(this.configBuilder); TestType testType = new TestType(TestClassForNumberMinMax.class); FieldScope field = testType.getMemberField(fieldName); + if (asContainerItem) { + field = field.asFakeContainerItemScope(); + } ArgumentCaptor> minInclusiveCaptor = ArgumentCaptor.forClass(ConfigFunction.class); Mockito.verify(this.fieldConfigPart).withNumberInclusiveMinimumResolver(minInclusiveCaptor.capture()); @@ -409,14 +425,16 @@ private static class TestClassForDescription { int annotatedWithoutValueField; double annotatedWithoutValueGetterField; @ApiModelProperty(value = "annotation value 1") - Object annotatedField; + List annotatedField; boolean annotatedGetterField; ExampleWithEmptyApiModel exampleWithEmptyApiModel; ExampleWithApiModelDescription exampleWithApiModelDescription; + ExampleWithApiModelDescription[] arrayExampleWithApiModelDescription; @ApiModelProperty(value = "property description") ExampleWithApiModelDescription exampleWithTwoDescriptions; ExampleWithApiModelTitle exampleWithApiModelTitle; + List listExampleWithApiModelTitle; public String getUnannotatedField() { return this.unannotatedField; @@ -431,7 +449,7 @@ public double getAnnotatedWithoutValueGetterField() { return this.annotatedWithoutValueGetterField; } - public Object getAnnotatedField() { + public List getAnnotatedField() { return this.annotatedField; } @@ -489,6 +507,7 @@ public boolean isAnnotatedGetterField() { private static class TestClassForNumberMinMax { int unannotatedInt; + int[] unannotatedIntArray; @ApiModelProperty(allowableValues = "range[-100, infinity)") long minMinusHundredLong; long minMinusHundredOnGetterLong; diff --git a/jsonschema-module-swagger-1.5/src/test/resources/com/github/victools/jsonschema/module/swagger15/integration-test-result.json b/jsonschema-module-swagger-1.5/src/test/resources/com/github/victools/jsonschema/module/swagger15/integration-test-result.json index ec2cf2de..90f03d4d 100644 --- a/jsonschema-module-swagger-1.5/src/test/resources/com/github/victools/jsonschema/module/swagger15/integration-test-result.json +++ b/jsonschema-module-swagger-1.5/src/test/resources/com/github/victools/jsonschema/module/swagger15/integration-test-result.json @@ -17,7 +17,10 @@ "maximum": 20 }, "fieldWithOverriddenName": { - "type": "boolean" + "type": ["array", "null"], + "items": { + "type": "boolean" + } } }, "title": "test title", From ff9367c7d9686f889fb6d38d503c86d5f7b9b64f Mon Sep 17 00:00:00 2001 From: Carsten Wickner Date: Wed, 1 Apr 2020 00:27:02 +0200 Subject: [PATCH 4/4] chore: update CHANGELOG --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90444891..1db1bab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### `jsonschema-generator` +#### Added +- Convenience methods on `FieldScope`/`MethodScope` for accessing (first level) container item annotations: `getContainerItemAnnotation()` and `getContainerItemAnnotationConsideringFieldAndGetter()` + +#### Changed +- `MethodScope.getAnnotation()` now also considers annotations directly on return type (when no matching annotation was found on the method itself) + +### `jsonschema-module-javax-validation` +#### Changed +- Consider (first level) container item annotations (e.g. List<@Size(min = 3) String>`) + ## [4.8.1] - 2020-03-31 ### All #### Fixed @@ -297,6 +309,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Indicate a number's "exclusiveMaximum" according to `@DecimalMax` or `@Negative` +[Unreleased]: https://github.com/victools/jsonschema-generator/compare/v4.8.1...HEAD +[4.8.1]: https://github.com/victools/jsonschema-generator/compare/v4.8.0...v4.8.2 [4.8.0]: https://github.com/victools/jsonschema-generator/compare/v4.7.0...v4.8.0 [4.7.0]: https://github.com/victools/jsonschema-generator/compare/v4.6.0...v4.7.0 [4.6.0]: https://github.com/victools/jsonschema-generator/compare/v4.5.0...v4.6.0