From ba4d18ed5ad2d71468e5f979f9886dee548eb87b Mon Sep 17 00:00:00 2001 From: Mattias-Sehlstedt <60173714+Mattias-Sehlstedt@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:42:24 +0200 Subject: [PATCH] refactor: unify handling of content examples parsing --- .../codegen/CodegenConstants.java | 1 + .../openapitools/codegen/DefaultCodegen.java | 22 +++--------- .../languages/AbstractFSharpCodegen.java | 5 +-- .../languages/AbstractJavaCodegen.java | 35 ++++--------------- .../codegen/languages/RustServerCodegen.java | 14 ++++---- .../RustServerCodegenDeprecated.java | 16 ++++----- .../languages/TypeScriptClientCodegen.java | 24 +++++-------- .../codegen/utils/ExamplesUtils.java | 32 +++++++++++++++++ 8 files changed, 71 insertions(+), 78 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index ad507e2ff370..c77074c646bf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -481,6 +481,7 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, public static final String ENUM_DESCRIPTION = "enumDescription"; // Vendor extensions + public static final String X_EXAMPLE = "x-example"; public static final String X_INTERNAL = "x-internal"; public static final String X_PARENT = "x-parent"; public static final String X_COMPOSED_DATA_TYPE = "x-composed-data-type"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 47dd2b9dd1be..0bac114b2ea7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2115,8 +2115,8 @@ public void setParameterExampleValue(CodegenParameter codegenParameter) { // set the example value // if not specified in x-example, generate a default value // TODO need to revise how to obtain the example value - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get(X_EXAMPLE)); } else if (codegenParameter.isBoolean) { codegenParameter.example = "true"; } else if (codegenParameter.isLong) { @@ -2201,25 +2201,13 @@ public void setParameterExamples(CodegenParameter codegenParameter, Parameter pa public void setParameterExampleValue(CodegenParameter codegenParameter, RequestBody requestBody) { Content content = requestBody.getContent(); - if (content.size() > 1) { - // @see ModelUtils.getSchemaFromContent() - once(LOGGER).debug("Multiple MediaTypes found, using only the first one"); - } + Optional contentExample = ExamplesUtils.getContentExample(content); - MediaType mediaType = content.values().iterator().next(); - if (mediaType.getExample() != null) { - codegenParameter.example = mediaType.getExample().toString(); + if (contentExample.isPresent()) { + codegenParameter.example = contentExample.get().toString(); return; } - if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) { - Example example = mediaType.getExamples().values().iterator().next(); - if (example.getValue() != null) { - codegenParameter.example = example.getValue().toString(); - return; - } - } - setParameterExampleValue(codegenParameter); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java index 6ef2b9020983..303ddc01dee1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java @@ -39,6 +39,7 @@ import static org.openapitools.codegen.CodegenConstants.ENUM_VARS; import static org.openapitools.codegen.CodegenConstants.X_ENUM_BYTE; +import static org.openapitools.codegen.CodegenConstants.X_EXAMPLE; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.EnumUtils.getEnumVarsAsString; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -1006,8 +1007,8 @@ public void setParameterExampleValue(CodegenParameter codegenParameter) { // set the example value // if not specified in x-example, generate a default value // TODO need to revise how to obtain the example value - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get(X_EXAMPLE)); } else if (Boolean.TRUE.equals(codegenParameter.isBoolean)) { codegenParameter.example = "true"; } else if (Boolean.TRUE.equals(codegenParameter.isLong)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index ec6d25ed5ff3..7c1b6f3279ad 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -30,7 +30,6 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.examples.Example; -import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; @@ -1764,36 +1763,16 @@ public void setParameterExampleValue(CodegenParameter codegenParameter, Paramete public void setParameterExampleValue(CodegenParameter codegenParameter, RequestBody requestBody) { boolean isModel = (codegenParameter.isModel || (codegenParameter.isContainer && codegenParameter.getItems().isModel)); - Content content = requestBody.getContent(); - - if (content.size() > 1) { - // @see ModelUtils.getSchemaFromContent() - LOGGER.debug("Multiple MediaTypes found, using only the first one"); - } - - MediaType mediaType = content.values().iterator().next(); - if (mediaType.getExample() != null) { - if (isModel) { + MediaType mediaType = requestBody.getContent().values().iterator().next(); + boolean hasExample = mediaType.getExample() != null || (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()); + if (isModel) { + if (hasExample) { once(LOGGER).warn("Ignoring complex example on request body"); - } else { - codegenParameter.example = mediaType.getExample().toString(); - return; - } - } - - if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) { - Example example = mediaType.getExamples().values().iterator().next(); - if (example.getValue() != null) { - if (isModel) { - once(LOGGER).warn("Ignoring complex example on request body"); - } else { - codegenParameter.example = example.getValue().toString(); - return; - } } + setParameterExampleValue(codegenParameter); + } else { + super.setParameterExampleValue(codegenParameter, requestBody); } - - setParameterExampleValue(codegenParameter); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index ff09fd229b3d..161400d9b83b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -1087,8 +1087,8 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S codegenParameter.isByteArray = ModelUtils.isByteArraySchema(original_schema); // This is a model, so should only have an example if explicitly defined. - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get(X_EXAMPLE)); } else if (!codegenParameter.required) { //mandatory parameter use the example in the yaml. if no example, it is also null. codegenParameter.example = null; @@ -1784,24 +1784,24 @@ private void processParam(CodegenParameter param, CodegenOperation op) { if (param.required) { if (example != null) { - param.vendorExtensions.put("x-example", example); + param.vendorExtensions.put(X_EXAMPLE, example); } else if (param.isArray) { // Use the empty list if we don't have an example - param.vendorExtensions.put("x-example", "&Vec::new()"); + param.vendorExtensions.put(X_EXAMPLE, "&Vec::new()"); } else { // If we don't have an example that we can provide, we need to disable the client example, as it won't build. - param.vendorExtensions.put("x-example", "???"); + param.vendorExtensions.put(X_EXAMPLE, "???"); op.vendorExtensions.put("x-no-client-example", Boolean.TRUE); } } else if ((param.dataFormat != null) && (("date-time".equals(param.dataFormat)) || ("date".equals(param.dataFormat)))) { param.vendorExtensions.put("x-format-string", "{:?}"); - param.vendorExtensions.put("x-example", "None"); + param.vendorExtensions.put(X_EXAMPLE, "None"); } else { // Not required, so override the format string and example boolean itemsAreEnum = param.isArray && param.items != null && param.items.getIsEnumOrRef(); param.vendorExtensions.put("x-format-string", (param.getIsEnumOrRef() || itemsAreEnum) ? "{}" : "{:?}"); String exampleString = (example != null) ? "Some(" + example + ")" : "None"; - param.vendorExtensions.put("x-example", exampleString); + param.vendorExtensions.put(X_EXAMPLE, exampleString); } // Add a vendor extension to flag if this can have validate() run on it. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java index c5a5d206b895..f53a91cf6520 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java @@ -48,8 +48,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.openapitools.codegen.CodegenConstants.ENUM_VALUES; import static org.openapitools.codegen.CodegenConstants.X_ONE_OF_NAME; +import static org.openapitools.codegen.CodegenConstants.X_EXAMPLE; import static org.openapitools.codegen.utils.EnumUtils.getEnumValues; import static org.openapitools.codegen.utils.EnumUtils.hasEnumValues; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -1028,8 +1028,8 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S // This is a model, so should only have an example if explicitly // defined. - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get(X_EXAMPLE)); } else if (!codegenParameter.required) { //mandatory parameter use the example in the yaml. if no example, it is also null. codegenParameter.example = null; @@ -1583,23 +1583,23 @@ private void processParam(CodegenParameter param, CodegenOperation op) { if (param.required) { if (example != null) { - param.vendorExtensions.put("x-example", example); + param.vendorExtensions.put(X_EXAMPLE, example); } else if (param.isArray) { // Use the empty list if we don't have an example - param.vendorExtensions.put("x-example", "&Vec::new()"); + param.vendorExtensions.put(X_EXAMPLE, "&Vec::new()"); } else { // If we don't have an example that we can provide, we need to disable the client example, as it won't build. - param.vendorExtensions.put("x-example", "???"); + param.vendorExtensions.put(X_EXAMPLE, "???"); op.vendorExtensions.put("x-no-client-example", Boolean.TRUE); } } else if ((param.dataFormat != null) && (("date-time".equals(param.dataFormat)) || ("date".equals(param.dataFormat)))) { param.vendorExtensions.put("x-format-string", "{:?}"); - param.vendorExtensions.put("x-example", "None"); + param.vendorExtensions.put(X_EXAMPLE, "None"); } else { // Not required, so override the format string and example param.vendorExtensions.put("x-format-string", "{:?}"); String exampleString = (example != null) ? "Some(" + example + ")" : "None"; - param.vendorExtensions.put("x-example", exampleString); + param.vendorExtensions.put(X_EXAMPLE, exampleString); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index df0ed9077865..f58c7711630f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -39,6 +39,7 @@ import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.ExamplesUtils; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +52,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.openapitools.codegen.CodegenConstants.X_EXAMPLE; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.OnceLogger.once; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -1060,8 +1062,8 @@ public void setParameterExampleValue(CodegenParameter codegenParameter, Paramete } Object example = null; - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - example = codegenParameter.vendorExtensions.get("x-example"); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + example = codegenParameter.vendorExtensions.get(X_EXAMPLE); } else if (parameter.getExample() != null) { example = parameter.getExample(); } else if (parameter.getExamples() != null && !parameter.getExamples().isEmpty() && parameter.getExamples().values().iterator().next().getValue() != null) { @@ -1082,16 +1084,13 @@ public void setParameterExampleValue(CodegenParameter codegenParameter, Paramete */ @Override public void setParameterExampleValue(CodegenParameter codegenParameter, RequestBody requestBody) { - if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { - codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey(X_EXAMPLE)) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get(X_EXAMPLE)); } Content content = requestBody.getContent(); - if (content.size() > 1) { - // @see ModelUtils.getSchemaFromContent() - once(LOGGER).debug("Multiple MediaTypes found, using only the first one"); - } + Optional contentExample = ExamplesUtils.getContentExample(content); MediaType mediaType = content.values().iterator().next(); Schema schema = mediaType.getSchema(); @@ -1100,14 +1099,7 @@ public void setParameterExampleValue(CodegenParameter codegenParameter, RequestB return; } - Object example = null; - if (mediaType.getExample() != null) { - example = mediaType.getExample(); - } else if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty() && mediaType.getExamples().values().iterator().next().getValue() != null) { - example = mediaType.getExamples().values().iterator().next().getValue(); - } else { - example = getObjectExample(schema); - } + Object example = contentExample.orElseGet(() -> getObjectExample(schema)); example = exampleFromStringOrArraySchema(schema, example, codegenParameter.paramName); codegenParameter.example = toExampleValue(schema, example); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ExamplesUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ExamplesUtils.java index b69d11787dd2..64e46987af3f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ExamplesUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ExamplesUtils.java @@ -5,6 +5,8 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +37,36 @@ public static Map getExamplesFromResponse(OpenAPI openAPI, ApiR } } + /** + * Returns the example object for a request body. Note that the current implementation only fetches the first example + * found. If there are an {@code example} defined, then {@code examples} is not considered. + *

+ * Only the first media type is considered. + * @see ModelUtils#getSchemaFromRequestBody(RequestBody) + * @see ModelUtils#getSchemaFromResponse(OpenAPI, ApiResponse) + * + * @param content The request body content + * @return The first example found, or an empty optional if no example is found + */ + public static Optional getContentExample(Content content) { + if (content.size() > 1) { + once(LOGGER).debug("Multiple MediaTypes found, using only the first one"); + } + + MediaType mediaType = content.values().iterator().next(); + if (mediaType.getExample() != null) { + return Optional.of(mediaType.getExample()); + } + + if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) { + Example example = mediaType.getExamples().values().iterator().next(); + if (example.getValue() != null) { + return Optional.of(example.getValue()); + } + } + return Optional.empty(); + } + private static Map getExamplesFromContent(Content content) { if (content == null || content.isEmpty()) return Collections.emptyMap();