Add support for oneOf with discriminator when using kotlinx.serialization (#22373)

* Generate wrappers for oneOf with discriminator when using kotlinx.serialization

* Add spec with oneOf using discriminator

* Add config to generate samples

* Generate samples

* Update samples

* Change naming of wrapper classes

* Fix empty model test

* Update GH workflow with new samples
This commit is contained in:
Daniil Iastremskii
2025-11-18 20:55:37 +02:00
committed by GitHub
parent c52cc1f275
commit e86daf9059
51 changed files with 1949 additions and 19 deletions

View File

@@ -72,6 +72,7 @@ jobs:
- samples/client/others/kotlin-jvm-okhttp-path-comments
- samples/client/others/kotlin-integer-enum
- samples/client/petstore/kotlin-allOf-discriminator-kotlinx-serialization
- samples/client/others/kotlin-oneOf-discriminator-kotlinx-serialization
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5

View File

@@ -0,0 +1,12 @@
generatorName: kotlin
outputDir: samples/client/others/kotlin-oneOf-discriminator-kotlinx-serialization
inputSpec: modules/openapi-generator/src/test/resources/3_0/kotlin/polymorphism-oneof-discriminator.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-client
additionalProperties:
artifactId: kotlin-oneOf-discriminator
serializableModel: "false"
dateLibrary: java8
library: jvm-retrofit2
enumUnknownDefaultCase: true
serializationLibrary: kotlinx_serialization
generateOneOfAnyOfWrappers: true

View File

@@ -20,6 +20,7 @@ package org.openapitools.codegen.languages;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import lombok.Getter;
@@ -35,6 +36,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -1172,4 +1176,15 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
}
}
}
protected static abstract class CustomLambda implements Mustache.Lambda {
@Override
public void execute(Template.Fragment frag, Writer out) throws IOException {
final StringWriter tempWriter = new StringWriter();
frag.execute(tempWriter);
out.write(formatFragment(tempWriter.toString()));
}
public abstract String formatFragment(String fragment);
}
}

View File

