Skip to content

Commit feb16ce

Browse files
authored
Skip defaulting schema type to object when implementation is void (#2433)
Signed-off-by: Michael Edgar <[email protected]>
1 parent 281f72f commit feb16ce

File tree

6 files changed

+98
-3
lines changed

6 files changed

+98
-3
lines changed

core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ static <T> List<T> wrapInList(T value) {
521521
* @param schemaReferenceSupported
522522
*/
523523
static Schema readClassSchema(final AnnotationScannerContext context, Type type, boolean schemaReferenceSupported) {
524-
if (type == null) {
524+
if (type == null || TypeUtil.isVoid(type)) {
525525
return null;
526526
}
527527
Schema schema;

core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiDataObjectScanner.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ private void depthFirstGraphSearch() {
261261
continue;
262262
}
263263

264-
if (!hasNonNullType(currentSchema)) {
264+
if (!hasNonNullType(currentSchema)
265+
&& !isImplementationVoid(currentTarget, currentClass)) {
265266
// If not schema has yet been set, consider this an "object"
266267
SchemaSupport.setType(currentSchema, Schema.SchemaType.OBJECT);
267268
} else if (allowRegistration) {
@@ -288,6 +289,31 @@ private void depthFirstGraphSearch() {
288289
}
289290
}
290291

292+
/**
293+
* Determine if the {@code implementation} for the schema has been voided via an annotation,
294+
* either on the target being scanned or the class itself
295+
*
296+
* @param target annotated target, possible null
297+
* @param clazz target class that may be hosting a {@literal @}Schema annotation
298+
*/
299+
private boolean isImplementationVoid(AnnotationTarget target, ClassInfo clazz) {
300+
for (AnnotationTarget t : Arrays.asList(target, clazz)) {
301+
Type type = context.annotations()
302+
.getAnnotationValue(t, SchemaConstant.DOTNAME_SCHEMA,
303+
SchemaConstant.PROP_IMPLEMENTATION);
304+
305+
if (TypeUtil.isVoid(type)) {
306+
return true;
307+
}
308+
309+
if (type != null) {
310+
return false;
311+
}
312+
}
313+
314+
return false;
315+
}
316+
291317
private void maybeRegisterSchema(Type currentType, Schema currentSchema, Schema entrySchema) {
292318
Schema ref = SchemaFactory.schemaRegistration(context, currentType, currentSchema);
293319
Schema.SchemaType type = SchemaSupport.getNonNullType(currentSchema);

core/src/main/java/io/smallrye/openapi/runtime/util/TypeUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public class TypeUtil {
113113
// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#dataTypeFormat
114114
static {
115115
TYPE_MAP.put(DOTNAME_OBJECT, ANY);
116+
TYPE_MAP.put(DOTNAME_VOID, ANY);
116117

117118
// String
118119
TYPE_MAP.put(DotName.createSimple(String.class.getName()), STRING_FORMAT);

core/src/test/java/io/smallrye/openapi/runtime/scanner/StandaloneSchemaScanTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,4 +1084,31 @@ class DataList extends GenericListRoot<Bean> {
10841084
assertJsonEquals("components.schemas.resolved-mapped-generic-ptype.json",
10851085
Bean.class, GenericRoot.class, GenericListRoot.class, Data.class, DataList.class);
10861086
}
1087+
1088+
static final class TestVoidImplementationNoType {
1089+
@Schema(oneOf = { Implementation.class, String.class }, implementation = Void.class)
1090+
interface IFace1 {
1091+
}
1092+
1093+
interface IFace2 {
1094+
}
1095+
1096+
@Schema(additionalProperties = Schema.False.class)
1097+
static class Implementation implements IFace1 {
1098+
@Schema(required = true, description = "Identifier")
1099+
String id;
1100+
@Schema(defaultValue = "1", minimum = "1", description = "Sequence of this implemenatation instance")
1101+
long sequence;
1102+
@Schema(oneOf = { Implementation.class, String.class }, implementation = Void.class)
1103+
IFace2 oneOfIface2;
1104+
@Schema(implementation = String.class)
1105+
IFace2 stringIface2;
1106+
}
1107+
}
1108+
1109+
@Test
1110+
void testVoidImplementationNoType() throws IOException, JSONException {
1111+
assertJsonEquals("components.schemas.void-implementation-no-type.json",
1112+
TestVoidImplementationNoType.IFace1.class, TestVoidImplementationNoType.Implementation.class);
1113+
}
10871114
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"openapi" : "3.1.0",
3+
"components" : {
4+
"schemas" : {
5+
"IFace1" : {
6+
"oneOf" : [ {
7+
"$ref" : "#/components/schemas/Implementation"
8+
}, {
9+
"type" : "string"
10+
} ]
11+
},
12+
"Implementation" : {
13+
"additionalProperties" : false,
14+
"type" : "object",
15+
"required" : [ "id" ],
16+
"properties" : {
17+
"id" : {
18+
"type" : "string",
19+
"description" : "Identifier"
20+
},
21+
"sequence" : {
22+
"type" : "integer",
23+
"format" : "int64",
24+
"minimum" : 1,
25+
"description" : "Sequence of this implemenatation instance",
26+
"default" : 1
27+
},
28+
"oneOfIface2" : {
29+
"oneOf" : [ {
30+
"$ref" : "#/components/schemas/Implementation"
31+
}, {
32+
"type" : "string"
33+
} ]
34+
},
35+
"stringIface2" : {
36+
"type" : "string"
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}

extension-jaxrs/src/test/resources/io/smallrye/openapi/runtime/scanner/refsEnabled.kitchenSink.expected.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@
502502
}
503503
},
504504
"voidField": {
505-
"type": "object"
506505
},
507506
"writeOnlyInteger": {
508507
"format": "int32",

0 commit comments

Comments
 (0)