Skip to content

Commit f1df94b

Browse files
committed
Fix set schema example null value
See: swagger-api/swagger-core#4229
1 parent ce296f8 commit f1df94b

4 files changed

Lines changed: 89 additions & 7 deletions

File tree

openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiModelProp.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public interface OpenApiModelProp {
3434
String PROP_REF_DOLLAR = "$ref";
3535
String PROP_HIDDEN = "hidden";
3636
String PROP_EXAMPLE = "example";
37+
String PROP_EXAMPLE_SET_FLAG = "exampleSetFlag";
3738
String PROP_EXAMPLES = "examples";
3839
String PROP_NOT = "not";
3940
String PROP_ALL_OF = "allOf";

openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiNormalizeUtils.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import io.swagger.v3.oas.models.PathItem;
2727
import io.swagger.v3.oas.models.Paths;
2828
import io.swagger.v3.oas.models.examples.Example;
29+
import io.swagger.v3.oas.models.headers.Header;
2930
import io.swagger.v3.oas.models.media.Content;
3031
import io.swagger.v3.oas.models.media.Schema;
32+
import io.swagger.v3.oas.models.media.StringSchema;
3133
import io.swagger.v3.oas.models.parameters.Parameter;
3234
import io.swagger.v3.oas.models.responses.ApiResponse;
3335

@@ -43,6 +45,7 @@
4345
import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_SIMPLE_SCHEMA;
4446
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_OBJECT;
4547
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_STRING;
48+
import static io.micronaut.openapi.visitor.SchemaUtils.setSpecVersion;
4649

4750
/**
4851
* Normalization methods for openAPI objects.
@@ -134,10 +137,37 @@ public static void normalizeOperation(Operation operation, VisitorContext contex
134137
if (CollectionUtils.isNotEmpty(operation.getResponses())) {
135138
for (ApiResponse apiResponse : operation.getResponses().values()) {
136139
normalizeContent(apiResponse.getContent(), context);
140+
normalizeHeaders(apiResponse.getHeaders(), context);
137141
}
138142
}
139143
}
140144

145+
public static void normalizeHeaders(Map<String, Header> headers, VisitorContext context) {
146+
if (CollectionUtils.isEmpty(headers)) {
147+
return;
148+
}
149+
150+
for (var header : headers.values()) {
151+
Schema<?> headerSchema = header.getSchema();
152+
if (headerSchema == null) {
153+
headerSchema = setSpecVersion(new StringSchema());
154+
header.setSchema(headerSchema);
155+
}
156+
Schema<?> normalizedSchema = normalizeSchema(headerSchema, context);
157+
if (normalizedSchema != null) {
158+
header.setSchema(normalizedSchema);
159+
} else if (headerSchema.equals(EMPTY_SIMPLE_SCHEMA)) {
160+
headerSchema.setType(TYPE_OBJECT);
161+
}
162+
if (header.getExample() != null
163+
&& header.getExample() instanceof String exampleStr) {
164+
header.setExample(ConvertUtils.parseByTypeAndFormat(exampleStr, header.getSchema().getType(), header.getSchema().getFormat(), context, false));
165+
}
166+
normalizeExamples(header.getExamples());
167+
normalizeContent(header.getContent(), context);
168+
}
169+
}
170+
141171
public static void normalizeContent(Content content, VisitorContext context) {
142172
if (CollectionUtils.isEmpty(content)) {
143173
return;
@@ -233,9 +263,7 @@ public static Schema<?> normalizeSchema(Schema<?> schema, VisitorContext context
233263
schema.setType(type);
234264
schema.setAllOf(allOf);
235265
schema.setDefault(defaultValue);
236-
if (schema.getExample() == null) {
237-
schema.setExampleSetFlag(false);
238-
} else if (TYPE_STRING.equals(schema.getType()) && schema.getExample() instanceof String exampleStr) {
266+
if (!TYPE_STRING.equals(schema.getType()) && schema.getExample() instanceof String exampleStr) {
239267
schema.setExample(ConvertUtils.parseByTypeAndFormat(exampleStr, type, schema.getFormat(), context, false));
240268
}
241269
normalizeSchemaProperties(schema, context);
@@ -303,9 +331,7 @@ private static void normalizeSchemaProperties(Schema<?> schema, VisitorContext c
303331

304332
String type = schema.getType();
305333

306-
if (schema.getExample() == null) {
307-
schema.setExampleSetFlag(false);
308-
} else if (!TYPE_STRING.equals(type) && schema.getExample() instanceof String exampleStr) {
334+
if (!TYPE_STRING.equals(type) && schema.getExample() instanceof String exampleStr) {
309335
schema.setExample(ConvertUtils.parseByTypeAndFormat(exampleStr, type, schema.getFormat(), context, false));
310336
}
311337
}

openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ENUM;
165165
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXAMPLE;
166166
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXAMPLES;
167+
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXAMPLE_SET_FLAG;
167168
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXPRESSION;
168169
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXTENSIONS;
169170
import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXTERNAL_DOCS;
@@ -1336,6 +1337,9 @@ public static Map<CharSequence, Object> toValueMap(Map<CharSequence, Object> val
13361337
newValues.remove(PROP_WRITE_ONLY);
13371338
}
13381339
} else {
1340+
if (key.equals(PROP_EXAMPLE)) {
1341+
newValues.put(PROP_EXAMPLE_SET_FLAG, true);
1342+
}
13391343
var parsedJsonValue = parseJsonString(value);
13401344
newValues.put(key, parsedJsonValue != null ? parsedJsonValue : value);
13411345
}
@@ -2553,6 +2557,12 @@ private static Schema<?> doBindSchemaAnnotationValue(VisitorContext context, Typ
25532557
} catch (IOException e) {
25542558
warn("Error reading Swagger Schema for element [" + element + "]: " + e.getMessage(), context, element);
25552559
}
2560+
// fix for example = "null"
2561+
if (schemaToBind.getExample() == null
2562+
&& !schemaToBind.getExampleSetFlag()
2563+
&& schemaJson.get(PROP_EXAMPLE_SET_FLAG) != null) {
2564+
schemaToBind.setExampleSetFlag(true);
2565+
}
25562566

25572567
String defaultValue = null;
25582568
String[] allowableValues = null;
@@ -2666,7 +2676,7 @@ private static void schemaToValueMap(Map<CharSequence, Object> valueMap, Schema<
26662676
if (entry.getKey().equals("specVersion")) {
26672677
continue;
26682678
}
2669-
if (entry.getKey().equals("exampleSetFlag") && StringUtils.FALSE.equals(value.toString())) {
2679+
if (entry.getKey().equals(PROP_EXAMPLE_SET_FLAG) && StringUtils.FALSE.equals(value.toString())) {
26702680
continue;
26712681
}
26722682
valueMap.put(entry.getKey(), value);

openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiSchemaFieldSpec.groovy

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,4 +1402,49 @@ class MyBean {}
14021402
schemas.User.properties.id.format == "int32"
14031403
schemas.User.properties.name.type == "string"
14041404
}
1405+
1406+
void "test example set null"() {
1407+
1408+
when:
1409+
buildBeanDefinition('test.MyBean', '''
1410+
package test;
1411+
1412+
import io.micronaut.http.annotation.Controller;
1413+
import io.micronaut.http.annotation.Put;import io.swagger.v3.oas.annotations.media.Schema;import jakarta.validation.constraints.Pattern;
1414+
import java.util.List;
1415+
1416+
@Controller
1417+
class HelloController {
1418+
1419+
@Put("/sendModelWithDiscriminator")
1420+
Parent sendModelWithDiscriminator() {
1421+
return null;
1422+
}
1423+
}
1424+
1425+
class Parent {
1426+
1427+
@Schema(nullable = true, example = "null")
1428+
public Integer id;
1429+
}
1430+
1431+
@jakarta.inject.Singleton
1432+
class MyBean {}
1433+
''')
1434+
then: "the state is correct"
1435+
Utils.testReference != null
1436+
1437+
when: "The OpenAPI is retrieved"
1438+
def openApi = Utils.testReference
1439+
def schemas = openApi.components.schemas
1440+
1441+
then: "the components are valid"
1442+
schemas.Parent
1443+
schemas.Parent.properties.id
1444+
schemas.Parent.properties.id.type == 'integer'
1445+
schemas.Parent.properties.id.format == 'int32'
1446+
schemas.Parent.properties.id.nullable
1447+
schemas.Parent.properties.id.exampleSetFlag
1448+
schemas.Parent.properties.id.example == null
1449+
}
14051450
}

0 commit comments

Comments
 (0)