Skip to content

Commit

Permalink
Merge pull request swagger-api#517 from crazyk2/local_date_issues
Browse files Browse the repository at this point in the history
Local date issues
  • Loading branch information
HugoMario authored Jan 30, 2020
2 parents 773aade + 48a2542 commit 2b2f992
Show file tree
Hide file tree
Showing 16 changed files with 137 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ public AbstractKotlinCodegen() {
typeMapping.put("double", "kotlin.Double");
typeMapping.put("number", "java.math.BigDecimal");
typeMapping.put("date-time", "java.time.LocalDateTime");
typeMapping.put("date", "java.time.LocalDateTime");
typeMapping.put("date", "java.time.LocalDate");
typeMapping.put("file", "java.io.File");
typeMapping.put("array", "kotlin.Array");
typeMapping.put("list", "kotlin.Array");
typeMapping.put("map", "kotlin.collections.Map");
typeMapping.put("object", "kotlin.Any");
typeMapping.put("binary", "kotlin.Array<kotlin.Byte>");
typeMapping.put("Date", "java.time.LocalDateTime");
typeMapping.put("Date", "java.time.LocalDate");
typeMapping.put("DateTime", "java.time.LocalDateTime");

instantiationTypes.put("array", "arrayOf");
Expand Down Expand Up @@ -498,11 +498,13 @@ public String toModelName(final String name) {
String modifiedName = name.replaceAll("\\.", "");
modifiedName = sanitizeKotlinSpecificNames(modifiedName);

if (isReservedWord(modifiedName)) {
modifiedName = escapeReservedWord(modifiedName);
modifiedName = titleCase(modifiedName);

if (modifiedName.equalsIgnoreCase("Companion")) {
modifiedName = "_" + modifiedName;
}

return titleCase(modifiedName);
return modifiedName;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,8 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("infrastructure/ResponseExtensions.kt.mustache", infrastructureFolder, "ResponseExtensions.kt"));
supportingFiles.add(new SupportingFile("infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt"));
supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt"));
supportingFiles.add(new SupportingFile("infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt"));
supportingFiles.add(new SupportingFile("infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt"));

}
}
2 changes: 0 additions & 2 deletions src/main/resources/handlebars/kotlin-client/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ class {{classname}}(basePath: kotlin.String = "{{{basePath}}}") : ApiClient(base
ResponseType.Redirection -> TODO()
ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error")
ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error")
else -> throw kotlin.IllegalStateException("Undefined ResponseType.")
}
}

{{/contents}}
{{/operation}}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ task wrapper(type: Wrapper) {
}

