Skip to content

Commit 71e1fef

Browse files
mdemblaniclaude
andauthored
fix(kotlin-spring): preserve 'default' response code in postProcessOperationsWithModels (#23833)
* 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 * 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 <noreply@anthropic.com> * fix(kotlin-spring): handle 'default' response code in templates and SpringHttpStatusLambda 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 417840c commit 71e1fef

11 files changed

Lines changed: 69 additions & 34 deletions

File tree

modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v
7676
operationId = "{{{operationId}}}",
7777
description = """{{{unescapedNotes}}}""",
7878
responses = [{{#responses}}
79-
ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} ]{{#hasAuthMethods}},
79+
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}},
8080
security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}}
8181
){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}
8282
@ApiOperation(

modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ interface {{classname}} {
8888
operationId = "{{{operationId}}}",
8989
description = """{{{unescapedNotes}}}""",
9090
responses = [{{#responses}}
91-
ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}}
91+
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}}
9292
]{{#hasAuthMethods}},
9393
security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}}
9494
){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,6 +3022,41 @@ public void nonReactiveWithResponseEntity() throws Exception {
30223022
);
30233023
}
30243024

3025+
/**
3026+
* Regression test for https://github.com/OpenAPITools/openapi-generator/issues/17445.
3027+
* OpenAPI 'default' responses must emit responseCode = "default" in @ApiResponse (swagger2),
3028+
* not "0" (internal pre-processed value) or "200" (incorrect mapping from parent codegen).
3029+
* Also verifies that useResponseEntity=false does not crash when the first response is 'default'.
3030+
*/
3031+
@Test
3032+
public void defaultResponseCodeRenderedAsDefault() throws Exception {
3033+
Path root = generateApiSources(Map.of(
3034+
KotlinSpringServerCodegen.REACTIVE, false,
3035+
KotlinSpringServerCodegen.ANNOTATION_LIBRARY, "swagger2",
3036+
KotlinSpringServerCodegen.INTERFACE_ONLY, true,
3037+
KotlinSpringServerCodegen.USE_RESPONSE_ENTITY, false
3038+
), Map.of(
3039+
CodegenConstants.MODELS, "false",
3040+
CodegenConstants.MODEL_TESTS, "false",
3041+
CodegenConstants.MODEL_DOCS, "false",
3042+
CodegenConstants.APIS, "true",
3043+
CodegenConstants.SUPPORTING_FILES, "false"
3044+
));
3045+
Path userApi = root.resolve("src/main/kotlin/org/openapitools/api/UserApi.kt");
3046+
// operations whose only OpenAPI response is 'default:' must use responseCode = "default"
3047+
assertFileContains(userApi,
3048+
"ApiResponse(responseCode = \"default\", description = \"successful operation\")"
3049+
);
3050+
// explicit HTTP 200 responses must still use the concrete status code
3051+
assertFileContains(userApi,
3052+
"ApiResponse(responseCode = \"200\", description = \"successful operation\", content"
3053+
);
3054+
// the raw internal representation ("0") must never appear in generated output
3055+
assertFileNotContains(userApi,
3056+
"responseCode = \"0\""
3057+
);
3058+
}
3059+
30253060
@Test
30263061
public void reactiveWithoutFlow() throws Exception {
30273062
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();

samples/server/petstore/kotlin-spring-default/src/main/kotlin/org/openapitools/api/UserApiController.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class UserApiController() {
3838
operationId = "createUser",
3939
description = """This can only be done by the logged in user.""",
4040
responses = [
41-
ApiResponse(responseCode = "200", description = "successful operation") ],
41+
ApiResponse(responseCode = "default", description = "successful operation") ],
4242
security = [ SecurityRequirement(name = "api_key") ]
4343
)
4444
@RequestMapping(
@@ -58,7 +58,7 @@ class UserApiController() {
5858
operationId = "createUsersWithArrayInput",
5959
description = """""",
6060
responses = [
61-
ApiResponse(responseCode = "200", description = "successful operation") ],
61+
ApiResponse(responseCode = "default", description = "successful operation") ],
6262
security = [ SecurityRequirement(name = "api_key") ]
6363
)
6464
@RequestMapping(
@@ -78,7 +78,7 @@ class UserApiController() {
7878
operationId = "createUsersWithListInput",
7979
description = """""",
8080
responses = [
81-
ApiResponse(responseCode = "200", description = "successful operation") ],
81+
ApiResponse(responseCode = "default", description = "successful operation") ],
8282
security = [ SecurityRequirement(name = "api_key") ]
8383
)
8484
@RequestMapping(
@@ -160,7 +160,7 @@ class UserApiController() {
160160
operationId = "logoutUser",
161161
description = """""",
162162
responses = [
163-
ApiResponse(responseCode = "200", description = "successful operation") ],
163+
ApiResponse(responseCode = "default", description = "successful operation") ],
164164
security = [ SecurityRequirement(name = "api_key") ]
165165
)
166166
@RequestMapping(

samples/server/petstore/kotlin-springboot-delegate-nodefaults/src/main/kotlin/org/openapitools/api/UserApi.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ interface UserApi {
4646
operationId = "createUser",
4747
description = """This can only be done by the logged in user.""",
4848
responses = [
49-
ApiResponse(responseCode = "200", description = "successful operation")
49+
ApiResponse(responseCode = "default", description = "successful operation")
5050
],
5151
security = [ SecurityRequirement(name = "api_key") ]
5252
)
@@ -68,7 +68,7 @@ interface UserApi {
6868
operationId = "createUsersWithArrayInput",
6969
description = """""",
7070
responses = [
71-
ApiResponse(responseCode = "200", description = "successful operation")
71+
ApiResponse(responseCode = "default", description = "successful operation")
7272
],
7373
security = [ SecurityRequirement(name = "api_key") ]
7474
)
@@ -90,7 +90,7 @@ interface UserApi {
9090
operationId = "createUsersWithListInput",
9191
description = """""",
9292
responses = [
93-
ApiResponse(responseCode = "200", description = "successful operation")
93+
ApiResponse(responseCode = "default", description = "successful operation")
9494
],
9595
security = [ SecurityRequirement(name = "api_key") ]
9696
)
@@ -180,7 +180,7 @@ interface UserApi {
180180
operationId = "logoutUser",
181181
description = """""",
182182
responses = [
183-
ApiResponse(responseCode = "200", description = "successful operation")
183+
ApiResponse(responseCode = "default", description = "successful operation")
184184
],
185185
security = [ SecurityRequirement(name = "api_key") ]
186186
)

samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/UserApi.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ interface UserApi {
4545
operationId = "createUser",
4646
description = """This can only be done by the logged in user.""",
4747
responses = [
48-
ApiResponse(responseCode = "200", description = "successful operation")
48+
ApiResponse(responseCode = "default", description = "successful operation")
4949
],
5050
security = [ SecurityRequirement(name = "api_key") ]
5151
)
@@ -67,7 +67,7 @@ interface UserApi {
6767
operationId = "createUsersWithArrayInput",
6868
description = """""",
6969
responses = [
70-
ApiResponse(responseCode = "200", description = "successful operation")
70+
ApiResponse(responseCode = "default", description = "successful operation")
7171
],
7272
security = [ SecurityRequirement(name = "api_key") ]
7373
)
@@ -89,7 +89,7 @@ interface UserApi {
8989
operationId = "createUsersWithListInput",
9090
description = """""",
9191
responses = [
92-
ApiResponse(responseCode = "200", description = "successful operation")
92+
ApiResponse(responseCode = "default", description = "successful operation")
9393
],
9494
security = [ SecurityRequirement(name = "api_key") ]
9595
)
@@ -179,7 +179,7 @@ interface UserApi {
179179
operationId = "logoutUser",
180180
description = """""",
181181
responses = [
182-
ApiResponse(responseCode = "200", description = "successful operation")
182+
ApiResponse(responseCode = "default", description = "successful operation")
183183
],
184184
security = [ SecurityRequirement(name = "api_key") ]
185185
)

samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/UserApiController.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
3838
operationId = "createUser",
3939
description = """This can only be done by the logged in user.""",
4040
responses = [
41-
ApiResponse(responseCode = "200", description = "successful operation") ]
41+
ApiResponse(responseCode = "default", description = "successful operation") ]
4242
)
4343
@RequestMapping(
4444
method = [RequestMethod.POST],
@@ -56,7 +56,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
5656
operationId = "createUsersWithArrayInput",
5757
description = """""",
5858
responses = [
59-
ApiResponse(responseCode = "200", description = "successful operation") ]
59+
ApiResponse(responseCode = "default", description = "successful operation") ]
6060
)
6161
@RequestMapping(
6262
method = [RequestMethod.POST],
@@ -74,7 +74,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
7474
operationId = "createUsersWithListInput",
7575
description = """""",
7676
responses = [
77-
ApiResponse(responseCode = "200", description = "successful operation") ]
77+
ApiResponse(responseCode = "default", description = "successful operation") ]
7878
)
7979
@RequestMapping(
8080
method = [RequestMethod.POST],
@@ -153,7 +153,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
153153
operationId = "logoutUser",
154154
description = """""",
155155
responses = [
156-
ApiResponse(responseCode = "200", description = "successful operation") ]
156+
ApiResponse(responseCode = "default", description = "successful operation") ]
157157
)
158158
@RequestMapping(
159159
method = [RequestMethod.GET],

samples/server/petstore/kotlin-springboot-reactive-without-flow/src/main/kotlin/org/openapitools/api/UserApiController.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
3939
operationId = "createUser",
4040
description = """This can only be done by the logged in user.""",
4141
responses = [
42-
ApiResponse(responseCode = "200", description = "successful operation") ],
42+
ApiResponse(responseCode = "default", description = "successful operation") ],
4343
security = [ SecurityRequirement(name = "api_key") ]
4444
)
4545
@RequestMapping(
@@ -59,7 +59,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
5959
operationId = "createUsersWithArrayInput",
6060
description = """""",
6161
responses = [
62-
ApiResponse(responseCode = "200", description = "successful operation") ],
62+
ApiResponse(responseCode = "default", description = "successful operation") ],
6363
security = [ SecurityRequirement(name = "api_key") ]
6464
)
6565
@RequestMapping(
@@ -79,7 +79,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
7979
operationId = "createUsersWithListInput",
8080
description = """""",
8181
responses = [
82-
ApiResponse(responseCode = "200", description = "successful operation") ],
82+
ApiResponse(responseCode = "default", description = "successful operation") ],
8383
security = [ SecurityRequirement(name = "api_key") ]
8484
)
8585
@RequestMapping(
@@ -161,7 +161,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
161161
operationId = "logoutUser",
162162
description = """""",
163163
responses = [
164-
ApiResponse(responseCode = "200", description = "successful operation") ],
164+
ApiResponse(responseCode = "default", description = "successful operation") ],
165165
security = [ SecurityRequirement(name = "api_key") ]
166166
)
167167
@RequestMapping(

samples/server/petstore/kotlin-springboot-reactive/src/main/kotlin/org/openapitools/api/UserApiController.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
3939
operationId = "createUser",
4040
description = """This can only be done by the logged in user.""",
4141
responses = [
42-
ApiResponse(responseCode = "200", description = "successful operation") ],
42+
ApiResponse(responseCode = "default", description = "successful operation") ],
4343
security = [ SecurityRequirement(name = "api_key") ]
4444
)
4545
@RequestMapping(
@@ -59,7 +59,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
5959
operationId = "createUsersWithArrayInput",
6060
description = """""",
6161
responses = [
62-
ApiResponse(responseCode = "200", description = "successful operation") ],
62+
ApiResponse(responseCode = "default", description = "successful operation") ],
6363
security = [ SecurityRequirement(name = "api_key") ]
6464
)
6565
@RequestMapping(
@@ -79,7 +79,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
7979
operationId = "createUsersWithListInput",
8080
description = """""",
8181
responses = [
82-
ApiResponse(responseCode = "200", description = "successful operation") ],
82+
ApiResponse(responseCode = "default", description = "successful operation") ],
8383
security = [ SecurityRequirement(name = "api_key") ]
8484
)
8585
@RequestMapping(
@@ -161,7 +161,7 @@ class UserApiController(@Autowired(required = true) val service: UserApiService)
161161
operationId = "logoutUser",
162162
description = """""",
163163
responses = [
164-
ApiResponse(responseCode = "200", description = "successful operation") ],
164+
ApiResponse(responseCode = "default", description = "successful operation") ],
165165
security = [ SecurityRequirement(name = "api_key") ]
166166
)
167167
@RequestMapping(

samples/server/petstore/kotlin-springboot-request-cookie/src/main/kotlin/org/openapitools/api/UserApi.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ interface UserApi {
4343
operationId = "createUser",
4444
description = """This can only be done by the logged in user.""",
4545
responses = [
46-
ApiResponse(responseCode = "200", description = "successful operation")
46+
ApiResponse(responseCode = "default", description = "successful operation")
4747
]
4848
)
4949
@RequestMapping(
@@ -64,7 +64,7 @@ interface UserApi {
6464
operationId = "createUsersWithArrayInput",
6565
description = """""",
6666
responses = [
67-
ApiResponse(responseCode = "200", description = "successful operation")
67+
ApiResponse(responseCode = "default", description = "successful operation")
6868
]
6969
)
7070
@RequestMapping(
@@ -85,7 +85,7 @@ interface UserApi {
8585
operationId = "createUsersWithListInput",
8686
description = """""",
8787
responses = [
88-
ApiResponse(responseCode = "200", description = "successful operation")
88+
ApiResponse(responseCode = "default", description = "successful operation")
8989
]
9090
)
9191
@RequestMapping(
@@ -173,7 +173,7 @@ interface UserApi {
173173
operationId = "logoutUser",
174174
description = """""",
175175
responses = [
176-
ApiResponse(responseCode = "200", description = "successful operation")
176+
ApiResponse(responseCode = "default", description = "successful operation")
177177
]
178178
)
179179
@RequestMapping(

0 commit comments

Comments
 (0)