diff --git a/bin/kotlin-uppercase-enum.sh b/bin/kotlin-uppercase-enum.sh
new file mode 100755
index 00000000000..49f690283f4
--- /dev/null
+++ b/bin/kotlin-uppercase-enum.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+SCRIPT="$0"
+echo "# START SCRIPT: $SCRIPT"
+
+while [ -h "$SCRIPT" ] ; do
+ ls=$(ls -ld "$SCRIPT")
+ link=$(expr "$ls" : '.*-> \(.*\)$')
+ if expr "$link" : '/.*' > /dev/null; then
+ SCRIPT="$link"
+ else
+ SCRIPT=$(dirname "$SCRIPT")/"$link"
+ fi
+done
+
+if [ ! -d "${APP_DIR}" ]; then
+ APP_DIR=$(dirname "$SCRIPT")/..
+ APP_DIR=$(cd "${APP_DIR}"; pwd)
+fi
+
+executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
+
+if [ ! -f "$executable" ]
+then
+ mvn -B clean package
+fi
+
+# if you've executed sbt assembly previously it will use that instead.
+export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
+ags="generate -t modules/openapi-generator/src/main/resources/kotlin-client -i modules/openapi-generator/src/test/resources/3_0/issue-4062.yaml -g kotlin --artifact-id kotlin-uppercase-enum --additional-properties enumPropertyNaming=UPPERCASE -o samples/client/petstore/kotlin-uppercase-enum $@"
+
+java ${JAVA_OPTS} -jar ${executable} ${ags}
+
+cp CI/samples.ci/client/petstore/kotlin-uppercase-enum/pom.xml samples/client/petstore/kotlin-uppercase-enum/pom.xml
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java
index bed71307d4d..b0227feae16 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java
@@ -530,7 +530,7 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
modified = underscore(modified);
break;
case UPPERCASE:
- modified = modified.toUpperCase(Locale.ROOT);
+ modified = underscore(modified).toUpperCase(Locale.ROOT);
break;
}
diff --git a/modules/openapi-generator/src/test/resources/3_0/issue-4062.yaml b/modules/openapi-generator/src/test/resources/3_0/issue-4062.yaml
new file mode 100644
index 00000000000..2ad7665265c
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/issue-4062.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+servers:
+ - url: 'http://petstore.swagger.io/v2'
+info:
+ description: Test for issue 4062
+ version: 1.0.0
+ title: OpenAPI Petstore
+ license:
+ name: Apache-2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+ /enum:
+ get:
+ tags:
+ - enum
+ summary: Get enums
+ description: ''
+ operationId: getEnum
+ responses:
+ '200':
+ description: success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetEnum'
+components:
+ schemas:
+ PetEnum:
+ type: string
+ description: An enum with complex-ish naming
+ enum:
+ - myFirstValue
+ - MY_SECOND_VALUE
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator-ignore b/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator-ignore
new file mode 100644
index 00000000000..7484ee590a3
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator-ignore
@@ -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
diff --git a/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator/VERSION b/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator/VERSION
new file mode 100644
index 00000000000..c3a2c7076fa
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/.openapi-generator/VERSION
@@ -0,0 +1 @@
+4.2.0-SNAPSHOT
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/README.md b/samples/client/petstore/kotlin-uppercase-enum/README.md
new file mode 100644
index 00000000000..9d5f9e7e33e
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/README.md
@@ -0,0 +1,50 @@
+# org.openapitools.client - Kotlin client library for OpenAPI Petstore
+
+## Requires
+
+* Kotlin 1.3.41
+* Gradle 4.9
+
+## 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.
+
+
+## Documentation for API Endpoints
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+*EnumApi* | [**getEnum**](docs/EnumApi.md#getenum) | **GET** /enum | Get enums
+
+
+
+## Documentation for Models
+
+ - [org.openapitools.client.models.PetEnum](docs/PetEnum.md)
+
+
+
+## Documentation for Authorization
+
+All endpoints do not require authorization.
diff --git a/samples/client/petstore/kotlin-uppercase-enum/build.gradle b/samples/client/petstore/kotlin-uppercase-enum/build.gradle
new file mode 100644
index 00000000000..c09f7912cfe
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/build.gradle
@@ -0,0 +1,37 @@
+group 'org.openapitools'
+version '1.0.0'
+
+wrapper {
+ gradleVersion = '4.9'
+ distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
+}
+
+buildscript {
+ ext.kotlin_version = '1.3.41'
+
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+apply plugin: 'kotlin'
+
+repositories {
+ mavenCentral()
+}
+
+test {
+ useJUnitPlatform()
+}
+
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
+ compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
+ compile "com.squareup.moshi:moshi-kotlin:1.8.0"
+ compile "com.squareup.moshi:moshi-adapters:1.8.0"
+ compile "com.squareup.okhttp3:okhttp:4.0.1"
+ testImplementation "io.kotlintest:kotlintest-runner-junit5:3.1.0"
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/docs/EnumApi.md b/samples/client/petstore/kotlin-uppercase-enum/docs/EnumApi.md
new file mode 100644
index 00000000000..7eaa182a4a5
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/docs/EnumApi.md
@@ -0,0 +1,50 @@
+# EnumApi
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**getEnum**](EnumApi.md#getEnum) | **GET** /enum | Get enums
+
+
+
+# **getEnum**
+> PetEnum getEnum()
+
+Get enums
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiInstance = EnumApi()
+try {
+ val result : PetEnum = apiInstance.getEnum()
+ println(result)
+} catch (e: ClientException) {
+ println("4xx response calling EnumApi#getEnum")
+ e.printStackTrace()
+} catch (e: ServerException) {
+ println("5xx response calling EnumApi#getEnum")
+ e.printStackTrace()
+}
+```
+
+### Parameters
+This endpoint does not need any parameter.
+
+### Return type
+
+[**PetEnum**](PetEnum.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/json
+
diff --git a/samples/client/petstore/kotlin-uppercase-enum/docs/PetEnum.md b/samples/client/petstore/kotlin-uppercase-enum/docs/PetEnum.md
new file mode 100644
index 00000000000..d970c3dd862
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/docs/PetEnum.md
@@ -0,0 +1,12 @@
+
+# PetEnum
+
+## Enum
+
+
+ * `MY_FIRST_VALUE` (value: `"myFirstValue"`)
+
+ * `MY_SECOND_VALUE` (value: `"MY_SECOND_VALUE"`)
+
+
+
diff --git a/samples/client/petstore/kotlin-uppercase-enum/settings.gradle b/samples/client/petstore/kotlin-uppercase-enum/settings.gradle
new file mode 100644
index 00000000000..cc08df3bfdc
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/settings.gradle
@@ -0,0 +1,2 @@
+
+rootProject.name = 'kotlin-uppercase-enum'
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/apis/EnumApi.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/apis/EnumApi.kt
new file mode 100644
index 00000000000..dcd526a91c3
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/apis/EnumApi.kt
@@ -0,0 +1,60 @@
+/**
+* OpenAPI Petstore
+* Test for issue 4062
+*
+* The version of the OpenAPI document: 1.0.0
+*
+*
+* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+package org.openapitools.client.apis
+
+import org.openapitools.client.models.PetEnum
+
+import org.openapitools.client.infrastructure.ApiClient
+import org.openapitools.client.infrastructure.ClientException
+import org.openapitools.client.infrastructure.ClientError
+import org.openapitools.client.infrastructure.ServerException
+import org.openapitools.client.infrastructure.ServerError
+import org.openapitools.client.infrastructure.MultiValueMap
+import org.openapitools.client.infrastructure.RequestConfig
+import org.openapitools.client.infrastructure.RequestMethod
+import org.openapitools.client.infrastructure.ResponseType
+import org.openapitools.client.infrastructure.Success
+import org.openapitools.client.infrastructure.toMultiValue
+
+class EnumApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiClient(basePath) {
+
+ /**
+ * Get enums
+ *
+ * @return PetEnum
+ */
+ @Suppress("UNCHECKED_CAST")
+ fun getEnum() : PetEnum {
+ val localVariableBody: kotlin.Any? = null
+ val localVariableQuery: MultiValueMap = mapOf()
+ val localVariableHeaders: MutableMap = mutableMapOf()
+ val localVariableConfig = RequestConfig(
+ RequestMethod.GET,
+ "/enum",
+ query = localVariableQuery,
+ headers = localVariableHeaders
+ )
+ val response = request(
+ localVariableConfig,
+ localVariableBody
+ )
+
+ return when (response.responseType) {
+ ResponseType.Success -> (response as Success<*>).data as PetEnum
+ ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.")
+ ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.")
+ ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error")
+ ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error")
+ }
+ }
+
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiAbstractions.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiAbstractions.kt
new file mode 100644
index 00000000000..f97cb88d233
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiAbstractions.kt
@@ -0,0 +1,23 @@
+package org.openapitools.client.infrastructure
+
+typealias MultiValueMap = Map>
+
+fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) {
+ "csv" -> ","
+ "tsv" -> "\t"
+ "pipes" -> "|"
+ "ssv" -> " "
+ else -> ""
+}
+
+val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" }
+
+fun toMultiValue(items: Array, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter)
+ = toMultiValue(items.asIterable(), collectionFormat, map)
+
+fun toMultiValue(items: Iterable, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List {
+ return when(collectionFormat) {
+ "multi" -> items.map(map)
+ else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map))
+ }
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt
new file mode 100644
index 00000000000..4ba5be35baa
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt
@@ -0,0 +1,153 @@
+package org.openapitools.client.infrastructure
+
+import okhttp3.OkHttpClient
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+import okhttp3.FormBody
+import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
+import okhttp3.ResponseBody
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.Request
+import java.io.File
+
+open class ApiClient(val baseUrl: String) {
+ companion object {
+ protected const val ContentType = "Content-Type"
+ protected const val Accept = "Accept"
+ protected const val Authorization = "Authorization"
+ protected const val JsonMediaType = "application/json"
+ protected const val FormDataMediaType = "multipart/form-data"
+ protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded"
+ protected const val XmlMediaType = "application/xml"
+
+ val apiKey: MutableMap = mutableMapOf()
+ val apiKeyPrefix: MutableMap = mutableMapOf()
+ var username: String? = null
+ var password: String? = null
+ var accessToken: String? = null
+
+ @JvmStatic
+ val client: OkHttpClient by lazy {
+ builder.build()
+ }
+
+ @JvmStatic
+ val builder: OkHttpClient.Builder = OkHttpClient.Builder()
+ }
+
+ protected inline fun requestBody(content: T, mediaType: String = JsonMediaType): RequestBody =
+ when {
+ content is File -> content.asRequestBody(
+ mediaType.toMediaTypeOrNull()
+ )
+ mediaType == FormDataMediaType || mediaType == FormUrlEncMediaType -> {
+ FormBody.Builder().apply {
+ // content's type *must* be Map
+ @Suppress("UNCHECKED_CAST")
+ (content as Map).forEach { (key, value) ->
+ add(key, value)
+ }
+ }.build()
+ }
+ mediaType == JsonMediaType -> Serializer.moshi.adapter(T::class.java).toJson(content).toRequestBody(
+ mediaType.toMediaTypeOrNull()
+ )
+ mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.")
+ // TODO: this should be extended with other serializers
+ else -> throw UnsupportedOperationException("requestBody currently only supports JSON body and File body.")
+ }
+
+ protected inline fun responseBody(body: ResponseBody?, mediaType: String? = JsonMediaType): T? {
+ if(body == null) {
+ return null
+ }
+ val bodyContent = body.string()
+ if (bodyContent.isEmpty()) {
+ return null
+ }
+ return when(mediaType) {
+ JsonMediaType -> Serializer.moshi.adapter(T::class.java).fromJson(bodyContent)
+ else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.")
+ }
+ }
+
+
+ protected inline fun request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse {
+ val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.")
+
+ val url = httpUrl.newBuilder()
+ .addPathSegments(requestConfig.path.trimStart('/'))
+ .apply {
+ requestConfig.query.forEach { query ->
+ query.value.forEach { queryValue ->
+ addQueryParameter(query.key, queryValue)
+ }
+ }
+ }.build()
+
+ // take content-type/accept from spec or set to default (application/json) if not defined
+ if (requestConfig.headers[ContentType].isNullOrEmpty()) {
+ requestConfig.headers[ContentType] = JsonMediaType
+ }
+ if (requestConfig.headers[Accept].isNullOrEmpty()) {
+ requestConfig.headers[Accept] = JsonMediaType
+ }
+ val headers = requestConfig.headers
+
+ if(headers[ContentType] ?: "" == "") {
+ throw kotlin.IllegalStateException("Missing Content-Type header. This is required.")
+ }
+
+ if(headers[Accept] ?: "" == "") {
+ throw kotlin.IllegalStateException("Missing Accept header. This is required.")
+ }
+
+ // TODO: support multiple contentType options here.
+ val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase()
+
+ val request = when (requestConfig.method) {
+ RequestMethod.DELETE -> Request.Builder().url(url).delete()
+ RequestMethod.GET -> Request.Builder().url(url)
+ RequestMethod.HEAD -> Request.Builder().url(url).head()
+ RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(body, contentType))
+ RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(body, contentType))
+ RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body, contentType))
+ RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null)
+ }.apply {
+ headers.forEach { header -> addHeader(header.key, header.value) }
+ }.build()
+
+ val response = client.newCall(request).execute()
+ val accept = response.header(ContentType)?.substringBefore(";")?.toLowerCase()
+
+ // TODO: handle specific mapping types. e.g. Map>
+ when {
+ response.isRedirect -> return Redirection(
+ response.code,
+ response.headers.toMultimap()
+ )
+ response.isInformational -> return Informational(
+ response.message,
+ response.code,
+ response.headers.toMultimap()
+ )
+ response.isSuccessful -> return Success(
+ responseBody(response.body, accept),
+ response.code,
+ response.headers.toMultimap()
+ )
+ response.isClientError -> return ClientError(
+ response.body?.string(),
+ response.code,
+ response.headers.toMultimap()
+ )
+ else -> return ServerError(
+ null,
+ response.body?.string(),
+ response.code,
+ response.headers.toMultimap()
+ )
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiInfrastructureResponse.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiInfrastructureResponse.kt
new file mode 100644
index 00000000000..f1a8aecc914
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApiInfrastructureResponse.kt
@@ -0,0 +1,40 @@
+package org.openapitools.client.infrastructure
+
+enum class ResponseType {
+ Success, Informational, Redirection, ClientError, ServerError
+}
+
+abstract class ApiInfrastructureResponse(val responseType: ResponseType) {
+ abstract val statusCode: Int
+ abstract val headers: Map>
+}
+
+class Success(
+ val data: T,
+ override val statusCode: Int = -1,
+ override val headers: Map> = mapOf()
+): ApiInfrastructureResponse(ResponseType.Success)
+
+class Informational(
+ val statusText: String,
+ override val statusCode: Int = -1,
+ override val headers: Map> = mapOf()
+) : ApiInfrastructureResponse(ResponseType.Informational)
+
+class Redirection(
+ override val statusCode: Int = -1,
+ override val headers: Map> = mapOf()
+) : ApiInfrastructureResponse(ResponseType.Redirection)
+
+class ClientError(
+ val body: Any? = null,
+ override val statusCode: Int = -1,
+ override val headers: Map> = mapOf()
+) : ApiInfrastructureResponse(ResponseType.ClientError)
+
+class ServerError(
+ val message: String? = null,
+ val body: Any? = null,
+ override val statusCode: Int = -1,
+ override val headers: Map>
+): ApiInfrastructureResponse(ResponseType.ServerError)
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApplicationDelegates.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApplicationDelegates.kt
new file mode 100644
index 00000000000..dd34bd48b2c
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ApplicationDelegates.kt
@@ -0,0 +1,29 @@
+package org.openapitools.client.infrastructure
+
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+object ApplicationDelegates {
+ /**
+ * Provides a property delegate, allowing the property to be set once and only once.
+ *
+ * If unset (no default value), a get on the property will throw [IllegalStateException].
+ */
+ fun setOnce(defaultValue: T? = null) : ReadWriteProperty = SetOnce(defaultValue)
+
+ private class SetOnce(defaultValue: T? = null) : ReadWriteProperty {
+ private var isSet = false
+ private var value: T? = defaultValue
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T {
+ return value ?: throw IllegalStateException("${property.name} not initialized")
+ }
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = synchronized(this) {
+ if (!isSet) {
+ this.value = value
+ isSet = true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ByteArrayAdapter.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
new file mode 100644
index 00000000000..617ac3fe906
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
@@ -0,0 +1,12 @@
+package org.openapitools.client.infrastructure
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.ToJson
+
+class ByteArrayAdapter {
+ @ToJson
+ fun toJson(data: ByteArray): String = String(data)
+
+ @FromJson
+ fun fromJson(data: String): ByteArray = data.toByteArray()
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt
new file mode 100644
index 00000000000..2f3b0157ba7
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt
@@ -0,0 +1,42 @@
+@file:Suppress("unused")
+package org.openapitools.client.infrastructure
+
+import java.lang.RuntimeException
+
+open class ClientException : RuntimeException {
+
+ /**
+ * Constructs an [ClientException] with no detail message.
+ */
+ constructor() : super()
+
+ /**
+ * Constructs an [ClientException] with the specified detail message.
+
+ * @param message the detail message.
+ */
+ constructor(message: kotlin.String) : super(message)
+
+ companion object {
+ private const val serialVersionUID: Long = 123L
+ }
+}
+
+open class ServerException : RuntimeException {
+
+ /**
+ * Constructs an [ServerException] with no detail message.
+ */
+ constructor() : super()
+
+ /**
+ * Constructs an [ServerException] with the specified detail message.
+
+ * @param message the detail message.
+ */
+ constructor(message: kotlin.String) : super(message)
+
+ companion object {
+ private const val serialVersionUID: Long = 456L
+ }
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateAdapter.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateAdapter.kt
new file mode 100644
index 00000000000..b2e1654479a
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateAdapter.kt
@@ -0,0 +1,19 @@
+package org.openapitools.client.infrastructure
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.ToJson
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+class LocalDateAdapter {
+ @ToJson
+ fun toJson(value: LocalDate): String {
+ return DateTimeFormatter.ISO_LOCAL_DATE.format(value)
+ }
+
+ @FromJson
+ fun fromJson(value: String): LocalDate {
+ return LocalDate.parse(value, DateTimeFormatter.ISO_LOCAL_DATE)
+ }
+
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
new file mode 100644
index 00000000000..e082db94811
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
@@ -0,0 +1,19 @@
+package org.openapitools.client.infrastructure
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.ToJson
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+
+class LocalDateTimeAdapter {
+ @ToJson
+ fun toJson(value: LocalDateTime): String {
+ return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value)
+ }
+
+ @FromJson
+ fun fromJson(value: String): LocalDateTime {
+ return LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
+ }
+
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt
new file mode 100644
index 00000000000..53e689237d7
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt
@@ -0,0 +1,16 @@
+package org.openapitools.client.infrastructure
+
+/**
+ * Defines a config object for a given request.
+ * NOTE: This object doesn't include 'body' because it
+ * allows for caching of the constructed object
+ * for many request definitions.
+ * NOTE: Headers is a Map because rfc2616 defines
+ * multi-valued headers as csv-only.
+ */
+data class RequestConfig(
+ val method: RequestMethod,
+ val path: String,
+ val headers: MutableMap = mutableMapOf(),
+ val query: Map> = mapOf()
+)
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestMethod.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestMethod.kt
new file mode 100644
index 00000000000..931b12b8bd7
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/RequestMethod.kt
@@ -0,0 +1,8 @@
+package org.openapitools.client.infrastructure
+
+/**
+ * Provides enumerated HTTP verbs
+ */
+enum class RequestMethod {
+ GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ResponseExtensions.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ResponseExtensions.kt
new file mode 100644
index 00000000000..934962ec6b5
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/ResponseExtensions.kt
@@ -0,0 +1,23 @@
+package org.openapitools.client.infrastructure
+
+import okhttp3.Response
+
+/**
+ * Provides an extension to evaluation whether the response is a 1xx code
+ */
+val Response.isInformational : Boolean get() = this.code in 100..199
+
+/**
+ * Provides an extension to evaluation whether the response is a 3xx code
+ */
+val Response.isRedirect : Boolean get() = this.code in 300..399
+
+/**
+ * Provides an extension to evaluation whether the response is a 4xx code
+ */
+val Response.isClientError : Boolean get() = this.code in 400..499
+
+/**
+ * Provides an extension to evaluation whether the response is a 5xx (Standard) through 999 (non-standard) code
+ */
+val Response.isServerError : Boolean get() = this.code in 500..999
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Serializer.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Serializer.kt
new file mode 100644
index 00000000000..7c5a353e0f7
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/Serializer.kt
@@ -0,0 +1,18 @@
+package org.openapitools.client.infrastructure
+
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import java.util.Date
+
+object Serializer {
+ @JvmStatic
+ val moshi: Moshi = Moshi.Builder()
+ .add(Date::class.java, Rfc3339DateJsonAdapter().nullSafe())
+ .add(LocalDateTimeAdapter())
+ .add(LocalDateAdapter())
+ .add(UUIDAdapter())
+ .add(ByteArrayAdapter())
+ .add(KotlinJsonAdapterFactory())
+ .build()
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/UUIDAdapter.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/UUIDAdapter.kt
new file mode 100644
index 00000000000..a4a44cc18b7
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/infrastructure/UUIDAdapter.kt
@@ -0,0 +1,13 @@
+package org.openapitools.client.infrastructure
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.ToJson
+import java.util.UUID
+
+class UUIDAdapter {
+ @ToJson
+ fun toJson(uuid: UUID) = uuid.toString()
+
+ @FromJson
+ fun fromJson(s: String) = UUID.fromString(s)
+}
diff --git a/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/models/PetEnum.kt b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/models/PetEnum.kt
new file mode 100644
index 00000000000..14d9c16c408
--- /dev/null
+++ b/samples/client/petstore/kotlin-uppercase-enum/src/main/kotlin/org/openapitools/client/models/PetEnum.kt
@@ -0,0 +1,35 @@
+/**
+* OpenAPI Petstore
+* Test for issue 4062
+*
+* The version of the OpenAPI document: 1.0.0
+*
+*
+* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+package org.openapitools.client.models
+
+
+import com.squareup.moshi.Json
+
+/**
+* An enum with complex-ish naming
+* Values: MY_FIRST_VALUE,MY_SECOND_VALUE
+*/
+
+enum class PetEnum(val value: kotlin.String){
+
+
+ @Json(name = "myFirstValue")
+ MY_FIRST_VALUE("myFirstValue"),
+
+
+ @Json(name = "MY_SECOND_VALUE")
+ MY_SECOND_VALUE("MY_SECOND_VALUE");
+
+
+
+}
+