Feature/update kotlin dependecies template (#12966)

* update Kotlin & Ktor versions

* update samples

* fix gradle version

* update the ktor client templates and project samples

* update the ktor client templates and project samples

* revert multiplatform upgrade

* update kotlin multiplatform

* upload kotlin Multiplatform samples

* fix gson ktor sample

* update samples

* fix: unused imports

* fix imports of ApiClient

* update kotlin samples
This commit is contained in:
mm's 2022-09-04 17:27:24 +02:00 committed by GitHub
parent f6be1d07bc
commit ef6d383433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 466 additions and 587 deletions

View File

@ -3,15 +3,15 @@ version '{{artifactVersion}}'
{{^omitGradleWrapper}}
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
{{/omitGradleWrapper}}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
{{#jvm-ktor}}
ext.ktor_version = '1.6.7'
ext.ktor_version = '2.0.3'
{{/jvm-ktor}}
{{#jvm-retrofit2}}
ext.retrofitVersion = '2.9.0'
@ -72,46 +72,49 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
{{^doNotUseRxAndCoroutines}}
{{#useCoroutines}}
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3"
{{/useCoroutines}}
{{/doNotUseRxAndCoroutines}}
{{#moshi}}
{{^moshiCodeGen}}
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
{{/moshiCodeGen}}
{{#moshiCodeGen}}
implementation "com.squareup.moshi:moshi:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.12.0"
implementation "com.squareup.moshi:moshi:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
{{/moshiCodeGen}}
{{/moshi}}
{{#gson}}
implementation "com.google.code.gson:gson:2.8.7"
implementation "com.google.code.gson:gson:2.9.0"
{{/gson}}
{{#jackson}}
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3"
{{/jackson}}
{{#kotlinx_serialization}}
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
{{/kotlinx_serialization}}
{{#jvm-ktor}}
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
{{#gson}}
implementation "io.ktor:ktor-serialization-gson:$ktor_version"
implementation "io.ktor:ktor-client-gson:$ktor_version"
{{/gson}}
{{#jackson}}
implementation "io.ktor:ktor-client-jackson:$ktor_version"
implementation "io.ktor:ktor-serialization-jackson:$ktor_version"
{{/jackson}}
{{/jvm-ktor}}
{{#jvm-okhttp3}}
implementation "com.squareup.okhttp3:okhttp:3.12.13"
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
{{/jvm-okhttp4}}
{{#threetenbp}}
implementation "org.threeten:threetenbp:1.5.1"
@ -120,7 +123,7 @@ dependencies {
{{#hasOAuthMethods}}
implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2"
{{/hasOAuthMethods}}
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
{{#useRxJava}}
implementation "io.reactivex:rxjava:$rxJavaVersion"
implementation "com.squareup.retrofit2:adapter-rxjava:$retrofitVersion"

View File

@ -9,9 +9,10 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
{{#gson}}
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.text.DateFormat
{{/gson}}
{{#jackson}}
import com.fasterxml.jackson.databind.ObjectMapper
@ -22,8 +23,13 @@ import com.fasterxml.jackson.databind.ObjectMapper
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: {{#gson}}Gson{{/gson}}{{#jackson}}ObjectMapper{{/jackson}} = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
{{#gson}}
jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT,
{{/gson}}
{{#jackson}}
jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT,
{{/jackson}}
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
{{#operation}}
/**

View File

@ -1,50 +1,68 @@
package {{packageName}}.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.request.*
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
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.request.request
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.utils.EmptyContent
import io.ktor.http.*
import io.ktor.http.content.ByteArrayContent
import io.ktor.http.content.OutgoingContent
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import io.ktor.http.URLBuilder
import io.ktor.http.content.PartData
import kotlin.Unit
import io.ktor.http.encodeURLQueryComponent
import io.ktor.http.encodedPath
import io.ktor.http.takeFrom
{{#gson}}
import com.google.gson.Gson
import java.nio.charset.StandardCharsets
import io.ktor.serialization.gson.*
import com.google.gson.GsonBuilder
import java.text.DateFormat
{{/gson}}
{{#jackson}}
import io.ktor.serialization.jackson.*
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
{{/jackson}}
import org.openapitools.client.auth.ApiKeyAuth
import org.openapitools.client.auth.Authentication
import org.openapitools.client.auth.HttpBasicAuth
import org.openapitools.client.auth.HttpBearerAuth
import org.openapitools.client.auth.OAuth
import {{packageName}}.auth.*
{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: {{#gson}}Gson{{/gson}}{{#jackson}}ObjectMapper{{/jackson}},
{{#gson}}
jsonBlock: GsonBuilder.() -> Unit = JSON_DEFAULT,
{{/gson}}
{{#jackson}}
jsonBlock: ObjectMapper.() -> Unit = JSON_DEFAULT,
{{/jackson}}
) {
private val serializer: JsonSerializer by lazy {
JsonSerializerImpl(json)
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) {
{{#gson}}
gson { jsonBlock() }
{{/gson}}
{{#jackson}}
jackson { jsonBlock() }
{{/jackson}}
}
httpClientConfig?.invoke(it)
}
}
@ -67,9 +85,24 @@ import {{packageName}}.auth.*
{{/hasAuthMethods}}
{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
const val BASE_URL = "{{{basePath}}}"
val JSON_DEFAULT = {{#gson}}Gson(){{/gson}}{{#jackson}}ObjectMapper(){{/jackson}}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
const val BASE_URL = "{{{basePath}}}"
{{#gson}}
val JSON_DEFAULT : GsonBuilder.() -> Unit = {
setDateFormat(DateFormat.LONG)
setPrettyPrinting()
}
{{/gson}}
{{#jackson}}
val JSON_DEFAULT: ObjectMapper.() -> Unit = {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
registerModule(JavaTimeModule())
}
{{/jackson}}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
/**
@ -78,7 +111,7 @@ import {{packageName}}.auth.*
* @param username Username
*/
fun setUsername(username: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.username = username
}
@ -89,7 +122,7 @@ import {{packageName}}.auth.*
* @param password Password
*/
fun setPassword(password: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.password = password
}
@ -101,7 +134,7 @@ import {{packageName}}.auth.*
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKey(apiKey: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKey = apiKey
}
@ -113,7 +146,7 @@ import {{packageName}}.auth.*
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKeyPrefix = apiKeyPrefix
}
@ -124,7 +157,7 @@ import {{packageName}}.auth.*
* @param accessToken Access token
*/
fun setAccessToken(accessToken: String) {
val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth?
val auth = authentications.values.firstOrNull { it is OAuth } as OAuth?
?: throw Exception("No OAuth2 authentication configured")
auth.accessToken = accessToken
}
@ -135,7 +168,7 @@ import {{packageName}}.auth.*
* @param bearerToken The bearer token.
*/
fun setBearerToken(bearerToken: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
val auth = authentications.values.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
?: throw Exception("No Bearer authentication configured")
auth.bearerToken = bearerToken
}
@ -148,18 +181,13 @@ import {{packageName}}.auth.*
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -172,8 +200,7 @@ import {{packageName}}.auth.*
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
setBody(body)
}
}
@ -199,13 +226,3 @@ import {{packageName}}.auth.*
RequestMethod.OPTIONS -> HttpMethod.Options
}
}
{{#gson}}private class JsonSerializerImpl(private val gson: Gson) : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent =
ByteArrayContent(gson.toJson(data).toByteArray(StandardCharsets.UTF_8), contentType)
}{{/gson}}
{{#jackson}}private class JsonSerializerImpl(private val objectMapper: ObjectMapper) : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent =
ByteArrayContent(objectMapper.writeValueAsBytes(data), contentType)
}{{/jackson}}

View File

@ -1,9 +1,9 @@
package {{packageName}}.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
{{#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
@ -29,11 +29,11 @@ import io.ktor.http.isSuccess
{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -8,7 +8,6 @@ import {{packageName}}.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -8,10 +8,10 @@ plugins {
group = "{{groupId}}"
version = "{{artifactVersion}}"
val kotlin_version = "1.6.0"
val coroutines_version = "1.5.2"
val serialization_version = "1.3.0"
val ktor_version = "1.6.4"
val kotlin_version = "1.6.10"
val coroutines_version = "1.6.3"
val serialization_version = "1.3.3"
val ktor_version = "2.0.3"
repositories {
mavenCentral()
@ -30,9 +30,11 @@ kotlin {
dependencies {
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")
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}
}

View File

@ -3,43 +3,31 @@ package {{packageName}}.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
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.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.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.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 kotlin.Unit
import kotlinx.serialization.json.Json
import {{apiPackage}}.*
import {{modelPackage}}.*
import {{packageName}}.auth.*
{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
private val json: Json
private val jsonBlock: Json
) {
private val serializer: JsonSerializer by lazy {
KotlinxSerializer(json).ignoreOutgoingContent()
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) { json(jsonBlock) }
httpClientConfig?.invoke(it)
}
}
@ -63,7 +51,11 @@ import {{packageName}}.auth.*
{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
const val BASE_URL = "{{{basePath}}}"
val JSON_DEFAULT = Json { ignoreUnknownKeys = true }
val JSON_DEFAULT = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
@ -143,18 +135,13 @@ import {{packageName}}.auth.*
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -167,7 +154,7 @@ import {{packageName}}.auth.*
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
this.setBody(body)
}
}
@ -194,13 +181,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)
}
}

View File

@ -14,7 +14,7 @@ private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it
private fun Int.toBase64(): Char = BASE64_ALPHABET[this]
private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK
internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeStringUtf8(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { dropLastWhile { it == BASE64_PAD } }.decodeBase64Bytes().readBytes()
/**
* Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes.

View File

@ -1,9 +1,9 @@
package {{packageName}}.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
{{#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
@ -29,11 +29,11 @@ import io.ktor.http.isSuccess
{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:3.12.13"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -8,10 +8,10 @@ plugins {
group = "org.openapitools"
version = "1.0.0"
val kotlin_version = "1.6.0"
val coroutines_version = "1.5.2"
val serialization_version = "1.3.0"
val ktor_version = "1.6.4"
val kotlin_version = "1.6.10"
val coroutines_version = "1.6.3"
val serialization_version = "1.3.3"
val ktor_version = "2.0.3"
repositories {
mavenCentral()
@ -30,9 +30,11 @@ kotlin {
dependencies {
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")
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}
}

View File

@ -25,7 +25,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -3,43 +3,31 @@ package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
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.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.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.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 kotlin.Unit
import kotlinx.serialization.json.Json
import org.openapitools.client.apis.*
import org.openapitools.client.models.*
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
private val json: Json
private val jsonBlock: Json
) {
private val serializer: JsonSerializer by lazy {
KotlinxSerializer(json).ignoreOutgoingContent()
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) { json(jsonBlock) }
httpClientConfig?.invoke(it)
}
}
@ -52,7 +40,11 @@ open class ApiClient(
companion object {
const val BASE_URL = "http://localhost"
val JSON_DEFAULT = Json { ignoreUnknownKeys = true }
val JSON_DEFAULT = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
@ -132,18 +124,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -156,7 +143,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
this.setBody(body)
}
}
@ -183,13 +170,3 @@ open class ApiClient(
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)
}
}

View File

@ -14,7 +14,7 @@ private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it
private fun Int.toBase64(): Char = BASE64_ALPHABET[this]
private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK
internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeStringUtf8(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { dropLastWhile { it == BASE64_PAD } }.decodeBase64Bytes().readBytes()
/**
* Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes.

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -8,10 +8,10 @@ plugins {
group = "org.openapitools"
version = "1.0.0"
val kotlin_version = "1.6.0"
val coroutines_version = "1.5.2"
val serialization_version = "1.3.0"
val ktor_version = "1.6.4"
val kotlin_version = "1.6.10"
val coroutines_version = "1.6.3"
val serialization_version = "1.3.3"
val ktor_version = "2.0.3"
repositories {
mavenCentral()
@ -30,9 +30,11 @@ kotlin {
dependencies {
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")
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}
}

View File

@ -26,7 +26,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -3,43 +3,31 @@ package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
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.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.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.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 kotlin.Unit
import kotlinx.serialization.json.Json
import org.openapitools.client.apis.*
import org.openapitools.client.models.*
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
private val json: Json
private val jsonBlock: Json
) {
private val serializer: JsonSerializer by lazy {
KotlinxSerializer(json).ignoreOutgoingContent()
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) { json(jsonBlock) }
httpClientConfig?.invoke(it)
}
}
@ -52,7 +40,11 @@ open class ApiClient(
companion object {
const val BASE_URL = "http://localhost"
val JSON_DEFAULT = Json { ignoreUnknownKeys = true }
val JSON_DEFAULT = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
@ -132,18 +124,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -156,7 +143,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
this.setBody(body)
}
}
@ -183,13 +170,3 @@ open class ApiClient(
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)
}
}

View File

@ -14,7 +14,7 @@ private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it
private fun Int.toBase64(): Char = BASE64_ALPHABET[this]
private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK
internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeStringUtf8(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { dropLastWhile { it == BASE64_PAD } }.decodeBase64Bytes().readBytes()
/**
* Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes.

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:3.12.13"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
ext.retrofitVersion = '2.9.0'
repositories {
@ -31,9 +31,9 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"

View File

@ -8,10 +8,10 @@ plugins {
group = "org.openapitools"
version = "1.0.0"
val kotlin_version = "1.6.0"
val coroutines_version = "1.5.2"
val serialization_version = "1.3.0"
val ktor_version = "1.6.4"
val kotlin_version = "1.6.10"
val coroutines_version = "1.6.3"
val serialization_version = "1.3.3"
val ktor_version = "2.0.3"
repositories {
mavenCentral()
@ -30,9 +30,11 @@ kotlin {
dependencies {
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")
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}
}

View File

@ -25,7 +25,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -3,43 +3,31 @@ package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
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.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.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.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 kotlin.Unit
import kotlinx.serialization.json.Json
import org.openapitools.client.apis.*
import org.openapitools.client.models.*
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
private val json: Json
private val jsonBlock: Json
) {
private val serializer: JsonSerializer by lazy {
KotlinxSerializer(json).ignoreOutgoingContent()
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) { json(jsonBlock) }
httpClientConfig?.invoke(it)
}
}
@ -52,7 +40,11 @@ open class ApiClient(
companion object {
const val BASE_URL = "http://localhost"
val JSON_DEFAULT = Json { ignoreUnknownKeys = true }
val JSON_DEFAULT = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
@ -132,18 +124,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -156,7 +143,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
this.setBody(body)
}
}
@ -183,13 +170,3 @@ open class ApiClient(
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)
}
}

View File

@ -14,7 +14,7 @@ private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it
private fun Int.toBase64(): Char = BASE64_ALPHABET[this]
private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK
internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeStringUtf8(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { dropLastWhile { it == BASE64_PAD } }.decodeBase64Bytes().readBytes()
/**
* Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes.

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -29,7 +29,7 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.google.code.gson:gson:2.8.7"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.google.code.gson:gson:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -32,8 +32,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,13 +2,13 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.ktor_version = '1.6.7'
ext.kotlin_version = '1.6.10'
ext.ktor_version = '2.0.3'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,10 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.google.code.gson:gson:2.8.7"
implementation "com.google.code.gson:gson:2.9.0"
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
implementation "io.ktor:ktor-serialization-gson:$ktor_version"
implementation "io.ktor:ktor-client-gson:$ktor_version"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -28,15 +28,16 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.text.DateFormat
open class PetApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: Gson = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Add a new pet to the store

View File

@ -27,15 +27,16 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.text.DateFormat
open class StoreApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: Gson = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Delete purchase order by ID

View File

@ -27,15 +27,16 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.text.DateFormat
open class UserApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: Gson = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Create user

View File

@ -1,45 +1,47 @@
package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.request.*
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
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.request.request
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.utils.EmptyContent
import io.ktor.http.*
import io.ktor.http.content.ByteArrayContent
import io.ktor.http.content.OutgoingContent
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import io.ktor.http.URLBuilder
import io.ktor.http.content.PartData
import kotlin.Unit
import com.google.gson.Gson
import java.nio.charset.StandardCharsets
import io.ktor.http.encodeURLQueryComponent
import io.ktor.http.encodedPath
import io.ktor.http.takeFrom
import io.ktor.serialization.gson.*
import com.google.gson.GsonBuilder
import java.text.DateFormat
import org.openapitools.client.auth.ApiKeyAuth
import org.openapitools.client.auth.Authentication
import org.openapitools.client.auth.HttpBasicAuth
import org.openapitools.client.auth.HttpBearerAuth
import org.openapitools.client.auth.OAuth
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: Gson,
jsonBlock: GsonBuilder.() -> Unit = JSON_DEFAULT,
) {
private val serializer: JsonSerializer by lazy {
JsonSerializerImpl(json)
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) {
gson { jsonBlock() }
}
httpClientConfig?.invoke(it)
}
}
@ -55,9 +57,12 @@ open class ApiClient(
}
companion object {
const val BASE_URL = "http://petstore.swagger.io/v2"
val JSON_DEFAULT = Gson()
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
const val BASE_URL = "http://petstore.swagger.io/v2"
val JSON_DEFAULT : GsonBuilder.() -> Unit = {
setDateFormat(DateFormat.LONG)
setPrettyPrinting()
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
/**
@ -66,7 +71,7 @@ open class ApiClient(
* @param username Username
*/
fun setUsername(username: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.username = username
}
@ -77,7 +82,7 @@ open class ApiClient(
* @param password Password
*/
fun setPassword(password: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.password = password
}
@ -89,7 +94,7 @@ open class ApiClient(
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKey(apiKey: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKey = apiKey
}
@ -101,7 +106,7 @@ open class ApiClient(
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKeyPrefix = apiKeyPrefix
}
@ -112,7 +117,7 @@ open class ApiClient(
* @param accessToken Access token
*/
fun setAccessToken(accessToken: String) {
val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth?
val auth = authentications.values.firstOrNull { it is OAuth } as OAuth?
?: throw Exception("No OAuth2 authentication configured")
auth.accessToken = accessToken
}
@ -123,7 +128,7 @@ open class ApiClient(
* @param bearerToken The bearer token.
*/
fun setBearerToken(bearerToken: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
val auth = authentications.values.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
?: throw Exception("No Bearer authentication configured")
auth.bearerToken = bearerToken
}
@ -136,18 +141,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -160,8 +160,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
setBody(body)
}
}
@ -187,10 +186,3 @@ open class ApiClient(
RequestMethod.OPTIONS -> HttpMethod.Options
}
}
private class JsonSerializerImpl(private val gson: Gson) : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent =
ByteArrayContent(gson.toJson(data).toByteArray(StandardCharsets.UTF_8), contentType)
}

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,13 +2,13 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.ktor_version = '1.6.7'
ext.kotlin_version = '1.6.10'
ext.ktor_version = '2.0.3'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -31,9 +31,11 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3"
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
implementation "io.ktor:ktor-client-jackson:$ktor_version"
implementation "io.ktor:ktor-serialization-jackson:$ktor_version"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -28,15 +28,14 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.fasterxml.jackson.databind.ObjectMapper
open class PetApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: ObjectMapper = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Add a new pet to the store

View File

@ -27,15 +27,14 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.fasterxml.jackson.databind.ObjectMapper
open class StoreApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: ObjectMapper = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Delete purchase order by ID

View File

@ -27,15 +27,14 @@ import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.http.ParametersBuilder
import com.fasterxml.jackson.databind.ObjectMapper
open class UserApi(
baseUrl: String = ApiClient.BASE_URL,
httpClientEngine: HttpClientEngine? = null,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: ObjectMapper = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, json) {
jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT,
) : ApiClient(baseUrl, httpClientEngine, httpClientConfig, jsonBlock) {
/**
* Create user

View File

@ -1,44 +1,51 @@
package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.request.*
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
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.request.request
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.utils.EmptyContent
import io.ktor.http.*
import io.ktor.http.content.ByteArrayContent
import io.ktor.http.content.OutgoingContent
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import io.ktor.http.URLBuilder
import io.ktor.http.content.PartData
import kotlin.Unit
import io.ktor.http.encodeURLQueryComponent
import io.ktor.http.encodedPath
import io.ktor.http.takeFrom
import io.ktor.serialization.jackson.*
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
import org.openapitools.client.auth.ApiKeyAuth
import org.openapitools.client.auth.Authentication
import org.openapitools.client.auth.HttpBasicAuth
import org.openapitools.client.auth.HttpBearerAuth
import org.openapitools.client.auth.OAuth
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
json: ObjectMapper,
jsonBlock: ObjectMapper.() -> Unit = JSON_DEFAULT,
) {
private val serializer: JsonSerializer by lazy {
JsonSerializerImpl(json)
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) {
jackson { jsonBlock() }
}
httpClientConfig?.invoke(it)
}
}
@ -54,9 +61,16 @@ open class ApiClient(
}
companion object {
const val BASE_URL = "http://petstore.swagger.io/v2"
val JSON_DEFAULT = ObjectMapper()
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
const val BASE_URL = "http://petstore.swagger.io/v2"
val JSON_DEFAULT: ObjectMapper.() -> Unit = {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
registerModule(JavaTimeModule())
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
/**
@ -65,7 +79,7 @@ open class ApiClient(
* @param username Username
*/
fun setUsername(username: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.username = username
}
@ -76,7 +90,7 @@ open class ApiClient(
* @param password Password
*/
fun setPassword(password: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
val auth = authentications.values.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth?
?: throw Exception("No HTTP basic authentication configured")
auth.password = password
}
@ -88,7 +102,7 @@ open class ApiClient(
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKey(apiKey: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKey = apiKey
}
@ -100,7 +114,7 @@ open class ApiClient(
* @param paramName The name of the API key parameter, or null or set the first key.
*/
fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) {
val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
val auth = authentications.values.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth?
?: throw Exception("No API key authentication configured")
auth.apiKeyPrefix = apiKeyPrefix
}
@ -111,7 +125,7 @@ open class ApiClient(
* @param accessToken Access token
*/
fun setAccessToken(accessToken: String) {
val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth?
val auth = authentications.values.firstOrNull { it is OAuth } as OAuth?
?: throw Exception("No OAuth2 authentication configured")
auth.accessToken = accessToken
}
@ -122,7 +136,7 @@ open class ApiClient(
* @param bearerToken The bearer token.
*/
fun setBearerToken(bearerToken: String) {
val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
val auth = authentications.values.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth?
?: throw Exception("No Bearer authentication configured")
auth.bearerToken = bearerToken
}
@ -135,18 +149,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -159,8 +168,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
setBody(body)
}
}
@ -186,10 +194,3 @@ open class ApiClient(
RequestMethod.OPTIONS -> HttpMethod.Options
}
}
private class JsonSerializerImpl(private val objectMapper: ObjectMapper) : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent =
ByteArrayContent(objectMapper.writeValueAsBytes(data), contentType)
}

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -29,8 +29,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1"
implementation "com.google.code.gson:gson:2.8.7"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3"
implementation "com.google.code.gson:gson:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,9 +30,9 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.squareup.moshi:moshi:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -8,10 +8,10 @@ plugins {
group = "org.openapitools"
version = "1.0.0"
val kotlin_version = "1.6.0"
val coroutines_version = "1.5.2"
val serialization_version = "1.3.0"
val ktor_version = "1.6.4"
val kotlin_version = "1.6.10"
val coroutines_version = "1.6.3"
val serialization_version = "1.3.3"
val ktor_version = "2.0.3"
repositories {
mavenCentral()
@ -30,9 +30,11 @@ kotlin {
dependencies {
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")
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}
}

View File

@ -27,7 +27,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -26,7 +26,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -26,7 +26,6 @@ import org.openapitools.client.infrastructure.*
import io.ktor.client.HttpClientConfig
import io.ktor.client.request.forms.formData
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import io.ktor.http.ParametersBuilder
import kotlinx.serialization.*

View File

@ -3,43 +3,31 @@ package org.openapitools.client.infrastructure
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
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.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.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.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 kotlin.Unit
import kotlinx.serialization.json.Json
import org.openapitools.client.apis.*
import org.openapitools.client.models.*
import org.openapitools.client.auth.*
open class ApiClient(
private val baseUrl: String,
httpClientEngine: HttpClientEngine?,
httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null,
private val json: Json
private val jsonBlock: Json
) {
private val serializer: JsonSerializer by lazy {
KotlinxSerializer(json).ignoreOutgoingContent()
}
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy {
{
// Hold a reference to the serializer to avoid freezing the entire ApiClient instance
// when the JsonFeature is configured.
val serializerReference = serializer
it.install(JsonFeature) { serializer = serializerReference }
it.install(ContentNegotiation) { json(jsonBlock) }
httpClientConfig?.invoke(it)
}
}
@ -56,7 +44,11 @@ open class ApiClient(
companion object {
const val BASE_URL = "http://petstore.swagger.io/v2"
val JSON_DEFAULT = Json { ignoreUnknownKeys = true }
val JSON_DEFAULT = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType)
}
@ -136,18 +128,13 @@ open class ApiClient(
return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
return if (body != null) request(requestConfig, serializer.write(body, contentType), authNames)
else request(requestConfig, authNames = authNames)
}
protected suspend fun <T: Any?> jsonRequest(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse = request(requestConfig, body, authNames)
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: OutgoingContent = EmptyContent, authNames: kotlin.collections.List<String>): HttpResponse {
protected suspend fun <T: Any?> request(requestConfig: RequestConfig<T>, body: Any? = null, authNames: kotlin.collections.List<String>): HttpResponse {
requestConfig.updateForAuth<T>(authNames)
val headers = requestConfig.headers
return client.request<HttpResponse> {
return client.request {
this.url {
this.takeFrom(URLBuilder(baseUrl))
appendPath(requestConfig.path.trimStart('/').split('/'))
@ -160,7 +147,7 @@ open class ApiClient(
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH))
this.body = body
this.setBody(body)
}
}
@ -187,13 +174,3 @@ open class ApiClient(
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)
}
}

View File

@ -14,7 +14,7 @@ private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it
private fun Int.toBase64(): Char = BASE64_ALPHABET[this]
private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK
internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeStringUtf8(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes()
internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { dropLastWhile { it == BASE64_PAD } }.decodeBase64Bytes().readBytes()
/**
* Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes.

View File

@ -1,9 +1,9 @@
package org.openapitools.client.infrastructure
import io.ktor.client.call.TypeInfo
import io.ktor.client.call.typeInfo
import io.ktor.http.Headers
import io.ktor.http.isSuccess
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
open class HttpResponse<T : Any>(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider<T>) {
val status: Int = response.status.value
@ -29,11 +29,11 @@ interface BodyProvider<T : Any> {
class TypedBodyProvider<T : Any>(private val type: TypeInfo) : BodyProvider<T> {
@Suppress("UNCHECKED_CAST")
override suspend fun body(response: io.ktor.client.statement.HttpResponse): T =
response.call.receive(type) as T
response.call.body(type) as T
@Suppress("UNCHECKED_CAST")
override suspend fun <V : Any> typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V =
response.call.receive(type) as V
response.call.body(type) as V
}
class MappedBodyProvider<S : Any, T : Any>(private val provider: BodyProvider<S>, private val block: S.() -> T) : BodyProvider<T> {

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:3.12.13"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
ext.retrofitVersion = '2.9.0'
repositories {
@ -32,9 +32,9 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
ext.retrofitVersion = '2.9.0'
ext.rxJava3Version = '3.0.12'
@ -32,10 +32,10 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
implementation "io.reactivex.rxjava3:rxjava:$rxJava3Version"
implementation "com.squareup.retrofit2:adapter-rxjava3:2.9.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
ext.retrofitVersion = '2.9.0'
repositories {
@ -31,10 +31,10 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,9 +30,9 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
implementation "org.threeten:threetenbp:1.5.1"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -31,8 +31,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -2,12 +2,12 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '6.8.3'
gradleVersion = '7.5'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.10'
repositories {
maven { url "https://repo1.maven.org/maven2" }
@ -30,8 +30,8 @@ test {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
implementation "com.squareup.moshi:moshi-adapters:1.13.0"
implementation "com.squareup.okhttp3:okhttp:4.10.0"
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
}

View File

@ -1,32 +0,0 @@
# coding: utf-8
import sys
if sys.version_info < (3, 7):
import typing
def is_generic(klass):
""" Determine whether klass is a generic class """
return type(klass) == typing.GenericMeta
def is_dict(klass):
""" Determine whether klass is a Dict """
return klass.__extra__ == dict
def is_list(klass):
""" Determine whether klass is a List """
return klass.__extra__ == list
else:
def is_generic(klass):
""" Determine whether klass is a generic class """
return hasattr(klass, '__origin__')
def is_dict(klass):
""" Determine whether klass is a Dict """
return klass.__origin__ == dict
def is_list(klass):
""" Determine whether klass is a List """
return klass.__origin__ == list