@@ -22,7 +22,6 @@ import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.features.*;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
@@ -40,10 +39,8 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.samskivert.mustache.Mustache;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel;
@@ -60,14 +57,7 @@ import org.openapitools.codegen.meta.features.ParameterFeature;
import org.openapitools.codegen.meta.features.SchemaSupportFeature;
import org.openapitools.codegen.meta.features.SecurityFeature;
import org.openapitools.codegen.meta.features.WireFormatFeature;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.ReplaceAllLambda;
import org.openapitools.codegen.utils.ProcessUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.Collections.sort;
@@ -569,6 +559,7 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
// as the parser interrupts that as a start of a multiline comment.
// We replace paths like `/v1/foo/*` with `/v1/foo/<*>` to avoid this
additionalProperties.put("sanitizePathComment", new ReplaceAllLambda("\\/\\*", "/<*>"));
additionalProperties.put("fnToOneOfWrapperName", new ToOneOfWrapperName());
}
private void processDateLibrary() {
@@ -974,11 +965,21 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
if (discriminator == null) {
continue;
}
// When using generateOneOfAnyOfWrappers and encountering oneOf, we keep discriminator properties,
// because single entity can be referenced in multiple "parent" entities,
// so discriminator for one might not be discriminator for another.
boolean shouldKeepDiscriminatorField = generateOneOfAnyOfWrappers && cm.oneOf != null && !cm.oneOf.isEmpty();
if (shouldKeepDiscriminatorField) {
continue;
}
// Remove discriminator property from the base class, it is not needed in the generated code
getAllVarProperties(cm).forEach(list -> list.removeIf(var -> var.name.equals(discriminator.getPropertyName())));
for (CodegenDiscriminator.MappedModel mappedModel : discriminator.getMappedModels()) {
// Add the mapping name to additionalProperties.disciminatorValue
// Add the mapping name to additionalProperties.discriminatorValue
// The mapping name is used to define SerializedName, which in result makes derived classes
// found by kotlinx-serialization during deserialization
CodegenProperty additionalProperties = mappedModel.getModel().getAdditionalProperties();
@@ -996,7 +997,6 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
mappedModel.getModel().setHasVars(false);
}
}
}
}
}
@@ -1141,6 +1141,13 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
param.defaultValue = type + "." + param.enumDefaultValue;
}
private class ToOneOfWrapperName extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return toModelName(StringUtils.lowerCase(fragment)) + "Wrapper";
}
}
@Override
public void postProcess() {
System.out.println("################################################################################");

View File

@@ -37,6 +37,28 @@ import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
{{/enumUnknownDefaultCase}}
{{^enumUnknownDefaultCase}}
{{#generateOneOfAnyOfWrappers}}
{{#discriminator}}
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
{{/discriminator}}
{{/generateOneOfAnyOfWrappers}}
{{/enumUnknownDefaultCase}}
{{#generateOneOfAnyOfWrappers}}
{{#discriminator}}
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
{{/discriminator}}
{{/generateOneOfAnyOfWrappers}}
{{#hasEnums}}
{{/hasEnums}}
{{/kotlinx_serialization}}
@@ -57,7 +79,9 @@ import java.io.Serializable
import {{roomModelPackage}}.{{classname}}RoomModel
import {{packageName}}.infrastructure.ITransformForStorage
{{/generateRoomModels}}
{{^kotlinx_serialization}}
import java.io.IOException
{{/kotlinx_serialization}}
/**
* {{{description}}}
@@ -66,12 +90,68 @@ import java.io.IOException
{{#parcelizeModels}}
@Parcelize
{{/parcelizeModels}}
{{^generateOneOfAnyOfWrappers}}
{{^discriminator}}
{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}}
{{/discriminator}}
{{/generateOneOfAnyOfWrappers}}
{{#kotlinx_serialization}}
{{#generateOneOfAnyOfWrappers}}
{{#discriminator}}
@Serializable(with = {{classname}}Serializer::class)
{{/discriminator}}
{{/generateOneOfAnyOfWrappers}}
{{/kotlinx_serialization}}
{{#isDeprecated}}
@Deprecated(message = "This schema is deprecated.")
{{/isDeprecated}}
{{>additionalModelTypeAnnotations}}
{{#kotlinx_serialization}}
{{#nonPublicApi}}internal {{/nonPublicApi}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}sealed interface {{classname}} {
{{#discriminator.mappedModels}}
@JvmInline
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}} value class {{#fnToOneOfWrapperName}}{{mappingName}}{{/fnToOneOfWrapperName}}(val value: {{modelName}}) : {{classname}}
{{/discriminator.mappedModels}}
}
{{#nonPublicApi}}internal {{/nonPublicApi}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}object {{classname}}Serializer : KSerializer<{{classname}}> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("{{classname}}")
override fun serialize(encoder: Encoder, value: {{classname}}) {
require(encoder is JsonEncoder)
val jsonObject = when (value) {
{{#discriminator.mappedModels}}
is {{classname}}.{{#fnToOneOfWrapperName}}{{mappingName}}{{/fnToOneOfWrapperName}} -> {
val jsonMap = encoder.json.encodeToJsonElement({{modelName}}.serializer(), value.value).jsonObject.toMutableMap()
jsonMap["{{discriminator.propertyBaseName}}"] = JsonPrimitive("{{mappingName}}")
JsonObject(jsonMap)
}
{{/discriminator.mappedModels}}
}
encoder.encodeJsonElement(jsonObject)
}
override fun deserialize(decoder: Decoder): {{classname}} {
require(decoder is JsonDecoder)
val element = decoder.decodeJsonElement().jsonObject
val discriminatorValue = element["{{discriminator.propertyBaseName}}"]?.jsonPrimitive?.content
?: throw SerializationException("Missing {{discriminator.propertyBaseName}} field")
return when (discriminatorValue) {
{{#discriminator.mappedModels}}
"{{mappingName}}" -> {
val decoded = decoder.json.decodeFromJsonElement({{modelName}}.serializer(), element)
{{classname}}.{{#fnToOneOfWrapperName}}{{mappingName}}{{/fnToOneOfWrapperName}}(decoded)
}
{{/discriminator.mappedModels}}
else -> throw SerializationException("Unknown {{classname}} {{discriminator.propertyBaseName}}: $discriminatorValue")
}
}
}
{{/kotlinx_serialization}}
{{^kotlinx_serialization}}
{{#nonPublicApi}}internal {{/nonPublicApi}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) {
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}class CustomTypeAdapterFactory : TypeAdapterFactory {
@@ -332,4 +412,5 @@ import java.io.IOException
}
}
}
}
}
{{/kotlinx_serialization}}

View File

@@ -43,7 +43,7 @@ import java.util.List;
import java.util.Map;
import static org.openapitools.codegen.CodegenConstants.*;
import static org.openapitools.codegen.TestUtils.assertFileContains;
import static org.openapitools.codegen.languages.KotlinClientCodegen.GENERATE_ONEOF_ANYOF_WRAPPERS;
@SuppressWarnings("static-method")
public class KotlinClientCodegenModelTest {
@@ -502,7 +502,7 @@ public class KotlinClientCodegenModelTest {
// properties.put(CodegenConstants.LIBRARY, ClientLibrary.JVM_KTOR);
properties.put(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.UPPERCASE.toString());
properties.put(SERIALIZATION_LIBRARY, KotlinClientCodegen.SERIALIZATION_LIBRARY_TYPE.gson.toString());
properties.put(KotlinClientCodegen.GENERATE_ONEOF_ANYOF_WRAPPERS, true);
properties.put(GENERATE_ONEOF_ANYOF_WRAPPERS, true);
properties.put(API_PACKAGE, "com.toasttab.service.scim.api");
properties.put(MODEL_PACKAGE, "com.toasttab.service.scim.models");
properties.put(PACKAGE_NAME, "com.toasttab.service.scim");
@@ -661,6 +661,7 @@ public class KotlinClientCodegenModelTest {
.setGeneratorName("kotlin")
.setAdditionalProperties(new HashMap<>() {{
put(CodegenConstants.MODEL_PACKAGE, "model");
put(GENERATE_ONEOF_ANYOF_WRAPPERS, false);
put(SERIALIZATION_LIBRARY, "kotlinx_serialization");
}})
.setInputSpec("src/test/resources/3_0/kotlin/empty-model.yaml")

View File

@@ -0,0 +1,83 @@
openapi: 3.0.1
info:
title: Example
description: An example
version: '0.1'
contact:
email: contact@example.org
url: 'https://example.org'
servers:
- url: http://example.org
tags:
- name: bird
paths:
'/v1/bird/{id}':
get:
tags:
- bird
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/animal'
operationId: get-bird
parameters:
- schema:
type: string
format: uuid
name: id
in: path
required: true
components:
schemas:
animal:
title: An animal
oneOf:
- $ref: '#/components/schemas/bird'
- $ref: '#/components/schemas/robobird'
discriminator:
propertyName: discriminator
mapping:
BIRD: '#/components/schemas/bird'
ROBOBIRD: '#/components/schemas/robobird'
another_animal:
title: Another animal
oneOf:
- $ref: '#/components/schemas/bird'
- $ref: '#/components/schemas/robobird'
discriminator:
propertyName: another_discriminator
mapping:
ANOTHER_BIRD: '#/components/schemas/bird'
ANOTHER_ROBOBIRD: '#/components/schemas/robobird'
bird:
title: A bird
required:
- discriminator
- another_discriminator
properties:
propertyA:
type: string
sameNameProperty:
type: integer
discriminator:
type: string
another_discriminator:
type: string
robobird:
title: A robo-bird
required:
- discriminator
- another_discriminator
properties:
propertyB:
type: string
sameNameProperty:
type: string
discriminator:
type: string
another_discriminator:
type: string

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,34 @@
README.md
build.gradle
docs/Animal.md
docs/AnotherAnimal.md
docs/Bird.md
docs/BirdApi.md
docs/Robobird.md
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.properties
gradlew
gradlew.bat
proguard-rules.pro
settings.gradle
src/main/kotlin/org/openapitools/client/apis/BirdApi.kt
src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt
src/main/kotlin/org/openapitools/client/infrastructure/AtomicBooleanAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/AtomicIntegerAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/AtomicLongAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/BigDecimalAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/BigIntegerAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/CollectionFormats.kt
src/main/kotlin/org/openapitools/client/infrastructure/LocalDateAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/ResponseExt.kt
src/main/kotlin/org/openapitools/client/infrastructure/Serializer.kt
src/main/kotlin/org/openapitools/client/infrastructure/StringBuilderAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/URIAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/URLAdapter.kt
src/main/kotlin/org/openapitools/client/infrastructure/UUIDAdapter.kt
src/main/kotlin/org/openapitools/client/models/Animal.kt
src/main/kotlin/org/openapitools/client/models/AnotherAnimal.kt
src/main/kotlin/org/openapitools/client/models/Bird.kt
src/main/kotlin/org/openapitools/client/models/Robobird.kt

View File

@@ -0,0 +1,70 @@
# org.openapitools.client - Kotlin client library for Example
An example
## Overview
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.
- API version: 0.1
- Package version:
- Generator version: 7.18.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.KotlinClientCodegen
For more information, please visit [https://example.org](https://example.org)
## Requires
* Kotlin 2.2.20
* Gradle 8.14
## Build
First, create the gradle wrapper script:
```
gradle wrapper
```
Then, run:
```
./gradlew check assemble
```
This runs all tests and packages the library.
## Features/Implementation Notes
* Supports JSON inputs/outputs, File inputs, and Form inputs.
* Supports collection formats for query parameters: csv, tsv, ssv, pipes.
* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions.
* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets.
<a id="documentation-for-api-endpoints"></a>
## Documentation for API Endpoints
All URIs are relative to *http://example.org*
| Class | Method | HTTP request | Description |
| ------------ | ------------- | ------------- | ------------- |
| *BirdApi* | [**getBird**](docs/BirdApi.md#getbird) | **GET** v1/bird/{id} | |
<a id="documentation-for-models"></a>
## Documentation for Models
- [org.openapitools.client.models.Animal](docs/Animal.md)
- [org.openapitools.client.models.AnotherAnimal](docs/AnotherAnimal.md)
- [org.openapitools.client.models.Bird](docs/Bird.md)
- [org.openapitools.client.models.Robobird](docs/Robobird.md)
<a id="documentation-for-authorization"></a>
## Documentation for Authorization
Endpoints do not require authorization.
## Author
contact@example.org

View File

@@ -0,0 +1,72 @@
group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '8.14.3'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '2.2.20'
ext.retrofitVersion = '3.0.0'
ext.spotless_version = "7.2.1"
repositories {
maven { url "https://repo1.maven.org/maven2" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.diffplug.spotless:spotless-plugin-gradle:$spotless_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlinx-serialization'
apply plugin: 'maven-publish'
apply plugin: 'com.diffplug.spotless'
repositories {
maven { url "https://repo1.maven.org/maven2" }
}
// Use spotless plugin to automatically format code, remove unused import, etc
// To apply changes directly to the file, run `gradlew spotlessApply`
// Ref: https://github.com/diffplug/spotless/tree/main/plugin-gradle
spotless {
// comment out below to run spotless as part of the `check` task
enforceCheck false
format 'misc', {
// define the files (e.g. '*.gradle', '*.md') to apply `misc` to
target '.gitignore'
// define the steps to apply to those files
trimTrailingWhitespace()
indentWithSpaces() // Takes an integer argument if you don't like 4
endWithNewline()
}
kotlin {
ktfmt()
}
}
test {
useJUnitPlatform()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:5.1.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-kotlinx-serialization:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs += "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
}

View File

@@ -0,0 +1,14 @@
# Animal
## Properties
| Name | Type | Description | Notes |
| ------------ | ------------- | ------------- | ------------- |
| **discriminator** | **kotlin.String** | | |
| **anotherDiscriminator** | **kotlin.String** | | |
| **propertyA** | **kotlin.String** | | [optional] |
| **sameNameProperty** | **kotlin.String** | | [optional] |
| **propertyB** | **kotlin.String** | | [optional] |

View File

@@ -0,0 +1,14 @@
# AnotherAnimal
## Properties
| Name | Type | Description | Notes |
| ------------ | ------------- | ------------- | ------------- |
| **discriminator** | **kotlin.String** | | |
| **anotherDiscriminator** | **kotlin.String** | | |
| **propertyA** | **kotlin.String** | | [optional] |
| **sameNameProperty** | **kotlin.String** | | [optional] |
| **propertyB** | **kotlin.String** | | [optional] |

View File

@@ -0,0 +1,13 @@
# Bird
## Properties
| Name | Type | Description | Notes |
| ------------ | ------------- | ------------- | ------------- |
| **discriminator** | **kotlin.String** | | |
| **anotherDiscriminator** | **kotlin.String** | | |
| **propertyA** | **kotlin.String** | | [optional] |
| **sameNameProperty** | **kotlin.Int** | | [optional] |

View File

@@ -0,0 +1,44 @@
# BirdApi
All URIs are relative to *http://example.org*
| Method | HTTP request | Description |
| ------------- | ------------- | ------------- |
| [**getBird**](BirdApi.md#getBird) | **GET** v1/bird/{id} | |
### Example
```kotlin
// Import classes:
//import org.openapitools.client.*
//import org.openapitools.client.infrastructure.*
//import org.openapitools.client.models.*
val apiClient = ApiClient()
val webService = apiClient.createWebservice(BirdApi::class.java)
val id : java.util.UUID = 38400000-8cf0-11bd-b23e-10b96e4ef00d // java.util.UUID |
val result : Animal = webService.getBird(id)
```
### Parameters
| Name | Type | Description | Notes |
| ------------- | ------------- | ------------- | ------------- |
| **id** | **java.util.UUID**| | |
### Return type
[**Animal**](Animal.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json

View File

@@ -0,0 +1,13 @@
# Robobird
## Properties
| Name | Type | Description | Notes |
| ------------ | ------------- | ------------- | ------------- |
| **discriminator** | **kotlin.String** | | |
| **anotherDiscriminator** | **kotlin.String** | | |
| **propertyB** | **kotlin.String** | | [optional] |
| **sameNameProperty** | **kotlin.String** | | [optional] |

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,252 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,11 @@
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
-keepclassmembers class kotlinx.serialization.json.** { *** Companion; }
-keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); }
# project specific.
-keep,includedescriptorclasses class org.openapitools.client.models.**$$serializer { *; }
-keepclassmembers class org.openapitools.client.models.** { *** Companion; }
-keepclasseswithmembers class org.openapitools.client.models.** { kotlinx.serialization.KSerializer serializer(...); }

View File

@@ -0,0 +1 @@
rootProject.name = 'kotlin-oneOf-discriminator'

View File

@@ -0,0 +1,26 @@
package org.openapitools.client.apis
import org.openapitools.client.infrastructure.CollectionFormats.*
import retrofit2.http.*
import retrofit2.Call
import okhttp3.RequestBody
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.openapitools.client.models.Animal
interface BirdApi {
/**
* GET v1/bird/{id}
*
*
* Responses:
* - 200: OK
*
* @param id
* @return [Call]<[Animal]>
*/
@GET("v1/bird/{id}")
fun getBird(@Path("id") id: java.util.UUID): Call<Animal>
}

View File

@@ -0,0 +1,111 @@
package org.openapitools.client.infrastructure
import okhttp3.Call
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.CallAdapter
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import org.openapitools.client.infrastructure.Serializer.kotlinxSerializationJson
import okhttp3.MediaType.Companion.toMediaType
class ApiClient(
private var baseUrl: String = defaultBasePath,
private val okHttpClientBuilder: OkHttpClient.Builder? = null,
private val callFactory: Call.Factory? = null,
private val callAdapterFactories: List<CallAdapter.Factory> = listOf(
),
private val converterFactories: List<Converter.Factory> = listOf(
ScalarsConverterFactory.create(),
kotlinxSerializationJson.asConverterFactory("application/json".toMediaType()),
)
) {
private val apiAuthorizations = mutableMapOf<String, Interceptor>()
var logger: ((String) -> Unit)? = null
private val retrofitBuilder: Retrofit.Builder by lazy {
Retrofit.Builder()
.baseUrl(baseUrl)
.apply {
callAdapterFactories.forEach {
addCallAdapterFactory(it)
}
}
.apply {
converterFactories.forEach {
addConverterFactory(it)
}
}
}
private val clientBuilder: OkHttpClient.Builder by lazy {
okHttpClientBuilder ?: defaultClientBuilder
}
private val defaultClientBuilder: OkHttpClient.Builder by lazy {
OkHttpClient()
.newBuilder()
.addInterceptor(HttpLoggingInterceptor { message -> logger?.invoke(message) }
.apply { level = HttpLoggingInterceptor.Level.BODY }
)
}
init {
normalizeBaseUrl()
}
/**
* Adds an authorization to be used by the client
* @param authName Authentication name
* @param authorization Authorization interceptor
* @return ApiClient
*/
fun addAuthorization(authName: String, authorization: Interceptor): ApiClient {
if (apiAuthorizations.containsKey(authName)) {
throw RuntimeException("auth name $authName already in api authorizations")
}
apiAuthorizations[authName] = authorization
clientBuilder.addInterceptor(authorization)
return this
}
fun setLogger(logger: (String) -> Unit): ApiClient {
this.logger = logger
return this
}
fun <S> createService(serviceClass: Class<S>): S {
val usedCallFactory = this.callFactory ?: clientBuilder.build()
return retrofitBuilder.callFactory(usedCallFactory).build().create(serviceClass)
}
private fun normalizeBaseUrl() {
if (!baseUrl.endsWith("/")) {
baseUrl += "/"
}
}
private inline fun <T, reified U> Iterable<T>.runOnFirst(callback: U.() -> Unit) {
for (element in this) {
if (element is U) {
callback.invoke(element)
break
}
}
}
companion object {
@JvmStatic
protected val baseUrlKey: String = "org.openapitools.client.baseUrl"
@JvmStatic
val defaultBasePath: String by lazy {
System.getProperties().getProperty(baseUrlKey, "http://example.org")
}
}
}

View File

@@ -0,0 +1,19 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.util.concurrent.atomic.AtomicBoolean
object AtomicBooleanAdapter : KSerializer<AtomicBoolean> {
override fun serialize(encoder: Encoder, value: AtomicBoolean) {
encoder.encodeBoolean(value.get())
}
override fun deserialize(decoder: Decoder): AtomicBoolean = AtomicBoolean(decoder.decodeBoolean())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicBoolean", PrimitiveKind.BOOLEAN)
}

View File

@@ -0,0 +1,19 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.util.concurrent.atomic.AtomicInteger
object AtomicIntegerAdapter : KSerializer<AtomicInteger> {
override fun serialize(encoder: Encoder, value: AtomicInteger) {
encoder.encodeInt(value.get())
}
override fun deserialize(decoder: Decoder): AtomicInteger = AtomicInteger(decoder.decodeInt())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicInteger", PrimitiveKind.INT)
}

View File

@@ -0,0 +1,19 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.util.concurrent.atomic.AtomicLong
object AtomicLongAdapter : KSerializer<AtomicLong> {
override fun serialize(encoder: Encoder, value: AtomicLong) {
encoder.encodeLong(value.get())
}
override fun deserialize(decoder: Decoder): AtomicLong = AtomicLong(decoder.decodeLong())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicLong", PrimitiveKind.LONG)
}

View File

@@ -0,0 +1,15 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.math.BigDecimal
object BigDecimalAdapter : KSerializer<BigDecimal> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): BigDecimal = BigDecimal(decoder.decodeString())
override fun serialize(encoder: Encoder, value: BigDecimal) = encoder.encodeString(value.toPlainString())
}

View File

@@ -0,0 +1,20 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.math.BigInteger
object BigIntegerAdapter : KSerializer<BigInteger> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigInteger", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): BigInteger {
return BigInteger(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: BigInteger) {
encoder.encodeString(value.toString())
}
}

View File

@@ -0,0 +1,56 @@
package org.openapitools.client.infrastructure
class CollectionFormats {
open class CSVParams {
var params: List<String>
constructor(params: List<String>) {
this.params = params
}
constructor(vararg params: String) {
this.params = listOf(*params)
}
override fun toString(): String {
return params.joinToString(",")
}
}
open class SSVParams : CSVParams {
constructor(params: List<String>) : super(params)
constructor(vararg params: String) : super(*params)
override fun toString(): String {
return params.joinToString(" ")
}
}
class TSVParams : CSVParams {
constructor(params: List<String>) : super(params)
constructor(vararg params: String) : super(*params)
override fun toString(): String {
return params.joinToString("\t")
}
}
class PIPESParams : CSVParams {
constructor(params: List<String>) : super(params)
constructor(vararg params: String) : super(*params)
override fun toString(): String {
return params.joinToString("|")
}
}
class SPACEParams : SSVParams()
}

View File

@@ -0,0 +1,22 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.time.LocalDate
import java.time.format.DateTimeFormatter
object LocalDateAdapter : KSerializer<LocalDate> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDate) {
encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE.format(value))
}
override fun deserialize(decoder: Decoder): LocalDate {
return LocalDate.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE)
}
}

View File

@@ -0,0 +1,22 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
object LocalDateTimeAdapter : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value))
}
override fun deserialize(decoder: Decoder): LocalDateTime {
return LocalDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME)
}
}

View File

@@ -0,0 +1,22 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
object OffsetDateTimeAdapter : KSerializer<OffsetDateTime> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("OffsetDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: OffsetDateTime) {
encoder.encodeString(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value))
}
override fun deserialize(decoder: Decoder): OffsetDateTime {
return OffsetDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
}
}

View File

@@ -0,0 +1,4 @@
package org.openapitools.client.infrastructure
import retrofit2.Response

View File

@@ -0,0 +1,73 @@
package org.openapitools.client.infrastructure
import java.math.BigDecimal
import java.math.BigInteger
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.util.UUID
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonBuilder
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.SerializersModuleBuilder
import java.net.URI
import java.net.URL
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
object Serializer {
private var isAdaptersInitialized = false
@JvmStatic
val kotlinxSerializationAdapters: SerializersModule by lazy {
isAdaptersInitialized = true
SerializersModule {
contextual(BigDecimal::class, BigDecimalAdapter)
contextual(BigInteger::class, BigIntegerAdapter)
contextual(LocalDate::class, LocalDateAdapter)
contextual(LocalDateTime::class, LocalDateTimeAdapter)
contextual(OffsetDateTime::class, OffsetDateTimeAdapter)
contextual(UUID::class, UUIDAdapter)
contextual(AtomicInteger::class, AtomicIntegerAdapter)
contextual(AtomicLong::class, AtomicLongAdapter)
contextual(AtomicBoolean::class, AtomicBooleanAdapter)
contextual(URI::class, URIAdapter)
contextual(URL::class, URLAdapter)
contextual(StringBuilder::class, StringBuilderAdapter)
apply(kotlinxSerializationAdaptersConfiguration)
}
}
var kotlinxSerializationAdaptersConfiguration: SerializersModuleBuilder.() -> Unit = {}
set(value) {
check(!isAdaptersInitialized) {
"Cannot configure kotlinxSerializationAdaptersConfiguration after kotlinxSerializationAdapters has been initialized."
}
field = value
}
private var isJsonInitialized = false
@JvmStatic
val kotlinxSerializationJson: Json by lazy {
isJsonInitialized = true
Json {
serializersModule = kotlinxSerializationAdapters
encodeDefaults = true
ignoreUnknownKeys = true
isLenient = true
apply(kotlinxSerializationJsonConfiguration)
}
}
var kotlinxSerializationJsonConfiguration: JsonBuilder.() -> Unit = {}
set(value) {
check(!isJsonInitialized) {
"Cannot configure kotlinxSerializationJsonConfiguration after kotlinxSerializationJson has been initialized."
}
field = value
}
}

