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 c19bd10753..75b4524a5b 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 @@ -603,9 +603,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context } else if (hasCompositionKeywords) { model = openapi31 ? new JsonSchema() : new ComposedSchema(); model.name(name); - if ( - (openapi31 && Boolean.TRUE.equals(PrimitiveType.explicitObjectType)) || - (!openapi31 && (!Boolean.FALSE.equals(PrimitiveType.explicitObjectType)))) { + if (isExplicitObjectType()) { if (openapi31 && resolvedArrayAnnotation == null) { model.addType("object"); } else { @@ -621,8 +619,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context return model; } else { model = openapi31 ? new JsonSchema().name(name) : new Schema().name(name); - if ((openapi31 && Boolean.TRUE.equals(PrimitiveType.explicitObjectType)) || - (!openapi31 && (!Boolean.FALSE.equals(PrimitiveType.explicitObjectType)))) { + if (isExplicitObjectType()) { if (openapi31 && resolvedArrayAnnotation == null) { model.addType("object"); } else { @@ -3579,4 +3576,9 @@ private boolean isStreamType(JavaType type) { type.getRawClass() != null && java.util.stream.Stream.class.isAssignableFrom(type.getRawClass()); } + + private boolean isExplicitObjectType() { + return (openapi31 && Boolean.TRUE.equals(PrimitiveType.explicitObjectType)) || + (!openapi31 && (!Boolean.FALSE.equals(PrimitiveType.explicitObjectType))); + } } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java index 52e8aade4b..6d99fb7a2c 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java @@ -239,7 +239,7 @@ public Schema createProperty() { } @Override public Schema createProperty31() { - return explicitObjectType == null || explicitObjectType ? new JsonSchema().typesItem("object") : new JsonSchema(); + return Boolean.TRUE.equals(explicitObjectType) ? new JsonSchema().typesItem("object") : new JsonSchema(); } }; diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java index 4e687886fd..8f86ba7f5d 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java @@ -240,8 +240,7 @@ public void testOAS31Fields() { " creditCard:\n" + " $ref: \"#/components/schemas/CreditCard\"\n" + " properties:\n" + - " extraObject:\n" + - " type: object\n" + + " extraObject: {}\n" + "MultipleBaseBean:\n" + " type: object\n" + " description: MultipleBaseBean\n" + diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/Oas31ObjectFieldTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/Oas31ObjectFieldTest.java index 6495787fba..64b7f7f3a0 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/Oas31ObjectFieldTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/Oas31ObjectFieldTest.java @@ -1,6 +1,7 @@ package io.swagger.v3.core.serialization; import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.oas.models.media.Schema; import org.testng.Assert; import org.testng.annotations.Test; @@ -9,19 +10,56 @@ import java.util.Map; public class Oas31ObjectFieldTest { - @Test(description = "Repro #4682: In OAS 3.1, raw Object property should not be rendered as an empty schema") - public void rawObjectPropertyShouldBeObjectSchema() { + + @Test(description = "Regression test for #4868: In OAS 3.1, raw Object property should be rendered as an empty schema") + public void rawObjectPropertyShouldBeEmptySchema() { Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingObjectField.class); Schema pojo = schemas.get("PojoUsingObjectField"); Assert.assertNotNull(pojo, "PojoUsingObjectField schema should exist"); + Schema myField = (Schema) pojo.getProperties().get("myField"); + Assert.assertNotNull(myField, "myField schema should exist"); + Assert.assertNull(myField.getTypes(), "Expected raw Object property to be an empty schema in OAS 3.1, but was: type=" + myField.getType() + ", types=" + myField.getTypes() + ", class=" + myField.getClass()); + } + @Test(description = "Regression test for #4868: OAS 3.1: List items schema should be empty") + public void listOfObjectItemsShouldBeEmptySchema() { + Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingListOfObject.class); + Schema pojo = schemas.get("PojoUsingListOfObject"); + Assert.assertNotNull(pojo, "PojoUsingListOfObject schema should exist"); + Schema itemsProp = (Schema) pojo.getProperties().get("items"); + Assert.assertNotNull(itemsProp, "items property schema should exist"); + Schema itemSchema = itemsProp.getItems(); + Assert.assertNotNull(itemSchema, "List items schema should exist"); + Assert.assertNull(itemSchema.getTypes(), "Expected List items to be an empty schema in OAS 3.1, but was: type=" + itemSchema.getType() + ", types=" + itemSchema.getTypes() + ", class=" + itemSchema.getClass()); + } + + @Test(description = "Regression test for #4868: OAS 3.1: Map additionalProperties schema should be empty") + public void mapOfObjectAdditionalPropertiesShouldBeEmptySchema() { + Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingMapStringToObject.class); + Schema pojo = schemas.get("PojoUsingMapStringToObject"); + Assert.assertNotNull(pojo, "PojoUsingMapStringToObject schema should exist"); + Schema additionalProp = (Schema) pojo.getProperties().get("additional"); + Assert.assertNotNull(additionalProp, "additional property schema should exist"); + Schema valueSchema = (Schema) additionalProp.getAdditionalProperties(); + Assert.assertNotNull(valueSchema, "Map additionalProperties (value schema) should exist"); + Assert.assertNull(valueSchema.getTypes(), "Expected Map additionalProperties to be an empty schema in OAS 3.1, but was: type=" + valueSchema.getType() + ", types=" + valueSchema.getTypes() + ", class=" + valueSchema.getClass()); + } + + @Test(description = "Repro #4682: In OAS 3.1, raw Object property should not be rendered as an empty schema if explicit-object-schema is enabled") + public void rawObjectPropertyShouldBeObjectSchema() { + setExplicitTypeObject(true); + Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingObjectField.class); + Schema pojo = schemas.get("PojoUsingObjectField"); + Assert.assertNotNull(pojo, "PojoUsingObjectField schema should exist"); Schema myField = (Schema) pojo.getProperties().get("myField"); Assert.assertNotNull(myField, "myField schema should exist"); Assert.assertTrue(isObjectSchema(myField), "Expected raw Object property to be an object schema in OAS 3.1, but was: type=" + myField.getType() + ", types=" + myField.getTypes() + ", class=" + myField.getClass()); + setExplicitTypeObject(null); } - @Test(description = "OAS 3.1: List items schema should be object") + @Test(description = "OAS 3.1: List items schema should be object if explicit-object-schema is enabled") public void listOfObjectItemsShouldBeObjectSchema() { + setExplicitTypeObject(true); Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingListOfObject.class); Schema pojo = schemas.get("PojoUsingListOfObject"); Assert.assertNotNull(pojo, "PojoUsingListOfObject schema should exist"); @@ -30,10 +68,12 @@ public void listOfObjectItemsShouldBeObjectSchema() { Schema itemSchema = itemsProp.getItems(); Assert.assertNotNull(itemSchema, "List items schema should exist"); Assert.assertTrue(isObjectSchema(itemSchema), "Expected List items to be an object schema in OAS 3.1, but was: type=" + itemSchema.getType() + ", types=" + itemSchema.getTypes() + ", class=" + itemSchema.getClass()); + setExplicitTypeObject(null); } - @Test(description = "OAS 3.1: Map additionalProperties schema should be object") + @Test(description = "OAS 3.1: Map additionalProperties schema should be object if explicit-object-schema is enabled") public void mapOfObjectAdditionalPropertiesShouldBeObjectSchema() { + setExplicitTypeObject(true); Map schemas = ModelConverters.getInstance(true).readAll(PojoUsingMapStringToObject.class); Schema pojo = schemas.get("PojoUsingMapStringToObject"); Assert.assertNotNull(pojo, "PojoUsingMapStringToObject schema should exist"); @@ -42,6 +82,7 @@ public void mapOfObjectAdditionalPropertiesShouldBeObjectSchema() { Schema valueSchema = (Schema) additionalProp.getAdditionalProperties(); Assert.assertNotNull(valueSchema, "Map additionalProperties (value schema) should exist"); Assert.assertTrue(isObjectSchema(valueSchema), "Expected Map additionalProperties to be an object schema in OAS 3.1, but was: type=" + valueSchema.getType() + ", types=" + valueSchema.getTypes() + ", class=" + valueSchema.getClass()); + setExplicitTypeObject(null); } /** @@ -88,4 +129,10 @@ public void setAdditional(Map additional) { this.additional = additional; } } + + // We need to set it programmatically rather than with system properties since the field instantiation is static + // (only read from system env once, thus it cannot be changed/alternated back and forth) + private void setExplicitTypeObject(Boolean value) { + PrimitiveType.explicitObjectType = value; + } } \ No newline at end of file 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 9175b99e37..5dd7e1de3f 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 @@ -3596,7 +3596,6 @@ public void testOas31Petstore() { " name:\n" + " type: string\n" + " annotated:\n" + - " type: object\n" + " $ref: \"#/components/schemas/Category\"\n" + " description: child description\n" + " properties:\n" + @@ -3650,7 +3649,6 @@ public void test31RefSiblings() { " type: object\n" + " properties:\n" + " annotated:\n" + - " type: object\n" + " $ref: \"#/components/schemas/SimpleCategory\"\n" + " description: child description\n" + " properties:\n" + @@ -4113,7 +4111,6 @@ public void testMisc31() { " type: object\n" + " properties:\n" + " country:\n" + - " type: object\n" + " const: United States\n" + " CreditCard:\n" + " type: object\n" + @@ -4124,13 +4121,11 @@ public void testMisc31() { " type: object\n" + " properties:\n" + " postalCode:\n" + - " type: object\n" + " pattern: \"[0-9]{5}(-[0-9]{4})?\"\n" + " PostalCodePattern:\n" + " type: object\n" + " properties:\n" + " postalCode:\n" + - " type: object\n" + " pattern: \"[A-Z][0-9][A-Z] [0-9][A-Z][0-9]\"\n" + " PropertyNamesPattern:\n" + " pattern: \"^[A-Za-z_][A-Za-z0-9_]*$\"\n"; diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml index 25d3d5a349..62e397cade 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml @@ -24,11 +24,9 @@ webhooks: $comment: random comment $id: http://yourdomain.com/schemas/myschema.json dependentSchemas: - pet: - type: object + pet: {} patternProperties: - user: - type: object + user: {} webhook1: post: description: "subscribes a client to updates relevant to the requestor's account,\ diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml index 524ccb0b35..79189d9464 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml @@ -32,11 +32,9 @@ paths: $comment: random comment $id: http://yourdomain.com/schemas/myschema.json dependentSchemas: - pet: - type: object + pet: { } patternProperties: - user: - type: object + user: { } testCallback2: http://www.url2.com: get: diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml index fa8377a630..e42efe9760 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml @@ -96,8 +96,7 @@ paths: user: $ref: "#/components/schemas/User" properties: - extraObject: - type: object + extraObject: {} components: schemas: Category: diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml index 1ffc71629d..2c5b00cc76 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml @@ -40,20 +40,16 @@ paths: - string - number - object - if: - type: object - then: - type: object - else: - type: object + if: {} + then: {} + else: {} $anchor: parameter $anchor $schema: parameter $schema description: User description example: User Description exclusiveMaximum: 100 exclusiveMinimum: 1 - unevaluatedProperties: - type: object + unevaluatedProperties: {} required: true responses: default: