[kotlin-client] Add oneOf discriminator support for multiplatform#23309
[kotlin-client] Add oneOf discriminator support for multiplatform#23309olsavmic wants to merge 2 commits intoOpenAPITools:masterfrom
Conversation
…rary When a oneOf schema has a discriminator, the kotlin-multiplatform generator now produces a sealed class hierarchy with @JsonClassDiscriminator instead of a brute-force wrapper that tries each type sequentially. This mirrors the approach from OpenAPITools#22610 (kotlin-server) adapted for kotlinx.serialization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
5 issues found across 40 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/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/ApiClient.kt">
<violation number="1" location="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/ApiClient.kt:23">
P2: Public primary constructor allows creating ApiClient without initializing `client`, leading to runtime crash when `request` is called.</violation>
</file>
<file name="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/docs/PetApi.md">
<violation number="1" location="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/docs/PetApi.md:23">
P2: Kotlin example is syntactically invalid: a `kotlin.String` value is assigned without quotes, causing compile failure.</violation>
</file>
<file name="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/apis/PetApi.kt">
<violation number="1" location="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/apis/PetApi.kt:76">
P2: Path parameter is inserted without pre-encoding, allowing `/` and reserved chars to alter route structure.</violation>
</file>
<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java">
<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java:1022">
P2: Multiplatform oneOf discriminator handling unconditionally overwrites child parent metadata, breaking reused child models across multiple parents.</violation>
</file>
<file name="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt">
<violation number="1" location="samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt:18">
P2: RequestConfig contract says body is excluded for caching, but implementation adds `body`, creating a misleading and contradictory API contract.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| import org.openapitools.client.auth.* | ||
|
|
||
| open class ApiClient( |
There was a problem hiding this comment.
P2: Public primary constructor allows creating ApiClient without initializing client, leading to runtime crash when request is called.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/ApiClient.kt, line 23:
<comment>Public primary constructor allows creating ApiClient without initializing `client`, leading to runtime crash when `request` is called.</comment>
<file context>
@@ -0,0 +1,188 @@
+
+import org.openapitools.client.auth.*
+
+open class ApiClient(
+ private val baseUrl: String
+) {
</file context>
| //import org.openapitools.client.models.* | ||
|
|
||
| val apiInstance = PetApi() | ||
| val id : kotlin.String = 38400000-8cf0-11bd-b23e-10b96e4ef00d // kotlin.String | |
There was a problem hiding this comment.
P2: Kotlin example is syntactically invalid: a kotlin.String value is assigned without quotes, causing compile failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/docs/PetApi.md, line 23:
<comment>Kotlin example is syntactically invalid: a `kotlin.String` value is assigned without quotes, causing compile failure.</comment>
<file context>
@@ -0,0 +1,53 @@
+//import org.openapitools.client.models.*
+
+val apiInstance = PetApi()
+val id : kotlin.String = 38400000-8cf0-11bd-b23e-10b96e4ef00d // kotlin.String |
+try {
+ val result : Pet = apiInstance.getPet(id)
</file context>
| val id : kotlin.String = 38400000-8cf0-11bd-b23e-10b96e4ef00d // kotlin.String | | |
| val id : kotlin.String = "38400000-8cf0-11bd-b23e-10b96e4ef00d" // kotlin.String | |
|
|
||
| val localVariableConfig = RequestConfig<kotlin.Any?>( | ||
| RequestMethod.GET, | ||
| "/v1/pet/{id}".replace("{" + "id" + "}", "$id"), |
There was a problem hiding this comment.
P2: Path parameter is inserted without pre-encoding, allowing / and reserved chars to alter route structure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/apis/PetApi.kt, line 76:
<comment>Path parameter is inserted without pre-encoding, allowing `/` and reserved chars to alter route structure.</comment>
<file context>
@@ -0,0 +1,90 @@
+
+ val localVariableConfig = RequestConfig<kotlin.Any?>(
+ RequestMethod.GET,
+ "/v1/pet/{id}".replace("{" + "id" + "}", "$id"),
+ query = localVariableQuery,
+ headers = localVariableHeaders,
</file context>
.../openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java
Show resolved
Hide resolved
| val params: MutableMap<String, Any> = mutableMapOf(), | ||
| val query: MutableMap<String, List<String>> = mutableMapOf(), | ||
| val requiresAuthentication: Boolean, | ||
| val body: T? = null |
There was a problem hiding this comment.
P2: RequestConfig contract says body is excluded for caching, but implementation adds body, creating a misleading and contradictory API contract.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/kotlin-multiplatform-oneOf-discriminator/src/commonMain/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt, line 18:
<comment>RequestConfig contract says body is excluded for caching, but implementation adds `body`, creating a misleading and contradictory API contract.</comment>
<file context>
@@ -0,0 +1,19 @@
+ val params: MutableMap<String, Any> = mutableMapOf(),
+ val query: MutableMap<String, List<String>> = mutableMapOf(),
+ val requiresAuthentication: Boolean,
+ val body: T? = null
+)
</file context>
…rSealedClass Adds a new boolean config option `useOneOfDiscriminatorSealedClass` (default: false) that must be explicitly enabled. This avoids breaking existing multiplatform users who rely on the current brute-force wrapper behavior for oneOf schemas. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
oneOfschemas with discriminators in the kotlin-multiplatform client library@JsonClassDiscriminator/@SerialName(kotlinx.serialization) instead of brute-force wrapper deserializationuseOneOfDiscriminatorSealedClass: true— no breaking changes for existing usersProblem
When a
oneOfschema has a discriminator (e.g., aPetparent withDogandCatsubtypes), the multiplatform generator produced a wrapper class withactualInstance: Any?that tries to deserialize against each type sequentially. This ignores the discriminator entirely, leading to fragile deserialization and no type-safe parent-child relationship.Before (default behavior, unchanged)
After (
useOneOfDiscriminatorSealedClass: true)Usage
Solution
New config option:
useOneOfDiscriminatorSealedClass(boolean, default:false) — opt-in to generate sealed class hierarchies for oneOf schemas with discriminators in multiplatformCodegen changes (
KotlinClientCodegen.java):postProcessAllModels(), whenuseOneOfDiscriminatorSealedClassis enabled, detects multiplatform oneOf models with discriminatorssetParent,setParentModel) on child modelsdiscriminatorValueon children for@SerialNameannotation@JsonClassDiscriminator)x-oneof-sealed-classvendor extension for template renderingTemplate changes (
multiplatform/oneof_class.mustache):x-oneof-sealed-classvendor extension is set: generates sealed class with@JsonClassDiscriminatorTest plan
multiplatformOneOfDiscriminatorSealedClassverifies sealed class generation,@JsonClassDiscriminator,@SerialName, parent extension, and discriminator property removalKotlinClientCodegenModelTesttests pass (no regressions)kotlin-multiplatform-oneOf-discriminatorgenerated and included🤖 Generated with Claude Code