View File

@@ -0,0 +1,18 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
object StringBuilderAdapter : KSerializer<StringBuilder> {
override fun serialize(encoder: Encoder, value: StringBuilder) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): StringBuilder = StringBuilder(decoder.decodeString())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StringBuilder", PrimitiveKind.STRING)
}

View File

@@ -0,0 +1,19 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.net.URI
object URIAdapter : KSerializer<URI> {
override fun serialize(encoder: Encoder, value: URI) {
encoder.encodeString(value.toASCIIString())
}
override fun deserialize(decoder: Decoder): URI = URI(decoder.decodeString())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URI", PrimitiveKind.STRING)
}

View File

@@ -0,0 +1,19 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.net.URL
object URLAdapter : KSerializer<URL> {
override fun serialize(encoder: Encoder, value: URL) {
encoder.encodeString(value.toExternalForm())
}
override fun deserialize(decoder: Decoder): URL = URL(decoder.decodeString())
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URL", PrimitiveKind.STRING)
}

View File

@@ -0,0 +1,21 @@
package org.openapitools.client.infrastructure
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import java.util.UUID
object UUIDAdapter : KSerializer<UUID> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): UUID {
return UUID.fromString(decoder.decodeString())
}
}

View File

@@ -0,0 +1,93 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import org.openapitools.client.models.Bird
import org.openapitools.client.models.Robobird
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
/**
*
*
*/
@Serializable(with = AnimalSerializer::class)
sealed interface Animal {
@JvmInline
value class BirdWrapper(val value: Bird) : Animal
@JvmInline
value class RobobirdWrapper(val value: Robobird) : Animal
}
object AnimalSerializer : KSerializer<Animal> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Animal")
override fun serialize(encoder: Encoder, value: Animal) {
require(encoder is JsonEncoder)
val jsonObject = when (value) {
is Animal.BirdWrapper -> {
val jsonMap = encoder.json.encodeToJsonElement(Bird.serializer(), value.value).jsonObject.toMutableMap()
jsonMap["discriminator"] = JsonPrimitive("BIRD")
JsonObject(jsonMap)
}
is Animal.RobobirdWrapper -> {
val jsonMap = encoder.json.encodeToJsonElement(Robobird.serializer(), value.value).jsonObject.toMutableMap()
jsonMap["discriminator"] = JsonPrimitive("ROBOBIRD")
JsonObject(jsonMap)
}
}
encoder.encodeJsonElement(jsonObject)
}
override fun deserialize(decoder: Decoder): Animal {
require(decoder is JsonDecoder)
val element = decoder.decodeJsonElement().jsonObject
val discriminatorValue = element["discriminator"]?.jsonPrimitive?.content
?: throw SerializationException("Missing discriminator field")
return when (discriminatorValue) {
"BIRD" -> {
val decoded = decoder.json.decodeFromJsonElement(Bird.serializer(), element)
Animal.BirdWrapper(decoded)
}
"ROBOBIRD" -> {
val decoded = decoder.json.decodeFromJsonElement(Robobird.serializer(), element)
Animal.RobobirdWrapper(decoded)
}
else -> throw SerializationException("Unknown Animal discriminator: $discriminatorValue")
}
}
}