buildscript {
ext.kotlin_version = '1.1.2'
ext.kotlin_version = '1.3.50'
repositories {
mavenCentral()
Expand All @@ -25,9 +25,9 @@ repositories {

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "com.squareup.moshi:moshi-kotlin:1.5.0"
compile "com.squareup.moshi:moshi-adapters:1.5.0"
compile "com.squareup.okhttp3:okhttp:3.8.0"
compile "org.threeten:threetenbp:1.3.6"
testCompile "io.kotlintest:kotlintest:2.0.2"
}
compile "com.squareup.moshi:moshi-kotlin:1.8.0"
compile "com.squareup.moshi:moshi-adapters:1.8.0"
compile "com.squareup.okhttp3:okhttp:3.14.2"
compile "org.threeten:threetenbp:1.4.0"
testCompile "io.kotlintest:kotlintest:2.0.7"
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^@last}},{{/@last}}{{/enumVars}}{{/allowableValues}}
*/
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^@last}},{{/@last}}{{/enumVars}}{{/allowableValues}}
*/
enum class {{classname}}(val value: {{dataType}}){
{{#allowableValues}}{{#enumVars}}
{{&name}}({{{value}}}){{^@last}},{{/@last}}{{#@last}};{{/@last}}
{{/enumVars}}{{/allowableValues}}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package {{packageName}}.infrastructure

typealias MultiValueMap = Map<String,List<String>>
typealias MultiValueMap = Map<String, List<String>>

fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) {
fun collectionDelimiter(collectionFormat: String) = when (collectionFormat) {
"csv" -> ","
"tsv" -> "\t"
"pipes" -> "|"
Expand All @@ -12,8 +12,8 @@ fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) {

val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" }

fun <T: Any?> toMultiValue(items: List<T>, collectionFormat: String, map: (item: Any?) -> String = defaultMultiValueConverter): List<String> {
return when(collectionFormat) {
fun <T : Any?> toMultiValue(items: List<T>, collectionFormat: String, map: (item: Any?) -> String = defaultMultiValueConverter): List<String> {
return when (collectionFormat) {
"multi" -> items.map(map)
else -> listOf(items.map(map).joinToString(separator = collectionDelimiter(collectionFormat)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import java.io.File

open class ApiClient(val baseUrl: String) {
companion object {
protected val ContentType = "Content-Type"
protected val Accept = "Accept"
protected val JsonMediaType = "application/json"
protected val FormDataMediaType = "multipart/form-data"
protected val XmlMediaType = "application/xml"
protected const val ContentType = "Content-Type"
protected const val Accept = "Accept"
protected const val JsonMediaType = "application/json"
protected const val FormDataMediaType = "multipart/form-data"
protected const val XmlMediaType = "application/xml"
@JvmStatic
val client : OkHttpClient = OkHttpClient()
val client: OkHttpClient = OkHttpClient()
@JvmStatic
var defaultHeaders: Map<String, String> by ApplicationDelegates.setOnce(mapOf(ContentType to JsonMediaType, Accept to JsonMediaType))
Expand All @@ -21,40 +21,37 @@ open class ApiClient(val baseUrl: String) {
val jsonHeaders: Map<String, String> = mapOf(ContentType to JsonMediaType, Accept to JsonMediaType)
}

inline protected fun <reified T> requestBody(content: T, mediaType: String = JsonMediaType): RequestBody {
if(content is File) {
return RequestBody.create(
MediaType.parse(mediaType), content
)
} else if(mediaType == FormDataMediaType) {
var builder = FormBody.Builder()
// content's type *must* be Map<String, Any>
@Suppress("UNCHECKED_CAST")
(content as Map<String,String>).forEach { key, value ->
builder = builder.add(key, value)
protected inline fun <reified T> requestBody(content: T, mediaType: String = JsonMediaType): RequestBody =
when {
content is File -> RequestBody.create(MediaType.parse(mediaType), content)
mediaType == FormDataMediaType -> {
var builder = FormBody.Builder()
// content's type *must* be Map<String, Any>
@Suppress("UNCHECKED_CAST")
(content as Map<String, String>).forEach { key, value ->
builder = builder.add(key, value)
}
builder.build()
}
mediaType == JsonMediaType -> RequestBody.create(
MediaType.parse(mediaType), Serializer.moshi.adapter(T::class.java).toJson(content)
)
mediaType == XmlMediaType -> TODO("xml not currently supported.")
// TODO: this should be extended with other serializers
else -> TODO("requestBody currently only supports JSON body and File body.")
}
return builder.build()
} else if(mediaType == JsonMediaType) {
return RequestBody.create(
MediaType.parse(mediaType), Serializer.moshi.adapter(T::class.java).toJson(content)
)
} else if (mediaType == XmlMediaType) {
TODO("xml not currently supported.")
}
// TODO: this should be extended with other serializers
TODO("requestBody currently only supports JSON body and File body.")
}
inline protected fun <reified T: Any?> responseBody(body: ResponseBody?, mediaType: String = JsonMediaType): T? {
if(body == null) return null
return when(mediaType) {
protected inline fun <reified T : Any?> responseBody(body: ResponseBody?, mediaType: String = JsonMediaType): T? {
if (body == null) return null
return when (mediaType) {
JsonMediaType -> Serializer.moshi.adapter(T::class.java).fromJson(body.source())
else -> TODO()
}
}
inline protected fun <reified T: Any?> request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse<T?> {
protected inline fun <reified T : Any?> request(requestConfig: RequestConfig, body: Any? = null): ApiInfrastructureResponse<T?> {
val httpUrl = HttpUrl.parse(baseUrl) ?: throw IllegalStateException("baseUrl is invalid.")
var urlBuilder = httpUrl.newBuilder()
Expand All @@ -69,19 +66,19 @@ open class ApiClient(val baseUrl: String) {
val url = urlBuilder.build()
val headers = requestConfig.headers + defaultHeaders
if(headers[ContentType] ?: "" == "") {
if (headers[ContentType] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Content-Type header. This is required.")
}
if(headers[Accept] ?: "" == "") {
if (headers[Accept] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Accept header. This is required.")
}
// TODO: support multiple contentType,accept options here.
val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase()
val accept = (headers[Accept] as String).substringBefore(";").toLowerCase()
var request : Request.Builder = when (requestConfig.method) {
var request: Request.Builder = when (requestConfig.method) {
RequestMethod.DELETE -> Request.Builder().url(url).delete()
RequestMethod.GET -> Request.Builder().url(url)
RequestMethod.HEAD -> Request.Builder().url(url).head()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ enum class ResponseType {

abstract class ApiInfrastructureResponse<T>(val responseType: ResponseType) {
abstract val statusCode: Int
abstract val headers: Map<String,List<String>>
abstract val headers: Map<String, List<String>>
}

class Success<T>(
val data: T,
override val statusCode: Int = -1,
override val headers: Map<String, List<String>> = mapOf()
): ApiInfrastructureResponse<T>(ResponseType.Success)
) : ApiInfrastructureResponse<T>(ResponseType.Success)

class Informational<T>(
val statusText: String,
Expand All @@ -37,4 +37,4 @@ class ServerError<T>(
val body: Any? = null,
override val statusCode: Int = -1,
override val headers: Map<String, List<String>>
): ApiInfrastructureResponse<T>(ResponseType.ServerError)
) : ApiInfrastructureResponse<T>(ResponseType.ServerError)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object ApplicationDelegates {
*
* If unset (no default value), a get on the property will throw [IllegalStateException].
*/
fun <T> setOnce(defaultValue: T? = null) : ReadWriteProperty<Any?, T> = SetOnce(defaultValue)
fun <T> setOnce(defaultValue: T? = null): ReadWriteProperty<Any?, T> = SetOnce(defaultValue)
private class SetOnce<T>(defaultValue: T? = null) : ReadWriteProperty<Any?, T> {
private var isSet = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package {{packageName}}.infrastructure

import com.squareup.moshi.FromJson
import com.squareup.moshi.ToJson
{{^threetenbp}}
import java.time.LocalDate
import java.time.format.DateTimeFormatter
{{/threetenbp}}
{{#threetenbp}}
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
{{/threetenbp}}

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_TIME)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package {{packageName}}.infrastructure

import com.squareup.moshi.FromJson
import com.squareup.moshi.ToJson
{{^threetenbp}}
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
{{/threetenbp}}
{{#threetenbp}}
import org.threeten.bp.LocalDateTime
import org.threeten.bp.format.DateTimeFormatter
{{/threetenbp}}

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)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ data class RequestConfig(
val method: RequestMethod,
val path: String,
val headers: Map<String, String> = mapOf(),
val query: Map<String, List<String>> = mapOf())
val query: Map<String, List<String>> = mapOf()
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ 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
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
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
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
val Response.isServerError: Boolean get() = this.code() in 500..999
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package {{packageName}}.infrastructure

import com.squareup.moshi.KotlinJsonAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.Rfc3339DateJsonAdapter
import java.util.*
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(KotlinJsonAdapterFactory())
.add(Date::class.java, Rfc3339DateJsonAdapter().nullSafe())
.add(LocalDateTimeAdapter())
.add(LocalDateAdapter())
.build()
}
}
20 changes: 10 additions & 10 deletions src/main/resources/handlebars/kotlin-client/licenseInfo.mustache
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}OpenAPI spec version: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}OpenAPI spec version: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
Loading

0 comments on commit 2b2f992

Please sign in to comment.