Skip to content

Commit fe918cc

Browse files
fix: ensure explicit types override inferred object type and fix properties of type Object in OAS 3.1 (#5034)
--------- Co-authored-by: Daniel Kmiecik <daniel.kmiecik@smartbear.com>
1 parent ea616b2 commit fe918cc

10 files changed

Lines changed: 121 additions & 23 deletions

File tree

modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3215,8 +3215,8 @@ protected void resolveSchemaMembers(Schema schema, Annotated a, Annotation[] ann
32153215
}
32163216

32173217
if (openapi31 && schemaAnnotation != null) {
3218-
for (String type : schemaAnnotation.types()) {
3219-
schema.addType(type);
3218+
if (schemaAnnotation.types().length > 0) {
3219+
schema.setTypes(new LinkedHashSet<>(Arrays.asList(schemaAnnotation.types())));
32203220
}
32213221
BigDecimal exclusiveMaximumValue = resolveExclusiveMaximumValue(a, annotations, schemaAnnotation);
32223222
if (exclusiveMaximumValue != null) {

modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -678,12 +678,7 @@ public static Optional<Schema> getSchemaFromAnnotation(
678678
}
679679

680680
if (schema.types().length > 0) {
681-
if (schema.types().length == 1) {
682-
schemaObject.setType(schema.types()[0]);
683-
}
684-
for (String type : schema.types()) {
685-
schemaObject.addType(type);
686-
}
681+
schemaObject.setTypes(new LinkedHashSet<>(Arrays.asList(schema.types())));
687682
}
688683
if (StringUtils.isNotBlank(schema.$id())) {
689684
schemaObject.set$id(schema.$id());

modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public Schema createProperty() {
239239
}
240240
@Override
241241
public Schema createProperty31() {
242-
return new JsonSchema();
242+
return explicitObjectType == null || explicitObjectType ? new JsonSchema().typesItem("object") : new JsonSchema();
243243
}
244244
};
245245

modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ public void testOAS31Fields() {
240240
" creditCard:\n" +
241241
" $ref: \"#/components/schemas/CreditCard\"\n" +
242242
" properties:\n" +
243-
" extraObject: {}\n" +
243+
" extraObject:\n" +
244+
" type: object\n" +
244245
"MultipleBaseBean:\n" +
245246
" type: object\n" +
246247
" description: MultipleBaseBean\n" +
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package io.swagger.v3.core.serialization;
2+
3+
import io.swagger.v3.core.converter.ModelConverters;
4+
import io.swagger.v3.oas.models.media.Schema;
5+
import org.testng.Assert;
6+
import org.testng.annotations.Test;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public class Oas31ObjectFieldTest {
12+
@Test(description = "Repro #4682: In OAS 3.1, raw Object property should not be rendered as an empty schema")
13+
public void rawObjectPropertyShouldBeObjectSchema() {
14+
Map<String, Schema> schemas = ModelConverters.getInstance(true).readAll(PojoUsingObjectField.class);
15+
Schema pojo = schemas.get("PojoUsingObjectField");
16+
Assert.assertNotNull(pojo, "PojoUsingObjectField schema should exist");
17+
18+
Schema myField = (Schema) pojo.getProperties().get("myField");
19+
Assert.assertNotNull(myField, "myField schema should exist");
20+
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());
21+
}
22+
23+
@Test(description = "OAS 3.1: List<Object> items schema should be object")
24+
public void listOfObjectItemsShouldBeObjectSchema() {
25+
Map<String, Schema> schemas = ModelConverters.getInstance(true).readAll(PojoUsingListOfObject.class);
26+
Schema pojo = schemas.get("PojoUsingListOfObject");
27+
Assert.assertNotNull(pojo, "PojoUsingListOfObject schema should exist");
28+
Schema itemsProp = (Schema) pojo.getProperties().get("items");
29+
Assert.assertNotNull(itemsProp, "items property schema should exist");
30+
Schema itemSchema = itemsProp.getItems();
31+
Assert.assertNotNull(itemSchema, "List<Object> items schema should exist");
32+
Assert.assertTrue(isObjectSchema(itemSchema), "Expected List<Object> items to be an object schema in OAS 3.1, but was: type=" + itemSchema.getType() + ", types=" + itemSchema.getTypes() + ", class=" + itemSchema.getClass());
33+
}
34+
35+
@Test(description = "OAS 3.1: Map<String,Object> additionalProperties schema should be object")
36+
public void mapOfObjectAdditionalPropertiesShouldBeObjectSchema() {
37+
Map<String, Schema> schemas = ModelConverters.getInstance(true).readAll(PojoUsingMapStringToObject.class);
38+
Schema pojo = schemas.get("PojoUsingMapStringToObject");
39+
Assert.assertNotNull(pojo, "PojoUsingMapStringToObject schema should exist");
40+
Schema additionalProp = (Schema) pojo.getProperties().get("additional");
41+
Assert.assertNotNull(additionalProp, "additional property schema should exist");
42+
Schema valueSchema = (Schema) additionalProp.getAdditionalProperties();
43+
Assert.assertNotNull(valueSchema, "Map<String,Object> additionalProperties (value schema) should exist");
44+
Assert.assertTrue(isObjectSchema(valueSchema), "Expected Map<String,Object> additionalProperties to be an object schema in OAS 3.1, but was: type=" + valueSchema.getType() + ", types=" + valueSchema.getTypes() + ", class=" + valueSchema.getClass());
45+
}
46+
47+
/**
48+
* OAS 3.1 may represent types via Schema#getTypes() (JSON Schema style) rather than Schema#getType().
49+
*/
50+
private static boolean isObjectSchema(Schema s) {
51+
if (s == null) return false;
52+
if ("object".equals(s.getType())) return true;
53+
return s.getTypes() != null && s.getTypes().contains("object");
54+
}
55+
56+
private static class PojoUsingObjectField {
57+
private Object myField;
58+
59+
public Object getMyField() {
60+
return myField;
61+
}
62+
63+
public void setMyField(Object myField) {
64+
this.myField = myField;
65+
}
66+
}
67+
68+
private static class PojoUsingListOfObject {
69+
private List<Object> items;
70+
71+
public List<Object> getItems() {
72+
return items;
73+
}
74+
75+
public void setItems(List<Object> items) {
76+
this.items = items;
77+
}
78+
}
79+
80+
private static class PojoUsingMapStringToObject {
81+
private Map<String, Object> additional;
82+
83+
public Map<String, Object> getAdditional() {
84+
return additional;
85+
}
86+
87+
public void setAdditional(Map<String, Object> additional) {
88+
this.additional = additional;
89+
}
90+
}
91+
}

modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3505,8 +3505,8 @@ public void testOas31Petstore() {
35053505
" exclusiveMaximum: 4\n" +
35063506
" foobar:\n" +
35073507
" type:\n" +
3508-
" - integer\n" +
35093508
" - string\n" +
3509+
" - integer\n" +
35103510
" format: int32\n" +
35113511
" Category:\n" +
35123512
" type: object\n" +
@@ -3532,7 +3532,6 @@ public void testOas31Petstore() {
35323532
" exclusiveMaximum: 2\n" +
35333533
" foobar:\n" +
35343534
" type:\n" +
3535-
" - integer\n" +
35363535
" - string\n" +
35373536
" - object\n" +
35383537
" format: int32\n" +
@@ -3550,7 +3549,6 @@ public void testOas31Petstore() {
35503549
" exclusiveMaximum: 2\n" +
35513550
" foobar:\n" +
35523551
" type:\n" +
3553-
" - integer\n" +
35543552
" - string\n" +
35553553
" - object\n" +
35563554
" format: int32\n" +
@@ -3597,6 +3595,7 @@ public void testOas31Petstore() {
35973595
" name:\n" +
35983596
" type: string\n" +
35993597
" annotated:\n" +
3598+
" type: object\n" +
36003599
" $ref: \"#/components/schemas/Category\"\n" +
36013600
" description: child description\n" +
36023601
" properties:\n" +
@@ -3643,14 +3642,14 @@ public void test31RefSiblings() {
36433642
" exclusiveMaximum: 2\n" +
36443643
" foobar:\n" +
36453644
" type:\n" +
3646-
" - integer\n" +
36473645
" - string\n" +
36483646
" - object\n" +
36493647
" format: int32\n" +
36503648
" SimpleTag:\n" +
36513649
" type: object\n" +
36523650
" properties:\n" +
36533651
" annotated:\n" +
3652+
" type: object\n" +
36543653
" $ref: \"#/components/schemas/SimpleCategory\"\n" +
36553654
" description: child description\n" +
36563655
" properties:\n" +
@@ -4113,6 +4112,7 @@ public void testMisc31() {
41134112
" type: object\n" +
41144113
" properties:\n" +
41154114
" country:\n" +
4115+
" type: object\n" +
41164116
" const: United States\n" +
41174117
" CreditCard:\n" +
41184118
" type: object\n" +
@@ -4123,11 +4123,13 @@ public void testMisc31() {
41234123
" type: object\n" +
41244124
" properties:\n" +
41254125
" postalCode:\n" +
4126+
" type: object\n" +
41264127
" pattern: \"[0-9]{5}(-[0-9]{4})?\"\n" +
41274128
" PostalCodePattern:\n" +
41284129
" type: object\n" +
41294130
" properties:\n" +
41304131
" postalCode:\n" +
4132+
" type: object\n" +
41314133
" pattern: \"[A-Z][0-9][A-Z] [0-9][A-Z][0-9]\"\n" +
41324134
" PropertyNamesPattern:\n" +
41334135
" pattern: \"^[A-Za-z_][A-Za-z0-9_]*$\"\n";

modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ webhooks:
2424
$comment: random comment
2525
$id: http://yourdomain.com/schemas/myschema.json
2626
dependentSchemas:
27-
pet: {}
27+
pet:
28+
type: object
2829
patternProperties:
29-
user: {}
30+
user:
31+
type: object
3032
webhook1:
3133
post:
3234
description: "subscribes a client to updates relevant to the requestor's account,\

modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ paths:
3232
$comment: random comment
3333
$id: http://yourdomain.com/schemas/myschema.json
3434
dependentSchemas:
35-
pet: { }
35+
pet:
36+
type: object
3637
patternProperties:
37-
user: { }
38+
user:
39+
type: object
3840
testCallback2:
3941
http://www.url2.com:
4042
get:

modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ paths:
9696
user:
9797
$ref: "#/components/schemas/User"
9898
properties:
99-
extraObject: {}
99+
extraObject:
100+
type: object
100101
components:
101102
schemas:
102103
Category:

modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,20 @@ paths:
4040
- string
4141
- number
4242
- object
43-
if: {}
44-
then: {}
45-
else: {}
43+
if:
44+
type: object
45+
then:
46+
type: object
47+
else:
48+
type: object
4649
$anchor: parameter $anchor
4750
$schema: parameter $schema
4851
description: User description
4952
example: User Description
5053
exclusiveMaximum: 100
5154
exclusiveMinimum: 1
52-
unevaluatedProperties: {}
55+
unevaluatedProperties:
56+
type: object
5357
required: true
5458
responses:
5559
default:

0 commit comments

Comments
 (0)