View File

@@ -0,0 +1,93 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import org.openapitools.client.models.Bird
import org.openapitools.client.models.Robobird
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
/**
*
*
*/
@Serializable(with = AnotherAnimalSerializer::class)
sealed interface AnotherAnimal {
@JvmInline
value class AnotherBirdWrapper(val value: Bird) : AnotherAnimal
@JvmInline
value class AnotherRobobirdWrapper(val value: Robobird) : AnotherAnimal
}
object AnotherAnimalSerializer : KSerializer<AnotherAnimal> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("AnotherAnimal")
override fun serialize(encoder: Encoder, value: AnotherAnimal) {
require(encoder is JsonEncoder)
val jsonObject = when (value) {
is AnotherAnimal.AnotherBirdWrapper -> {
val jsonMap = encoder.json.encodeToJsonElement(Bird.serializer(), value.value).jsonObject.toMutableMap()
jsonMap["another_discriminator"] = JsonPrimitive("ANOTHER_BIRD")
JsonObject(jsonMap)
}
is AnotherAnimal.AnotherRobobirdWrapper -> {
val jsonMap = encoder.json.encodeToJsonElement(Robobird.serializer(), value.value).jsonObject.toMutableMap()
jsonMap["another_discriminator"] = JsonPrimitive("ANOTHER_ROBOBIRD")
JsonObject(jsonMap)
}
}
encoder.encodeJsonElement(jsonObject)
}
override fun deserialize(decoder: Decoder): AnotherAnimal {
require(decoder is JsonDecoder)
val element = decoder.decodeJsonElement().jsonObject
val discriminatorValue = element["another_discriminator"]?.jsonPrimitive?.content
?: throw SerializationException("Missing another_discriminator field")
return when (discriminatorValue) {
"ANOTHER_BIRD" -> {
val decoded = decoder.json.decodeFromJsonElement(Bird.serializer(), element)
AnotherAnimal.AnotherBirdWrapper(decoded)
}
"ANOTHER_ROBOBIRD" -> {
val decoded = decoder.json.decodeFromJsonElement(Robobird.serializer(), element)
AnotherAnimal.AnotherRobobirdWrapper(decoded)
}
else -> throw SerializationException("Unknown AnotherAnimal another_discriminator: $discriminatorValue")
}
}
}

