Kotlin ktor delegate pattern support#23756
Open
chomnoue wants to merge 11 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
14 issues found across 79 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="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/main/resources/logback.xml">
<violation number="1" location="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/main/resources/logback.xml:4">
P2: Use calendar year (`yyyy`) instead of week-year (`YYYY`) in the Logback timestamp pattern.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/Delegates.kt.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/Delegates.kt.mustache:44">
P1: Unsafe `as T` casting makes missing delegates crash before the intended APINotImplementedException path can run.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/README.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/README.mustache:155">
P2: Catch-all StatusPages handler has no fallback, so unexpected throwables are intercepted but not responded to.</violation>
</file>
<file name="docs/generators/kotlin-server.md">
<violation number="1" location="docs/generators/kotlin-server.md:25">
P3: The documented default for `delegatePattern` is wrong: the generator defaults it to `false`, so this line misleads users about the default scaffold shape.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-server/_common_validation.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/_common_validation.mustache:57">
P2: Floating-point multipleOf checks use exact modulo equality, which can falsely reject valid Float/Double values due to precision error.</violation>
</file>
<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java">
<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java:182">
P1: delegatePattern is registered with the wrong default value source (`getAutoHeadFeatureEnabled()`), which can make the option default to the auto-head setting instead of its own false default.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/apiDelegate.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/apiDelegate.mustache:17">
P2: Multipart file-only operations can generate an illegal `data class XMultiPart()` with no constructor properties, causing Kotlin compilation to fail.</violation>
<violation number="2" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/apiDelegate.mustache:39">
P1: Generated code throws the wrong exception type/signature here; it should use `BadParameterException`, otherwise the template emits uncompilable code for required multipart parameters.</violation>
<violation number="3" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/apiDelegate.mustache:46">
P1: Generated multipart builder passes nullable builder properties into non-null constructor parameters, causing compile errors for non-null form fields.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/_extract_param_value.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/_extract_param_value.mustache:1">
P1: Enum request parsing uses Kotlin constant names instead of the generated enum wire values, so valid OpenAPI enum inputs can be rejected when names differ from serialized values.</violation>
<violation number="2" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/_extract_param_value.mustache:1">
P1: Numeric parameters are parsed with `{{{dataType}}}(it)`, which can generate invalid Kotlin for common numeric types like Int/Long/Float/Double.</violation>
<violation number="3" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor/_extract_param_value.mustache:1">
P1: Uses `BadRequestException` with `parameterName`/`validationType`, but the Ktor delegate templates and documented handler path use `BadParameterException`; this likely generates invalid or unhandled validation exceptions.</violation>
</file>
<file name="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/test/kotlin/org/openapitools/server/TestUtil.kt">
<violation number="1" location="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/test/kotlin/org/openapitools/server/TestUtil.kt:43">
P2: Catch-all StatusPages handler swallows unexpected exceptions because it matches every `Throwable` but only handles two specific types and has no fallback path.</violation>
</file>
<file name="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/test/kotlin/org/openapitools/server/PetApiTest.kt">
<violation number="1" location="samples/server/petstore/kotlin-server/ktor-delegate-pattern/src/test/kotlin/org/openapitools/server/PetApiTest.kt:246">
P2: Invalid-status test doesn't exercise validation; it only asserts the no-delegate 501 fallback, so it can pass even if status validation is broken.</violation>
</file>
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
added 6 commits
May 11, 2026 12:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #23755
Adds delegate pattern for the kotlin-server ktor generator.
Integration tests added under samples/server/petstore/kotlin-server/ktor-delegate-pattern
See the readme under samples/server/petstore/kotlin-server/ktor-delegate-pattern/generated/README.md for usage example.
Using the delegate pattern (ktor)
When generating a server with the
delegatePatternenabled, the router delegates request handling to small, focused interfaces you implement. This is ideal for API‑first development: you wire your business logic without touching generated routing code.What gets generated
For each API: an interface
org.openapitools.server.apis.<ApiName>Delegatewith onesuspendfunction per operation. Each function receives parsed parameters plus aio.ktor.server.routing.RoutingCallnamedcall.In
org.openapitools.server.infrastructure:AppDelegates– a data holder aggregating all optional delegates.Delegates– helpers to register and inject delegates (Application.delegates(AppDelegates(...)),RoutingCall.delegates(AppDelegates(...)).APINotImplementedException– thrown when a delegate is missing.–
BadParameterException– used for parameter validation errors (400).Generated interface:
org.openapitools.server.apis.PetApiDelegateGenerated interface:
org.openapitools.server.apis.StoreApiDelegateGenerated interface:
org.openapitools.server.apis.UserApiDelegateImplement a delegate
Implement the generated interface(s) in your codebase. Example (using the first generated API):
Notes:
call.respond(...)inside the delegate. Return the value; the generated route will serialize and respond for you. For operations without a response body, returnkotlin.Unitand the route responds with 200 by default.Register your delegates
Register your implementations once during application startup so routes can resolve them:
If you prefer to register APIs individually:
If you prefer per-request scoping, you may also attach delegates to a specific
RoutingCall:Runtime behavior
APINotImplementedExceptionand responds with 501 Not Implemented.BadParameterExceptionis thrown and the server responds with 400 Bad Request.call.principal<org.openapitools.server.infrastructure.ApiPrincipal>()when auth is enabled for the operation.Handle delegate exceptions with StatusPages
To return consistent responses for delegate errors, install Ktor
StatusPagesand map both exceptions:Handle file uploads in delegates
For multipart endpoints, generated delegates receive an
<operation_id>MultiPartReceiver(operation-specific name) so you can parse form fields and process file parts inside your implementation.Example (
uploadFile):PR checklist
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.
master(upcoming7.x.0minor release - breaking changes with fallbacks),8.0.x(breaking changes without fallbacks)"fixes #123"present in the PR description)@karismann, @Zomzog, @andrewemery @4brunu @yutaka0m @stefankoppier @e5l @dennisameling
Summary by cubic
Adds delegate pattern support to the
kotlin-servergenerator forktor, letting routes call generated*ApiDelegateinterfaces for clean separation between transport and business logic. Also fixesmultipleOfvalidation for floating-point values and improves enum/number parameter parsing with safer delegate injection.New Features
delegatePatternoption forktorinkotlin-server(documented in generator options).*ApiDelegateinterfaces per API and anAllApis()route helper.AppDelegates,Delegates(safeas?lookups),APINotImplementedException,BadParameterException.validate()methods; violations return 400 viaBadParameterException.multipleOfnow correctly supports float/double values.fromValue(), numbers viato<Type>(); invalids produce clear 400 errors.*MultiPartReceiver.samples/server/petstore/kotlin-server/ktor-delegate-patternwith tests and a Maven profile.Migration
additionalProperties.delegatePattern=truewhen generatingkotlin-serverwithktor.XxxApiDelegateand register once:Application.delegates(AppDelegates(xxxApiDelegate = ...))(or per-request viacall.delegates(...)).call.respond(...). Unimplemented ops throwAPINotImplementedException(501).StatusPagesto handleBadParameterException(400) andAPINotImplementedException(501).Written for commit 3de9af8. Summary will update on new commits.