From 3280fd0da7fe5f27965b01abf8f6c7637e47506b Mon Sep 17 00:00:00 2001 From: Manish M Demblani Date: Wed, 20 May 2026 18:28:30 +0400 Subject: [PATCH 1/4] fix(kotlin-spring): preserve 'default' response code in postProcessOperationsWithModels When an OpenAPI spec defines a 'default' response (no specific status code), the codegen internally represents it as code '0'. The existing logic unconditionally mapped '0' -> '200', causing generated @ApiResponse annotations to incorrectly use responseCode = "200" instead of responseCode = "default". Fix: check resp.isDefault before mapping; only fall back to '200' for non-default zero-coded responses. Fixes #17445 --- .../codegen/languages/KotlinSpringServerCodegen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 62d602f6a6ef..80e6ea0eaa5c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -1605,7 +1605,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List { if ("0".equals(resp.code)) { - resp.code = "200"; + resp.code = resp.isDefault ? "default" : "200"; } // This is necessary in case 'modelMutable' is enabled, From 08c679f724cca3223735376fe8861cdd3580ac39 Mon Sep 17 00:00:00 2001 From: Manish M Demblani Date: Thu, 21 May 2026 01:49:53 +0400 Subject: [PATCH 2/4] chore(samples): regenerate kotlin-spring samples with default response code fix Updates 8 kotlin-spring sample files to reflect the fix for issue #17445: createUser, createUsersWithArrayInput, createUsersWithListInput, and logoutUser operations in the petstore spec have `default` responses; their @ApiResponse annotations now correctly use responseCode = "default" instead of "200". Co-Authored-By: Claude Sonnet 4.6 --- .../main/kotlin/org/openapitools/api/UserApiController.kt | 8 ++++---- .../src/main/kotlin/org/openapitools/api/UserApi.kt | 8 ++++---- .../src/main/kotlin/org/openapitools/api/UserApi.kt | 8 ++++---- .../main/kotlin/org/openapitools/api/UserApiController.kt | 8 ++++---- .../main/kotlin/org/openapitools/api/UserApiController.kt | 8 ++++---- .../main/kotlin/org/openapitools/api/UserApiController.kt | 8 ++++---- .../src/main/kotlin/org/openapitools/api/UserApi.kt | 8 ++++---- .../main/kotlin/org/openapitools/api/UserApiController.kt | 8 ++++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/samples/server/petstore/kotlin-spring-default/src/main/kotlin/org/openapitools/api/UserApiController.kt b/samples/server/petstore/kotlin-spring-default/src/main/kotlin/org/openapitools/api/UserApiController.kt index b59055789659..779ab20356e1 100644 --- a/samples/server/petstore/kotlin-spring-default/src/main/kotlin/org/openapitools/api/UserApiController.kt +++ b/samples/server/petstore/kotlin-spring-default/src/main/kotlin/org/openapitools/api/UserApiController.kt @@ -38,7 +38,7 @@ class UserApiController() { operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -58,7 +58,7 @@ class UserApiController() { operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -78,7 +78,7 @@ class UserApiController() { operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -160,7 +160,7 @@ class UserApiController() { operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( diff --git a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/src/main/kotlin/org/openapitools/api/UserApi.kt b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/src/main/kotlin/org/openapitools/api/UserApi.kt index fff346fae41b..4bce0a3890ff 100644 --- a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/src/main/kotlin/org/openapitools/api/UserApi.kt +++ b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/src/main/kotlin/org/openapitools/api/UserApi.kt @@ -46,7 +46,7 @@ interface UserApi { operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -68,7 +68,7 @@ interface UserApi { operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -90,7 +90,7 @@ interface UserApi { operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -180,7 +180,7 @@ interface UserApi { operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) diff --git a/samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/UserApi.kt b/samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/UserApi.kt index 5c6b0adfc1a2..ad3e776b029e 100644 --- a/samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/UserApi.kt +++ b/samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/UserApi.kt @@ -45,7 +45,7 @@ interface UserApi { operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -67,7 +67,7 @@ interface UserApi { operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -89,7 +89,7 @@ interface UserApi { operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @@ -179,7 +179,7 @@ interface UserApi { operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) diff --git a/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/UserApiController.kt b/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/UserApiController.kt index 7dd168a5cf20..166e2764f7c2 100644 --- a/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/UserApiController.kt +++ b/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/UserApiController.kt @@ -38,7 +38,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -56,7 +56,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -74,7 +74,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -153,7 +153,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.GET], diff --git a/samples/server/petstore/kotlin-springboot-reactive-without-flow/src/main/kotlin/org/openapitools/api/UserApiController.kt b/samples/server/petstore/kotlin-springboot-reactive-without-flow/src/main/kotlin/org/openapitools/api/UserApiController.kt index d677e66a952f..e81a31b5697d 100644 --- a/samples/server/petstore/kotlin-springboot-reactive-without-flow/src/main/kotlin/org/openapitools/api/UserApiController.kt +++ b/samples/server/petstore/kotlin-springboot-reactive-without-flow/src/main/kotlin/org/openapitools/api/UserApiController.kt @@ -39,7 +39,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -59,7 +59,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -79,7 +79,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -161,7 +161,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( diff --git a/samples/server/petstore/kotlin-springboot-reactive/src/main/kotlin/org/openapitools/api/UserApiController.kt b/samples/server/petstore/kotlin-springboot-reactive/src/main/kotlin/org/openapitools/api/UserApiController.kt index d677e66a952f..e81a31b5697d 100644 --- a/samples/server/petstore/kotlin-springboot-reactive/src/main/kotlin/org/openapitools/api/UserApiController.kt +++ b/samples/server/petstore/kotlin-springboot-reactive/src/main/kotlin/org/openapitools/api/UserApiController.kt @@ -39,7 +39,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -59,7 +59,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -79,7 +79,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( @@ -161,7 +161,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ], + ApiResponse(responseCode = "default", description = "successful operation") ], security = [ SecurityRequirement(name = "api_key") ] ) @RequestMapping( diff --git a/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/api/UserApi.kt b/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/api/UserApi.kt index 37506ccbab0d..38412b1067c6 100644 --- a/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/api/UserApi.kt +++ b/samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/api/UserApi.kt @@ -43,7 +43,7 @@ interface UserApi { operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( @@ -64,7 +64,7 @@ interface UserApi { operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( @@ -85,7 +85,7 @@ interface UserApi { operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( @@ -173,7 +173,7 @@ interface UserApi { operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( diff --git a/samples/server/petstore/kotlin-springboot-source-swagger2/src/main/kotlin/org/openapitools/api/UserApiController.kt b/samples/server/petstore/kotlin-springboot-source-swagger2/src/main/kotlin/org/openapitools/api/UserApiController.kt index 7dd168a5cf20..166e2764f7c2 100644 --- a/samples/server/petstore/kotlin-springboot-source-swagger2/src/main/kotlin/org/openapitools/api/UserApiController.kt +++ b/samples/server/petstore/kotlin-springboot-source-swagger2/src/main/kotlin/org/openapitools/api/UserApiController.kt @@ -38,7 +38,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUser", description = """This can only be done by the logged in user.""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -56,7 +56,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithArrayInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -74,7 +74,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "createUsersWithListInput", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.POST], @@ -153,7 +153,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService) operationId = "logoutUser", description = """""", responses = [ - ApiResponse(responseCode = "200", description = "successful operation") ] + ApiResponse(responseCode = "default", description = "successful operation") ] ) @RequestMapping( method = [RequestMethod.GET], From 597bf2e8c5af5dbae1cdf441982647edd2149053 Mon Sep 17 00:00:00 2001 From: Manish M Demblani Date: Thu, 21 May 2026 02:12:13 +0400 Subject: [PATCH 3/4] fix(kotlin-spring): handle 'default' response code in templates and SpringHttpStatusLambda MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SpringHttpStatusLambda threw IllegalArgumentException for the OpenAPI 'default' response code string, crashing generation for any kotlin-spring config that uses useResponseEntity=false (which emits @ResponseStatus via the lambda). Fix by mapping "default" → HttpStatus.OK, matching the semantic intent of an unspecified fallback response. Also guard the two other places that embed response.code as a literal: - returnValue.mustache: HttpStatus.valueOf({{code}}) becomes HttpStatus.valueOf(200) for default responses (keeps existing samples) - api.mustache / apiInterface.mustache swagger1 ApiResponse(code=...): code is an int, so default responses fall back to 200 Fixes: generateHttpInterface, generateHttpInterfaceReactiveWithCoroutines, generateHttpInterfaceReactiveWithReactor, nonReactiveWithoutResponseEntity, reactiveWithoutResponseEntity test failures. Co-Authored-By: Claude Sonnet 4.6 --- .../codegen/templating/mustache/SpringHttpStatusLambda.java | 4 ++++ .../src/main/resources/kotlin-spring/api.mustache | 2 +- .../src/main/resources/kotlin-spring/apiInterface.mustache | 2 +- .../src/main/resources/kotlin-spring/returnValue.mustache | 2 +- .../templating/mustache/SpringHttpStatusLambdaTest.java | 3 ++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java index 9d9d6123a1dd..a7e4878ee884 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java @@ -212,6 +212,10 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio case "506": writer.write(HTTP_STATUS_PREFIX + "VARIANT_ALSO_NEGOTIATES"); break; + case "default": + // OpenAPI 'default' response — no specific status code; fall back to 200 OK + writer.write(HTTP_STATUS_PREFIX + "OK"); + break; default: throw new IllegalArgumentException("The given HTTP status code: " + httpCode + " is not supported by the 'org.springframework.http.HttpStatus' enum."); diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache index 2b7662531379..1fae369d7d57 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache @@ -87,7 +87,7 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v responseContainer = "{{{.}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}}) @ApiResponses( - value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} + value = [{{#responses}}ApiResponse(code = {{#isDefault}}200{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} {{#implicitHeadersParams.0}} {{>implicitHeaders}} {{/implicitHeadersParams.0}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache index 82608294c6ba..051bcafff95f 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache @@ -101,7 +101,7 @@ interface {{classname}} { authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}} ) @ApiResponses( - value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}] + value = [{{#responses}}ApiResponse(code = {{#isDefault}}200{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}] ){{/swagger1AnnotationLibrary}} {{#implicitHeadersParams.0}} {{>implicitHeaders}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache index 44336bb5db69..a5d53c422408 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache @@ -1,4 +1,4 @@ -{{!}}{{#useResponseEntity}}{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}HttpStatus.valueOf({{code}}){{/-first}}{{/responses}}){{/serviceInterface}}{{/useResponseEntity}}{{! +{{!}}{{#useResponseEntity}}{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}{{#isDefault}}HttpStatus.valueOf(200){{/isDefault}}{{^isDefault}}HttpStatus.valueOf({{code}}){{/isDefault}}{{/-first}}{{/responses}}){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{#useResponseEntity}}{{^serviceInterface}}ResponseEntity(HttpStatus.NOT_IMPLEMENTED){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{^useResponseEntity}}{{#serviceInterface}}service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{^useResponseEntity}}{{^serviceInterface}}TODO("Not yet implemented"){{/serviceInterface}}{{/useResponseEntity}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java index 620fd4756420..3eb1b802c5fd 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java @@ -74,7 +74,8 @@ public static Object[][] springHttpStatusCodes() { {"UNSUPPORTED_MEDIA_TYPE", "415"}, {"UPGRADE_REQUIRED", "426"}, {"URI_TOO_LONG", "414"}, - {"VARIANT_ALSO_NEGOTIATES", "506"} + {"VARIANT_ALSO_NEGOTIATES", "506"}, + {"OK", "default"} }; } From aabbc520c8af994bd4fb7a21853960eff78e3f1d Mon Sep 17 00:00:00 2001 From: Manish M Demblani Date: Thu, 21 May 2026 02:19:18 +0400 Subject: [PATCH 4/4] refactor(kotlin-spring): use resp.isDefault in template instead of mutating resp.code Per reviewer feedback, avoid mutating resp.code to the non-numeric string "default" in postProcessOperationsWithModels. That change required cascading fixes to SpringHttpStatusLambda, returnValue.mustache, and swagger1 templates because resp.code feeds into @ResponseStatus and HttpStatus.valueOf() calls that require a numeric status code. Cleaner approach: keep resp.code = "200" (the parent's behaviour) and emit responseCode = "default" in the swagger2 @ApiResponse annotation by testing resp.isDefault directly in the Mustache template: ApiResponse(responseCode = "{{#isDefault}}default{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}", ...) This is analogous to how the Java Spring generator handles default responses. With resp.code remaining numeric, SpringHttpStatusLambda (@ResponseStatus), returnValue.mustache (HttpStatus.valueOf), and swagger1 ApiResponse(code=...) all continue to work without modification. Also reverts the unnecessary SpringHttpStatusLambda "default" case, returnValue isDefault guard, swagger1 isDefault guards, and SpringHttpStatusLambdaTest "default" entry added in the previous commit. Adds a focused regression test (defaultResponseCodeRenderedAsDefault) that: - verifies ApiResponse(responseCode = "default") for operations with a default response (resolves reviewer comment 2) - verifies ApiResponse(responseCode = "200") for explicit-200 responses - verifies "0" never appears in generated output - runs with useResponseEntity=false to confirm no crash (covers issue #17445) Co-Authored-By: Claude Sonnet 4.6 --- .../languages/KotlinSpringServerCodegen.java | 2 +- .../mustache/SpringHttpStatusLambda.java | 4 --- .../main/resources/kotlin-spring/api.mustache | 4 +-- .../kotlin-spring/apiInterface.mustache | 4 +-- .../kotlin-spring/returnValue.mustache | 2 +- .../spring/KotlinSpringServerCodegenTest.java | 35 +++++++++++++++++++ .../mustache/SpringHttpStatusLambdaTest.java | 3 +- 7 files changed, 42 insertions(+), 12 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 80e6ea0eaa5c..62d602f6a6ef 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -1605,7 +1605,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List { if ("0".equals(resp.code)) { - resp.code = resp.isDefault ? "default" : "200"; + resp.code = "200"; } // This is necessary in case 'modelMutable' is enabled, diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java index a7e4878ee884..9d9d6123a1dd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambda.java @@ -212,10 +212,6 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio case "506": writer.write(HTTP_STATUS_PREFIX + "VARIANT_ALSO_NEGOTIATES"); break; - case "default": - // OpenAPI 'default' response — no specific status code; fall back to 200 OK - writer.write(HTTP_STATUS_PREFIX + "OK"); - break; default: throw new IllegalArgumentException("The given HTTP status code: " + httpCode + " is not supported by the 'org.springframework.http.HttpStatus' enum."); diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache index 1fae369d7d57..dcc950cdeb75 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache @@ -76,7 +76,7 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v operationId = "{{{operationId}}}", description = """{{{unescapedNotes}}}""", responses = [{{#responses}} - ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} ]{{#hasAuthMethods}}, + ApiResponse(responseCode = "{{#isDefault}}default{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} ]{{#hasAuthMethods}}, security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}} ){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} @ApiOperation( @@ -87,7 +87,7 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v responseContainer = "{{{.}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}}) @ApiResponses( - value = [{{#responses}}ApiResponse(code = {{#isDefault}}200{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} + value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} {{#implicitHeadersParams.0}} {{>implicitHeaders}} {{/implicitHeadersParams.0}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache index 051bcafff95f..851b07427a6f 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache @@ -88,7 +88,7 @@ interface {{classname}} { operationId = "{{{operationId}}}", description = """{{{unescapedNotes}}}""", responses = [{{#responses}} - ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} + ApiResponse(responseCode = "{{#isDefault}}default{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} ]{{#hasAuthMethods}}, security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}} ){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} @@ -101,7 +101,7 @@ interface {{classname}} { authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}} ) @ApiResponses( - value = [{{#responses}}ApiResponse(code = {{#isDefault}}200{{/isDefault}}{{^isDefault}}{{{code}}}{{/isDefault}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}] + value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}] ){{/swagger1AnnotationLibrary}} {{#implicitHeadersParams.0}} {{>implicitHeaders}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache index a5d53c422408..44336bb5db69 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache @@ -1,4 +1,4 @@ -{{!}}{{#useResponseEntity}}{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}{{#isDefault}}HttpStatus.valueOf(200){{/isDefault}}{{^isDefault}}HttpStatus.valueOf({{code}}){{/isDefault}}{{/-first}}{{/responses}}){{/serviceInterface}}{{/useResponseEntity}}{{! +{{!}}{{#useResponseEntity}}{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}HttpStatus.valueOf({{code}}){{/-first}}{{/responses}}){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{#useResponseEntity}}{{^serviceInterface}}ResponseEntity(HttpStatus.NOT_IMPLEMENTED){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{^useResponseEntity}}{{#serviceInterface}}service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{/serviceInterface}}{{/useResponseEntity}}{{! ---}}{{^useResponseEntity}}{{^serviceInterface}}TODO("Not yet implemented"){{/serviceInterface}}{{/useResponseEntity}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 0e83e957927d..d375c88d01a2 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -3021,6 +3021,41 @@ public void nonReactiveWithResponseEntity() throws Exception { ); } + /** + * Regression test for https://github.com/OpenAPITools/openapi-generator/issues/17445. + * OpenAPI 'default' responses must emit responseCode = "default" in @ApiResponse (swagger2), + * not "0" (internal pre-processed value) or "200" (incorrect mapping from parent codegen). + * Also verifies that useResponseEntity=false does not crash when the first response is 'default'. + */ + @Test + public void defaultResponseCodeRenderedAsDefault() throws Exception { + Path root = generateApiSources(Map.of( + KotlinSpringServerCodegen.REACTIVE, false, + KotlinSpringServerCodegen.ANNOTATION_LIBRARY, "swagger2", + KotlinSpringServerCodegen.INTERFACE_ONLY, true, + KotlinSpringServerCodegen.USE_RESPONSE_ENTITY, false + ), Map.of( + CodegenConstants.MODELS, "false", + CodegenConstants.MODEL_TESTS, "false", + CodegenConstants.MODEL_DOCS, "false", + CodegenConstants.APIS, "true", + CodegenConstants.SUPPORTING_FILES, "false" + )); + Path userApi = root.resolve("src/main/kotlin/org/openapitools/api/UserApi.kt"); + // operations whose only OpenAPI response is 'default:' must use responseCode = "default" + assertFileContains(userApi, + "ApiResponse(responseCode = \"default\", description = \"successful operation\")" + ); + // explicit HTTP 200 responses must still use the concrete status code + assertFileContains(userApi, + "ApiResponse(responseCode = \"200\", description = \"successful operation\", content" + ); + // the raw internal representation ("0") must never appear in generated output + assertFileNotContains(userApi, + "responseCode = \"0\"" + ); + } + @Test public void reactiveWithoutFlow() throws Exception { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java index 3eb1b802c5fd..620fd4756420 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/mustache/SpringHttpStatusLambdaTest.java @@ -74,8 +74,7 @@ public static Object[][] springHttpStatusCodes() { {"UNSUPPORTED_MEDIA_TYPE", "415"}, {"UPGRADE_REQUIRED", "426"}, {"URI_TOO_LONG", "414"}, - {"VARIANT_ALSO_NEGOTIATES", "506"}, - {"OK", "default"} + {"VARIANT_ALSO_NEGOTIATES", "506"} }; }