View File

@@ -0,0 +1,56 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
*
*
* @param discriminator
* @param anotherDiscriminator
* @param propertyA
* @param sameNameProperty
*/
@Serializable
data class Bird (
@SerialName(value = "discriminator")
val discriminator: kotlin.String,
@SerialName(value = "another_discriminator")
val anotherDiscriminator: kotlin.String,
@SerialName(value = "propertyA")
val propertyA: kotlin.String? = null,
@SerialName(value = "sameNameProperty")
val sameNameProperty: kotlin.Int? = null
) {
}

View File

@@ -0,0 +1,56 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
*
*
* @param discriminator
* @param anotherDiscriminator
* @param propertyB
* @param sameNameProperty
*/
@Serializable
data class Robobird (
@SerialName(value = "discriminator")
val discriminator: kotlin.String,
@SerialName(value = "another_discriminator")
val anotherDiscriminator: kotlin.String,
@SerialName(value = "propertyB")
val propertyB: kotlin.String? = null,
@SerialName(value = "sameNameProperty")
val sameNameProperty: kotlin.String? = null
) {
}

View File

@@ -0,0 +1,38 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.apis
import io.kotlintest.shouldBe
import io.kotlintest.specs.ShouldSpec
import org.openapitools.client.apis.BirdApi
import org.openapitools.client.models.Animal
class BirdApiTest : ShouldSpec() {
init {
// uncomment below to create an instance of BirdApi
//val apiInstance = BirdApi()
// to test getBird
should("test getBird") {
// uncomment below to test getBird
//val id : java.util.UUID = 38400000-8cf0-11bd-b23e-10b96e4ef00d // java.util.UUID |
//val result : Animal = apiInstance.getBird(id)
//result shouldBe ("TODO")
}
}
}

