diff --git a/typed-client/build.gradle.kts b/typed-client/build.gradle.kts index 77af020..1c1c702 100644 --- a/typed-client/build.gradle.kts +++ b/typed-client/build.gradle.kts @@ -17,6 +17,7 @@ kotlin { sourceSets.commonMain.dependencies { api(projects.typedShared) + api(libs.kotlinx.coroutines) api(libs.ktor.client.core) } -- 2.51.2 From 1d639d96276bcf70c41ca480efaa0fd6d2fba7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 22 Jan 2024 17:24:21 +0100 Subject: [PATCH 2/2] feat(typed): Client-side endpoint declaration --- typed-client/src/commonMain/kotlin/Client.kt | 55 +++++++++++++++++++ .../src/commonMain/kotlin/SpineResponse.kt | 27 +++++++++ 2 files changed, 82 insertions(+) create mode 100644 typed-client/src/commonMain/kotlin/Client.kt create mode 100644 typed-client/src/commonMain/kotlin/SpineResponse.kt diff --git a/typed-client/src/commonMain/kotlin/Client.kt b/typed-client/src/commonMain/kotlin/Client.kt new file mode 100644 index 0000000..033520c --- /dev/null +++ b/typed-client/src/commonMain/kotlin/Client.kt @@ -0,0 +1,55 @@ +@file:Suppress("DEPRECATION_ERROR") + +package opensavvy.spine.typed.client + +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.http.* +import opensavvy.spine.typed.Endpoint +import opensavvy.spine.typed.Parameters + +suspend inline fun HttpClient.request( + endpoint: Endpoint, + input: In, + parameters: Params, + contentType: ContentType = ContentType.Application.Json, + crossinline configure: HttpRequestBuilder.() -> Unit = {}, +): SpineResponse { + val result = request { + method = endpoint.method + url(endpoint.path) + + for ((name, value) in parameters.data) + parameter(name, value) + + contentType(contentType) + setBody(input) + + configure() + } + + return SpineResponse(result) +} + +// The above was the real implementation. +// However, most usage will be with the default body, etc. + +suspend inline fun HttpClient.request( + endpoint: Endpoint, + parameters: Params, + contentType: ContentType = ContentType.Application.Json, + crossinline configure: HttpRequestBuilder.() -> Unit = {}, +): SpineResponse = request(endpoint, Unit, parameters, contentType, configure) + +suspend inline fun HttpClient.request( + endpoint: Endpoint, + input: In, + contentType: ContentType = ContentType.Application.Json, + crossinline configure: HttpRequestBuilder.() -> Unit = {}, +): SpineResponse = request(endpoint, input, Parameters.Empty, contentType, configure) + +suspend inline fun HttpClient.request( + endpoint: Endpoint, + contentType: ContentType = ContentType.Application.Json, + crossinline configure: HttpRequestBuilder.() -> Unit = {}, +): SpineResponse = request(endpoint, Unit, Parameters.Empty, contentType, configure) diff --git a/typed-client/src/commonMain/kotlin/SpineResponse.kt b/typed-client/src/commonMain/kotlin/SpineResponse.kt new file mode 100644 index 0000000..ed8a4fa --- /dev/null +++ b/typed-client/src/commonMain/kotlin/SpineResponse.kt @@ -0,0 +1,27 @@ +package opensavvy.spine.typed.client + +import io.ktor.client.call.* +import io.ktor.client.statement.* +import io.ktor.http.* +import kotlin.jvm.JvmInline + +@JvmInline +value class SpineResponse<@Suppress("unused") Out : Any>( + val httpResponse: HttpResponse, +) { + + val isSuccessful get() = httpResponse.status.isSuccess() +} + +suspend inline fun SpineResponse.bodyOrNull(): Out? = + if (isSuccessful) httpResponse.call.body() + else null + +suspend inline fun SpineResponse.bodyOrThrow(): Out = + if (isSuccessful) httpResponse.call.body() + else throw SpineReceptionException(httpResponse, httpResponse.bodyAsText()) + +class SpineReceptionException( + val response: HttpResponse, + body: String, +) : RuntimeException("Received the HTTP code ${response.status} with body '$body'")