Skip to content

Commit 54cedcb

Browse files
wing328rlnt
andauthored
[kotlin-client] Support for integer enums for multiplatform with custom serializer (#23211)
* use custom serializer for non string enums for multiplatform library * add missing imports * add tests for enum integers --------- Co-authored-by: rlnt <relentless@rlnt.dev>
1 parent 4603cc9 commit 54cedcb

File tree

36 files changed

+1425
-1
lines changed

36 files changed

+1425
-1
lines changed

.github/workflows/samples-kotlin-client.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
- samples/client/petstore/kotlin-default-values-jvm-okhttp4
4949
- samples/client/petstore/kotlin-default-values-jvm-retrofit2
5050
- samples/client/petstore/kotlin-default-values-multiplatform
51+
- samples/client/petstore/kotlin-enum-integers-multiplatform
5152
- samples/client/petstore/kotlin-array-simple-string-jvm-okhttp4
5253
- samples/client/petstore/kotlin-array-simple-string-multiplatform
5354
- samples/client/petstore/kotlin-bigdecimal-default-multiplatform
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
generatorName: kotlin
2+
outputDir: samples/client/petstore/kotlin-enum-integers-multiplatform
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/issue_21204_enum_integers.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/kotlin-client
5+
additionalProperties:
6+
artifactId: kotlin-enum-integers-multiplatform
7+
library: multiplatform
8+
dateLibrary: kotlinx-datetime
9+
sortParamsByRequiredFlag: false

modules/openapi-generator/src/main/resources/kotlin-client/enum_class.mustache

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,17 @@ import kotlinx.serialization.encoding.Encoder
4242
{{/multiplatform}}
4343
{{#multiplatform}}
4444
import kotlinx.serialization.*
45+
import kotlinx.serialization.builtins.serializer
46+
import kotlinx.serialization.encoding.Decoder
47+
import kotlinx.serialization.encoding.Encoder
4548
{{/multiplatform}}
4649

4750
/**
4851
* {{{description}}}
4952
*
5053
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
5154
*/
52-
{{#multiplatform}}@Serializable{{/multiplatform}}{{#kotlinx_serialization}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}{{^isString}}(with = {{classname}}Serializer::class){{/isString}}{{/enumUnknownDefaultCase}}{{/kotlinx_serialization}}
55+
{{#multiplatform}}@Serializable{{^isString}}(with = {{classname}}Serializer::class){{/isString}}{{/multiplatform}}{{#kotlinx_serialization}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}{{^isString}}(with = {{classname}}Serializer::class){{/isString}}{{/enumUnknownDefaultCase}}{{/kotlinx_serialization}}
5356
{{^multiplatform}}
5457
{{#moshi}}
5558
@JsonClass(generateAdapter = false)
@@ -74,7 +77,9 @@ import kotlinx.serialization.*
7477
{{/kotlinx_serialization}}
7578
{{/multiplatform}}
7679
{{#multiplatform}}
80+
{{#isString}}
7781
@SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}})
82+
{{/isString}}
7883
{{/multiplatform}}
7984
{{#isArray}}
8085
{{#isList}}
@@ -172,3 +177,20 @@ internal object {{classname}}Serializer : KSerializer<{{classname}}> {
172177
}
173178
{{/isString}}{{/enumUnknownDefaultCase}}
174179
{{/kotlinx_serialization}}
180+
{{#multiplatform}}
181+
{{^isString}}
182+
internal object {{classname}}Serializer : KSerializer<{{classname}}> {
183+
override val descriptor = {{dataType}}.serializer().descriptor
184+
185+
override fun deserialize(decoder: Decoder): {{classname}} {
186+
val value = decoder.decodeSerializableValue({{{dataType}}}.serializer())
187+
return {{classname}}.values().firstOrNull { it.value == value }
188+
?: throw IllegalArgumentException("Unknown enum value: $value")
189+
}
190+
191+
override fun serialize(encoder: Encoder, value: {{classname}}) {
192+
encoder.encodeSerializableValue({{{dataType}}}.serializer(), value.value)
193+
}
194+
}
195+
{{/isString}}
196+
{{/multiplatform}}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
openapi: 3.0.3
2+
info:
3+
title: Demo
4+
version: v1
5+
6+
components:
7+
schemas:
8+
Code:
9+
type: integer
10+
enum:
11+
- 0
12+
- 1
13+
- 2
14+
15+
paths:
16+
/do:
17+
get:
18+
summary: Do something
19+
responses:
20+
200:
21+
description: Successful response
22+
content:
23+
application/json:
24+
schema:
25+
type: object
26+
required:
27+
- code
28+
properties:
29+
code:
30+
$ref: '#/components/schemas/Code'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
README.md
2+
build.gradle.kts
3+
docs/Code.md
4+
docs/DefaultApi.md
5+
docs/DoGet200Response.md
6+
gradle/wrapper/gradle-wrapper.jar
7+
gradle/wrapper/gradle-wrapper.properties
8+
gradlew
9+
gradlew.bat
10+
settings.gradle.kts
11+
src/commonMain/kotlin/org/openapitools/client/apis/DefaultApi.kt
12+
src/commonMain/kotlin/org/openapitools/client/auth/ApiKeyAuth.kt
13+
src/commonMain/kotlin/org/openapitools/client/auth/Authentication.kt
14+
src/commonMain/kotlin/org/openapitools/client/auth/HttpBasicAuth.kt
15+
src/commonMain/kotlin/org/openapitools/client/auth/HttpBearerAuth.kt
16+
src/commonMain/kotlin/org/openapitools/client/auth/OAuth.kt
17+
src/commonMain/kotlin/org/openapitools/client/infrastructure/ApiAbstractions.kt
18+
src/commonMain/kotlin/org/openapitools/client/infrastructure/ApiClient.kt
19+
src/commonMain/kotlin/org/openapitools/client/infrastructure/Base64ByteArray.kt
20+
src/commonMain/kotlin/org/openapitools/client/infrastructure/HttpResponse.kt
21+
src/commonMain/kotlin/org/openapitools/client/infrastructure/OctetByteArray.kt
22+
src/commonMain/kotlin/org/openapitools/client/infrastructure/PartConfig.kt
23+
src/commonMain/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt
24+
src/commonMain/kotlin/org/openapitools/client/infrastructure/RequestMethod.kt
25+
src/commonMain/kotlin/org/openapitools/client/models/Code.kt
26+
src/commonMain/kotlin/org/openapitools/client/models/DoGet200Response.kt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7.21.0-SNAPSHOT
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# org.openapitools.client - Kotlin client library for Demo
2+
3+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
4+
5+
## Overview
6+
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate an API client.
7+
8+
- API version: v1
9+
- Package version:
10+
- Generator version: 7.21.0-SNAPSHOT
11+
- Build package: org.openapitools.codegen.languages.KotlinClientCodegen
12+
13+
## Requires
14+
15+
* Kotlin 2.2.20
16+
17+
## Build
18+
19+
```
20+
./gradlew check assemble
21+
```
22+
23+
This runs all tests and packages the library.
24+
25+
## Features/Implementation Notes
26+
27+
* Supports JSON inputs/outputs, File inputs, and Form inputs.
28+
* Supports collection formats for query parameters: csv, tsv, ssv, pipes.
29+
* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions.
30+
31+
32+
<a id="documentation-for-api-endpoints"></a>
33+
## Documentation for API Endpoints
34+
35+
All URIs are relative to *http://localhost*
36+
37+
| Class | Method | HTTP request | Description |
38+
| ------------ | ------------- | ------------- | ------------- |
39+
| *DefaultApi* | [**doGet**](docs/DefaultApi.md#doget) | **GET** /do | Do something |
40+
41+
42+
<a id="documentation-for-models"></a>
43+
## Documentation for Models
44+
45+
- [org.openapitools.client.models.Code](docs/Code.md)
46+
- [org.openapitools.client.models.DoGet200Response](docs/DoGet200Response.md)
47+
48+
49+
<a id="documentation-for-authorization"></a>
50+
## Documentation for Authorization
51+
52+
Endpoints do not require authorization.
53+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
2+
3+
plugins {
4+
kotlin("multiplatform") version "2.2.20" // kotlin_version
5+
kotlin("plugin.serialization") version "2.2.20" // kotlin_version
6+
}
7+
8+
group = "org.openapitools"
9+
version = "1.0.0"
10+
11+
val kotlin_version = "2.2.20"
12+
val coroutines_version = "1.10.2"
13+
val serialization_version = "1.9.0"
14+
val ktor_version = "3.2.3"
15+
16+
repositories {
17+
mavenCentral()
18+
}
19+
20+
kotlin {
21+
jvm()
22+
iosX64()
23+
iosArm64()
24+
iosSimulatorArm64()
25+
js {
26+
browser()
27+
nodejs()
28+
}
29+
30+
sourceSets {
31+
commonMain {
32+
dependencies {
33+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
34+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version")
35+
36+
api("io.ktor:ktor-client-core:$ktor_version")
37+
api("io.ktor:ktor-client-serialization:$ktor_version")
38+
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
39+
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
40+
41+
api("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
42+
}
43+
}
44+
45+
commonTest {
46+
dependencies {
47+
implementation(kotlin("test"))
48+
implementation("io.ktor:ktor-client-mock:$ktor_version")
49+
}
50+
}
51+
52+
jvmMain {
53+
dependencies {
54+
implementation(kotlin("stdlib-jdk7"))
55+
implementation("io.ktor:ktor-client-cio-jvm:$ktor_version")
56+
}
57+
}
58+
59+
jvmTest {
60+
dependencies {
61+
implementation(kotlin("test-junit"))
62+
}
63+
}
64+
65+
iosMain {
66+
dependencies {
67+
api("io.ktor:ktor-client-ios:$ktor_version")
68+
}
69+
}
70+
71+
jsMain {
72+
dependencies {
73+
api("io.ktor:ktor-client-js:$ktor_version")
74+
}
75+
}
76+
77+
all {
78+
languageSettings {
79+
optIn("kotlin.time.ExperimentalTime")
80+
}
81+
}
82+
}
83+
}
84+
85+
tasks {
86+
register<Exec>("iosTest") {
87+
val device = project.findProperty("device")?.toString() ?: "iPhone 8"
88+
dependsOn("linkDebugTestIosX64")
89+
group = JavaBasePlugin.VERIFICATION_GROUP
90+
description = "Execute unit tests on ${device} simulator"
91+
val binary = kotlin.targets.getByName<KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
92+
commandLine("xcrun", "simctl", "spawn", device, binary.outputFile)
93+
}
94+
register("test") {
95+
dependsOn("allTests")
96+
}
97+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
# Code
3+
4+
## Enum
5+
6+
7+
* `_0` (value: `0`)
8+
9+
* `_1` (value: `1`)
10+
11+
* `_2` (value: `2`)
12+
13+
14+

0 commit comments

Comments
 (0)