View File

@@ -0,0 +1,61 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import io.kotlintest.shouldBe
import io.kotlintest.specs.ShouldSpec
import org.openapitools.client.models.Animal
import org.openapitools.client.models.Bird
import org.openapitools.client.models.Robobird
class AnimalTest : ShouldSpec() {
init {
// uncomment below to create an instance of Animal
//val modelInstance = Animal()
// to test the property `discriminator`
should("test discriminator") {
// uncomment below to test the property
//modelInstance.discriminator shouldBe ("TODO")
}
// to test the property `anotherDiscriminator`
should("test anotherDiscriminator") {
// uncomment below to test the property
//modelInstance.anotherDiscriminator shouldBe ("TODO")
}
// to test the property `propertyA`
should("test propertyA") {
// uncomment below to test the property
//modelInstance.propertyA shouldBe ("TODO")
}
// to test the property `sameNameProperty`
should("test sameNameProperty") {
// uncomment below to test the property
//modelInstance.sameNameProperty shouldBe ("TODO")
}
// to test the property `propertyB`
should("test propertyB") {
// uncomment below to test the property
//modelInstance.propertyB shouldBe ("TODO")
}
}
}

View File

@@ -0,0 +1,61 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import io.kotlintest.shouldBe
import io.kotlintest.specs.ShouldSpec
import org.openapitools.client.models.AnotherAnimal
import org.openapitools.client.models.Bird
import org.openapitools.client.models.Robobird
class AnotherAnimalTest : ShouldSpec() {
init {
// uncomment below to create an instance of AnotherAnimal
//val modelInstance = AnotherAnimal()
// to test the property `discriminator`
should("test discriminator") {
// uncomment below to test the property
//modelInstance.discriminator shouldBe ("TODO")
}
// to test the property `anotherDiscriminator`
should("test anotherDiscriminator") {
// uncomment below to test the property
//modelInstance.anotherDiscriminator shouldBe ("TODO")
}
// to test the property `propertyA`
should("test propertyA") {
// uncomment below to test the property
//modelInstance.propertyA shouldBe ("TODO")
}
// to test the property `sameNameProperty`
should("test sameNameProperty") {
// uncomment below to test the property
//modelInstance.sameNameProperty shouldBe ("TODO")
}
// to test the property `propertyB`
should("test propertyB") {
// uncomment below to test the property
//modelInstance.propertyB shouldBe ("TODO")
}
}
}

