Skip to content

feat(kotlin-spring): support generic ResponseEntity return types - #24805

Open
c19yamamoto wants to merge 8 commits into
OpenAPITools:masterfrom
c19yamamoto:codex/kotlin-spring-generic-response-entity
Open

c19yamamoto wants to merge 8 commits into
OpenAPITools:masterfrom
c19yamamoto:codex/kotlin-spring-generic-response-entity

Conversation

@c19yamamoto

@c19yamamoto c19yamamoto commented Aug 29, 2026 •

Copy link
Copy Markdown

Operations returning an empty 201 response or a 202 response with a body can currently generate an unusable ResponseEntity<Unit> signature. This adds generateGenericResponseEntity=NEVER|ALWAYS to kotlin-spring, with ALWAYS generating ResponseEntity<*>.

NEVER (default) preserves existing return types. The option has no effect when useResponseEntity=false. String modes allow future extensions; boolean aliases are not supported.

Supports controllers, API interfaces, delegates, generated API tests, and declarative HTTP interfaces.

Fixes #24804.

Tests

  • ./mvnw clean package: full project build and tests passed, including 292 KotlinSpringServerCodegenTest cases.
  • ./bin/generate-samples.sh ./bin/configs/*.yaml: all 800 sample configurations regenerated successfully, with no differences.
  • ./bin/utils/export_docs_generators.sh: all generator documentation regenerated successfully, with no differences.
  • Verified CLI output: ResponseEntity<*> with ALWAYS, and ResponseEntity<Unit> with NEVER for the declarative HTTP interface.

Kotlin technical committee: @karismann @Zomzog @andrewemery @4brunu @stefankoppier @e5l

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache Outdated
Comment thread modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache Outdated
@c19yamamoto
c19yamamoto marked this pull request as draft September 5, 2026 05:44
@c19yamamoto
c19yamamoto marked this pull request as ready for review September 5, 2026 05:44

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache:39">
P2: The return-type expression is now a single ~700-character line that embeds the sealed-interface/returnTypes logic twice (once in the `useResponseEntity` branch, once in `^useResponseEntity`), and the identical block is duplicated again in apiInterface.mustache. The `^useResponseEntity` and `^useGenericResponseEntity` cases are equivalent, so this large conditional expression is copy-pasted four times across the two templates. This is a real maintainability/DRY regression: any change to how the body type is resolved (e.g., a new response-type mode) must be made in four places, and the unreadable single-line Mustache makes divergence likely. Extract the shared sealed/returnTypes resolution into a dedicated partial (e.g. `returnTypesBody.mustache`) and reference it from both the `useGenericResponseEntity` and non-generic branches in both templates.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

{{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}},
{{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},
{{/includeHttpRequestContext}}{{/hasParams}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} {
{{/includeHttpRequestContext}}{{/hasParams}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}): {{#useResponseEntity}}ResponseEntity<{{#useGenericResponseEntity}}*{{/useGenericResponseEntity}}{{^useGenericResponseEntity}}{{#useSealedResponseInterfaces}}{{#vendorExtensions.x-sealed-response-interface}}{{vendorExtensions.x-sealed-response-interface}}{{/vendorExtensions.x-sealed-response-interface}}{{^vendorExtensions.x-sealed-response-interface}}{{>returnTypes}}{{/vendorExtensions.x-sealed-response-interface}}{{/useSealedResponseInterfaces}}{{^useSealedResponseInterfaces}}{{>returnTypes}}{{/useSealedResponseInterfaces}}{{/useGenericResponseEntity}}>{{/useResponseEntity}}{{^useResponseEntity}}{{#useSealedResponseInterfaces}}{{#vendorExtensions.x-sealed-response-interface}}{{vendorExtensions.x-sealed-response-interface}}{{/vendorExtensions.x-sealed-response-interface}}{{^vendorExtensions.x-sealed-response-interface}}{{>returnTypes}}{{/vendorExtensions.x-sealed-response-interface}}{{/useSealedResponseInterfaces}}{{^useSealedResponseInterfaces}}{{>returnTypes}}{{/useSealedResponseInterfaces}}{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The return-type expression is now a single ~700-character line that embeds the sealed-interface/returnTypes logic twice (once in the useResponseEntity branch, once in ^useResponseEntity), and the identical block is duplicated again in apiInterface.mustache. The ^useResponseEntity and ^useGenericResponseEntity cases are equivalent, so this large conditional expression is copy-pasted four times across the two templates. This is a real maintainability/DRY regression: any change to how the body type is resolved (e.g., a new response-type mode) must be made in four places, and the unreadable single-line Mustache makes divergence likely. Extract the shared sealed/returnTypes resolution into a dedicated partial (e.g. returnTypesBody.mustache) and reference it from both the useGenericResponseEntity and non-generic branches in both templates.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache, line 39:

<comment>The return-type expression is now a single ~700-character line that embeds the sealed-interface/returnTypes logic twice (once in the `useResponseEntity` branch, once in `^useResponseEntity`), and the identical block is duplicated again in apiInterface.mustache. The `^useResponseEntity` and `^useGenericResponseEntity` cases are equivalent, so this large conditional expression is copy-pasted four times across the two templates. This is a real maintainability/DRY regression: any change to how the body type is resolved (e.g., a new response-type mode) must be made in four places, and the unreadable single-line Mustache makes divergence likely. Extract the shared sealed/returnTypes resolution into a dedicated partial (e.g. `returnTypesBody.mustache`) and reference it from both the `useGenericResponseEntity` and non-generic branches in both templates.</comment>

<file context>
@@ -36,7 +36,7 @@ interface {{classname}}Delegate {
         {{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}},
         {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},
-        {{/includeHttpRequestContext}}{{/hasParams}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} {
+        {{/includeHttpRequestContext}}{{/hasParams}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}): {{#useResponseEntity}}ResponseEntity<{{#useGenericResponseEntity}}*{{/useGenericResponseEntity}}{{^useGenericResponseEntity}}{{#useSealedResponseInterfaces}}{{#vendorExtensions.x-sealed-response-interface}}{{vendorExtensions.x-sealed-response-interface}}{{/vendorExtensions.x-sealed-response-interface}}{{^vendorExtensions.x-sealed-response-interface}}{{>returnTypes}}{{/vendorExtensions.x-sealed-response-interface}}{{/useSealedResponseInterfaces}}{{^useSealedResponseInterfaces}}{{>returnTypes}}{{/useSealedResponseInterfaces}}{{/useGenericResponseEntity}}>{{/useResponseEntity}}{{^useResponseEntity}}{{#useSealedResponseInterfaces}}{{#vendorExtensions.x-sealed-response-interface}}{{vendorExtensions.x-sealed-response-interface}}{{/vendorExtensions.x-sealed-response-interface}}{{^vendorExtensions.x-sealed-response-interface}}{{>returnTypes}}{{/vendorExtensions.x-sealed-response-interface}}{{/useSealedResponseInterfaces}}{{^useSealedResponseInterfaces}}{{>returnTypes}}{{/useSealedResponseInterfaces}}{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} {
         {{>methodBody}}{{! prevent indent}}
     }{{/skipDefaultDelegateInterface}}
</file context>

c19yamamoto and others added 2 commits September 5, 2026 15:21
Preserve both generic response entity modes and upstream pagination modes. Update the declarative HTTP interface test for upstream path-based API grouping.

Co-authored-by: Codex <noreply@openai.com>
@wing328 wing328 added this to the 7.27.0 milestone Oct 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REQ][KOTLIN-SPRING] Add an opt-in generic ResponseEntity return type

2 participants