Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import kotlinx.parcelize.Parcelize
{{/multiplatform}}
{{#multiplatform}}
import kotlinx.serialization.*
import kotlinx.serialization.internal.CommonEnumSerializer
{{/multiplatform}}
{{#serializableModel}}
import java.io.Serializable
Expand Down Expand Up @@ -72,6 +71,9 @@ import java.io.Serializable
{{#kotlinx_serialization}}
{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}
{{/kotlinx_serialization}}
{{#multiplatform}}
@Serializable
{{/multiplatform}}
{{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{{nameInCamelCase}}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) {
{{#allowableValues}}
{{#enumVars}}
Expand All @@ -90,7 +92,7 @@ import java.io.Serializable
{{/kotlinx_serialization}}
{{/multiplatform}}
{{#multiplatform}}
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
@SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/multiplatform}}
{{/enumVars}}
{{/allowableValues}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import kotlinx.serialization.Serializable
{{/multiplatform}}
{{#multiplatform}}
import kotlinx.serialization.*
import kotlinx.serialization.internal.CommonEnumSerializer
{{/multiplatform}}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@ package {{packageName}}.infrastructure
* NOTE: Headers is a Map<String,String> because rfc2616 defines
* multi-valued headers as csv-only.
*/
{{^multiplatform}}
{{#nonPublicApi}}internal {{/nonPublicApi}}data class RequestConfig<T>(
{{/multiplatform}}
{{#multiplatform}}
{{! Migrating the 'multiplatform' template to RequestConfig<T> is considered a separate piece of effort. }}
{{#nonPublicApi}}internal {{/nonPublicApi}}data class RequestConfig(
{{/multiplatform}}
val method: RequestMethod,
val path: String,
val headers: MutableMap<String, String> = mutableMapOf(),
val query: MutableMap<String, List<String>> = mutableMapOf(),
{{^multiplatform}}
val body: T? = null
{{/multiplatform}}
{{#multiplatform}}
val body: kotlin.Any? = null
{{/multiplatform}}
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,26 @@ package {{apiPackage}}

import {{packageName}}.infrastructure.*
import io.ktor.client.request.forms.formData
import kotlinx.serialization.UnstableDefault
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*
import kotlinx.serialization.internal.StringDescriptor
import kotlinx.serialization.builtins.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

{{#operations}}
{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}} @UseExperimental(UnstableDefault::class) constructor(
{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}} constructor(
baseUrl: kotlin.String = "{{{basePath}}}",
httpClientEngine: HttpClientEngine? = null,
serializer: KotlinxSerializer
serializer: KotlinxSerializer = KotlinxSerializer(Json { ignoreUnknownKeys = true }),
) : ApiClient(baseUrl, httpClientEngine, serializer) {

@UseExperimental(UnstableDefault::class)
constructor(
baseUrl: kotlin.String = "{{{basePath}}}",
httpClientEngine: HttpClientEngine? = null,
jsonConfiguration: JsonConfiguration = JsonConfiguration.Default
) : this(baseUrl, httpClientEngine, KotlinxSerializer(Json(jsonConfiguration)))

{{#operation}}
/**
* {{summary}}
Expand Down Expand Up @@ -103,23 +100,5 @@ import kotlinx.serialization.internal.StringDescriptor
{{/isMap}}

{{/operation}}

{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
internal fun setMappers(serializer: KotlinxSerializer) {
{{#operation}}
{{#hasBodyParam}}
{{#bodyParam}}
{{#isArray}}serializer.setMapper({{operationIdCamelCase}}Request::class, {{operationIdCamelCase}}Request.serializer()){{/isArray}}{{#isMap}}serializer.setMapper({{operationIdCamelCase}}Request::class, {{operationIdCamelCase}}Request.serializer()){{/isMap}}
{{/bodyParam}}
{{/hasBodyParam}}
{{#isArray}}
serializer.setMapper({{operationIdCamelCase}}Response::class, {{operationIdCamelCase}}Response.serializer())
{{/isArray}}
{{#isMap}}
serializer.setMapper({{operationIdCamelCase}}Response::class, {{operationIdCamelCase}}Response.serializer())
{{/isMap}}
{{/operation}}
}
}
}
{{/operations}}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ group '{{groupId}}'
version '{{artifactVersion}}'

ext {
kotlin_version = '1.3.50'
kotlinx_version = '1.1.0'
coroutines_version = '1.3.1'
serialization_version = '0.12.0'
ktor_version = '1.2.4'
// Recommended library versions for the used version of Kotlin were taken from
// https://kotlinlang.org/docs/releases.html#release-details
kotlin_version = '1.5.10'
coroutines_version = '1.5.0'
serialization_version = '1.2.1'
ktor_version = '1.5.4'
}

buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50" // $kotlin_version
classpath "org.jetbrains.kotlin:kotlin-serialization:1.3.50" // $kotlin_version
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10" // $kotlin_version
classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.10" // $kotlin_version
}
}

Expand All @@ -28,16 +29,16 @@ repositories {

kotlin {
jvm()
iosArm64() { binaries { framework { freeCompilerArgs.add("-Xobjc-generics") } } }
iosX64() { binaries { framework { freeCompilerArgs.add("-Xobjc-generics") } } }
iosArm64() { binaries { framework { freeCompilerArgs += "-Xobjc-generics" } } }
iosX64() { binaries { framework { freeCompilerArgs += "-Xobjc-generics" } } }
js()

sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version"
api "io.ktor:ktor-client-core:$ktor_version"
api "io.ktor:ktor-client-json:$ktor_version"
api "io.ktor:ktor-client-serialization:$ktor_version"
Expand All @@ -57,7 +58,8 @@ kotlin {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version"
implementation "io.ktor:ktor-client-cio-jvm:$ktor_version"
api "io.ktor:ktor-client-core-jvm:$ktor_version"
api "io.ktor:ktor-client-json-jvm:$ktor_version"
api "io.ktor:ktor-client-serialization-jvm:$ktor_version"
Expand All @@ -77,7 +79,7 @@ kotlin {
dependsOn commonMain
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core-native:$serialization_version"
api "io.ktor:ktor-client-ios:$ktor_version"
}
}
Expand Down Expand Up @@ -116,7 +118,7 @@ kotlin {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core-js:$serialization_version"
api "io.ktor:ktor-client-js:$ktor_version"
api "io.ktor:ktor-client-json-js:$ktor_version"
api "io.ktor:ktor-client-serialization-js:$ktor_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@ import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.request.accept
import io.ktor.client.request.request
import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.request.forms.MultiPartFormDataContent
import io.ktor.client.request.header
import io.ktor.client.request.parameter
import io.ktor.client.response.HttpResponse
import io.ktor.client.statement.HttpResponse
import io.ktor.client.utils.EmptyContent
import io.ktor.http.*
import io.ktor.http.content.OutgoingContent
import io.ktor.http.content.PartData
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration

import {{apiPackage}}.*
import {{modelPackage}}.*
Expand All @@ -28,18 +25,7 @@ import {{packageName}}.auth.*
{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
serializer: KotlinxSerializer) {

@UseExperimental(UnstableDefault::class)
constructor(
baseUrl: String,
httpClientEngine: HttpClientEngine?,
jsonConfiguration: JsonConfiguration) :
this(baseUrl, httpClientEngine, KotlinxSerializer(Json(jsonConfiguration)))

private val serializer: JsonSerializer by lazy {
serializer.apply { setMappers(this) }.ignoreOutgoingContent()
}
private val serializer: KotlinxSerializer) {

private val client: HttpClient by lazy {
val jsonConfig: JsonFeature.Config.() -> Unit = { this.serializer = this@ApiClient.serializer }
Expand All @@ -61,15 +47,6 @@ import {{packageName}}.auth.*

{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)

private fun setMappers(serializer: KotlinxSerializer) {
{{#apiInfo}}{{#apis}}
{{classname}}.setMappers(serializer)
{{/apis}}{{/apiInfo}}
{{#models}}
{{#model}}{{^isAlias}}serializer.setMapper({{modelPackage}}.{{classname}}::class, {{modelPackage}}.{{classname}}.{{#isEnum}}Serializer{{/isEnum}}{{^isEnum}}serializer(){{/isEnum}}){{/isAlias}}{{/model}}
{{/models}}
}
}

/**
Expand Down Expand Up @@ -155,11 +132,11 @@ import {{packageName}}.auth.*
else request(requestConfig, authNames = authNames)
}

protected suspend inline fun <reified T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun request(requestConfig: RequestConfig, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth(authNames)
val headers = requestConfig.headers

return client.call {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
Expand All @@ -174,7 +151,7 @@ import {{packageName}}.auth.*
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body

}.response
}
}

private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List<String>) {
Expand All @@ -199,13 +176,3 @@ import {{packageName}}.auth.*
RequestMethod.OPTIONS -> HttpMethod.Options
}
}

// https://github.com/ktorio/ktor/issues/851
private fun JsonSerializer.ignoreOutgoingContent() = IgnoreOutgoingContentJsonSerializer(this)

private class IgnoreOutgoingContentJsonSerializer(private val delegate: JsonSerializer) : JsonSerializer by delegate {
override fun write(data: Any): OutgoingContent {
if (data is OutgoingContent) return data
return delegate.write(data)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package {{packageName}}.infrastructure

import kotlinx.serialization.*
import kotlinx.serialization.internal.StringDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable
class Base64ByteArray(val value: ByteArray) {
@Serializer(Base64ByteArray::class)
companion object : KSerializer<Base64ByteArray> {
override val descriptor = StringDescriptor.withName("Base64ByteArray")
override val descriptor = PrimitiveSerialDescriptor("Base64ByteArray", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, obj: Base64ByteArray) = encoder.encodeString(obj.value.encodeBase64())
override fun deserialize(decoder: Decoder) = Base64ByteArray(decoder.decodeString().decodeBase64Bytes())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package {{packageName}}.infrastructure

import kotlinx.io.core.*
import io.ktor.utils.io.core.*
import kotlin.experimental.and

private val digits = "0123456789abcdef".toCharArray()
Expand Down Expand Up @@ -32,7 +32,7 @@ internal fun hex(bytes: ByteArray): String {
result[resultIndex++] = digits[b and 0x0f]
}

return String(result)
return result.concatToString()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess

{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse<T : Any>(val response: io.ktor.client.response.HttpResponse, val provider: BodyProvider<T>) {
{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
val success: Boolean = response.status.isSuccess()
val headers: Map<String, List<String>> = response.headers.mapEntries()
Expand All @@ -22,29 +22,29 @@ import io.ktor.http.isSuccess
}

{{#nonPublicApi}}internal {{/nonPublicApi}}interface BodyProvider<T : Any> {
suspend fun body(response: io.ktor.client.response.HttpResponse): T
suspend fun <V : Any> typedBody(response: io.ktor.client.response.HttpResponse, type: TypeInfo): V
suspend fun body(response: io.ktor.client.statement.HttpResponse): T
suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V
}

{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.response.HttpResponse): T =
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T

@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.response.HttpResponse, type: TypeInfo): V =
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
}

{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {
override suspend fun body(response: io.ktor.client.response.HttpResponse): T =
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
block(provider.body(response))

override suspend fun <V : Any> typedBody(response: io.ktor.client.response.HttpResponse, type: TypeInfo): V =
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
provider.typedBody(response, type)
}

{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun <reified T : Any> io.ktor.client.response.HttpResponse.wrap(): HttpResponse<T> =
{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun <reified T : Any> io.ktor.client.statement.HttpResponse.wrap(): HttpResponse<T> =
HttpResponse(this, TypedBodyProvider(typeInfo<T>()))

{{#nonPublicApi}}internal {{/nonPublicApi}}fun <T : Any, V : Any> HttpResponse<T>.map(block: T.() -> V): HttpResponse<V> =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package {{packageName}}.infrastructure

import kotlinx.serialization.*
import kotlinx.serialization.internal.StringDescriptor
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Serializable
class OctetByteArray(val value: ByteArray) {
@Serializer(OctetByteArray::class)
companion object : KSerializer<OctetByteArray> {
override val descriptor = StringDescriptor.withName("OctetByteArray")
override val descriptor = PrimitiveSerialDescriptor("OctetByteArray", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, obj: OctetByteArray) = encoder.encodeString(hex(obj.value))
override fun deserialize(decoder: Decoder) = OctetByteArray(hex(decoder.decodeString()))
}
Expand Down
Loading