View File

@@ -0,0 +1,53 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import io.kotlintest.shouldBe
import io.kotlintest.specs.ShouldSpec
import org.openapitools.client.models.Bird
class BirdTest : ShouldSpec() {
init {
// uncomment below to create an instance of Bird
//val modelInstance = Bird()
// to test the property `discriminator`
should("test discriminator") {
// uncomment below to test the property
//modelInstance.discriminator shouldBe ("TODO")
}
// to test the property `anotherDiscriminator`
should("test anotherDiscriminator") {
// uncomment below to test the property
//modelInstance.anotherDiscriminator shouldBe ("TODO")
}
// to test the property `propertyA`
should("test propertyA") {
// uncomment below to test the property
//modelInstance.propertyA shouldBe ("TODO")
}
// to test the property `sameNameProperty`
should("test sameNameProperty") {
// uncomment below to test the property
//modelInstance.sameNameProperty shouldBe ("TODO")
}
}
}

View File

@@ -0,0 +1,53 @@
/**
*
* Please note:
* This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* Do not edit this file manually.
*
*/
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package org.openapitools.client.models
import io.kotlintest.shouldBe
import io.kotlintest.specs.ShouldSpec
import org.openapitools.client.models.Robobird
class RobobirdTest : ShouldSpec() {
init {
// uncomment below to create an instance of Robobird
//val modelInstance = Robobird()
// to test the property `discriminator`
should("test discriminator") {
// uncomment below to test the property
//modelInstance.discriminator shouldBe ("TODO")
}
// to test the property `anotherDiscriminator`
should("test anotherDiscriminator") {
// uncomment below to test the property
//modelInstance.anotherDiscriminator shouldBe ("TODO")
}
// to test the property `propertyB`
should("test propertyB") {
// uncomment below to test the property
//modelInstance.propertyB shouldBe ("TODO")
}
// to test the property `sameNameProperty`
should("test sameNameProperty") {
// uncomment below to test the property
//modelInstance.sameNameProperty shouldBe ("TODO")
}
}
}

View File

@@ -35,8 +35,6 @@ import java.io.IOException
*
*
*/
data class ApiUserOrPet(var actualInstance: Any? = null) {
class CustomTypeAdapterFactory : TypeAdapterFactory {
@@ -155,3 +153,4 @@ data class ApiUserOrPet(var actualInstance: Any? = null) {
}
}
}

View File

@@ -35,8 +35,6 @@ import java.io.IOException
*
*
*/
data class ApiUserOrPetOrArrayString(var actualInstance: Any? = null) {
class CustomTypeAdapterFactory : TypeAdapterFactory {
@@ -205,3 +203,4 @@ data class ApiUserOrPetOrArrayString(var actualInstance: Any? = null) {
}
}
}