diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index 6a59c85bd1..fa9da08ca0 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -745,7 +745,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context propSchemaOrArray == null ? null : propSchemaOrArray instanceof io.swagger.v3.oas.annotations.media.ArraySchema ? - ((io.swagger.v3.oas.annotations.media.ArraySchema) propSchemaOrArray).schema() : + ((io.swagger.v3.oas.annotations.media.ArraySchema) propSchemaOrArray).arraySchema() : (io.swagger.v3.oas.annotations.media.Schema) propSchemaOrArray; io.swagger.v3.oas.annotations.media.Schema.AccessMode accessMode = resolveAccessMode(propDef, type, propResolvedSchemaAnnotation); diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java index 7d2eea8c2b..c7e9de8e3a 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java @@ -13,6 +13,7 @@ import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.core.model.ApiDescription; import io.swagger.v3.core.util.Configuration; +import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.jaxrs2.matchers.SerializationMatchers; import io.swagger.v3.jaxrs2.petstore31.PetResource; @@ -83,6 +84,7 @@ import io.swagger.v3.jaxrs2.resources.Ticket3731BisResource; import io.swagger.v3.jaxrs2.resources.Ticket3731Resource; import io.swagger.v3.jaxrs2.resources.Ticket4065Resource; +import io.swagger.v3.jaxrs2.resources.Ticket4341Resource; import io.swagger.v3.jaxrs2.resources.Ticket4412Resource; import io.swagger.v3.jaxrs2.resources.Ticket4446Resource; import io.swagger.v3.jaxrs2.resources.Ticket4483Resource; @@ -795,6 +797,32 @@ public void test2497() { assertEquals(openAPI.getComponents().getSchemas().get("User").getRequired().get(0), "issue3438"); } + @Test(description = "array required property resolved from ArraySchema.arraySchema.requiredMode") + public void test4341() { + Reader reader = new Reader(new OpenAPI()); + OpenAPI openAPI = reader.read(Ticket4341Resource.class); + Schema userSchema = openAPI.getComponents().getSchemas().get("User"); + List required = userSchema.getRequired(); + + assertTrue(required.contains("requiredArray")); + assertFalse(required.contains("notRequiredArray")); + assertFalse(required.contains("notRequiredArrayWithNotNull")); + assertTrue(required.contains("autoRequiredWithNotNull")); + assertFalse(required.contains("autoNotRequired")); + + assertTrue( + required.contains("requiredArrayArraySchemaOnly"), + "arraySchema.requiredMode=REQUIRED should make the array property required " + + "even when items schema is not explicitly provided" + ); + + assertFalse( + required.contains("requiredItemsOnlyArray"), + "schema(requiredMode=REQUIRED) on items must not make the array property required; " + + "requiredness is controlled by arraySchema.requiredMode" + ); + } + @Test(description = "test resource with subresources") public void testResourceWithSubresources() { Reader reader = new Reader(new OpenAPI()); @@ -5503,4 +5531,81 @@ public void testTicket4907() { SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); ModelConverters.reset(); } + + @Test(description = "array property metadata is resolved from ArraySchema.arraySchema, items metadata from ArraySchema.schema") + public void test4341ArraySchemaOtherAttributes() { + Reader reader = new Reader(new OpenAPI()); + OpenAPI openAPI = reader.read(Ticket4341Resource.class); + System.out.println(Json.pretty(openAPI)); + + Schema userSchema = openAPI.getComponents().getSchemas().get("User"); + assertNotNull(userSchema, "User schema should be present"); + + @SuppressWarnings("unchecked") + Map properties = userSchema.getProperties(); + assertNotNull(properties, "User properties should not be null"); + + Schema metadataArray = properties.get("metadataArray"); + assertNotNull(metadataArray, "metadataArray property should be present"); + assertTrue(metadataArray instanceof ArraySchema, "metadataArray should be an ArraySchema"); + + // Property-level assertions + assertEquals( + metadataArray.getDescription(), + "array-level description", + "Array property description should come from arraySchema, not items schema" + ); + + assertEquals( + metadataArray.getDeprecated(), + Boolean.TRUE, + "Array property deprecated should come from arraySchema" + ); + + assertEquals( + metadataArray.getReadOnly(), + Boolean.TRUE, + "Array property readOnly should be true from arraySchema.accessMode=READ_ONLY" + ); + assertNotEquals( + metadataArray.getWriteOnly(), + Boolean.TRUE, + "Array property writeOnly should not be true when accessMode=READ_ONLY" + ); + + // Item-level assertions + + ArraySchema metadataArraySchema = (ArraySchema) metadataArray; + Schema items = metadataArraySchema.getItems(); + assertNotNull(items, "Items schema should not be null"); + + assertEquals( + items.getDescription(), + "item-level description", + "Items description should come from schema element of @ArraySchema" + ); + + assertNotEquals( + items.getDeprecated(), + Boolean.TRUE, + "Items deprecated should not be true when schema.deprecated=false" + ); + + assertEquals( + items.getWriteOnly(), + Boolean.TRUE, + "Items writeOnly should be true from schema.accessMode=WRITE_ONLY" + ); + assertNotEquals( + items.getReadOnly(), + Boolean.TRUE, + "Items readOnly should not be true when accessMode=WRITE_ONLY" + ); + + assertEquals( + items.getFormat(), + "email", + "Items format should come from schema.format" + ); + } } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/Ticket4341Resource.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/Ticket4341Resource.java new file mode 100644 index 0000000000..6082234acc --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/Ticket4341Resource.java @@ -0,0 +1,76 @@ +package io.swagger.v3.jaxrs2.resources; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.List; + +public class Ticket4341Resource { + + @GET + @Path("/user") + public User getUsers() { + return null; + } + + static class User { + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED), + schema = @Schema(type = "string") + ) + public List requiredArray; + + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED), + schema = @Schema(type = "string") + ) + public List notRequiredArray; + + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED), + schema = @Schema(type = "string") + ) + @NotNull + public List notRequiredArrayWithNotNull; + + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.AUTO), + schema = @Schema(type = "string") + ) + @NotNull + public List autoRequiredWithNotNull; + + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.AUTO), + schema = @Schema(type = "string") + ) + public List autoNotRequired; + + @ArraySchema( + arraySchema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + ) + public List requiredArrayArraySchemaOnly; + + @ArraySchema( + schema = @Schema(type = "string", requiredMode = Schema.RequiredMode.REQUIRED) + ) + public List requiredItemsOnlyArray; + + @ArraySchema( + arraySchema = @Schema( + description = "array-level description", + deprecated = true, + accessMode = Schema.AccessMode.READ_ONLY + ), + schema = @Schema( + description = "item-level description", + deprecated = false, + accessMode = Schema.AccessMode.WRITE_ONLY, + format = "email" + ) + ) + public List metadataArray; + } +}