Skip to content

Commit b5746e2

Browse files
committed
task: allow custom configuration of Http client
1 parent bdb081a commit b5746e2

File tree

5 files changed

+42
-19
lines changed

5 files changed

+42
-19
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies {
2121
implementation(libs.ktor.client.cio)
2222
implementation(libs.ktor.serialization.kotlinx.json)
2323
implementation(libs.ktor.client.content.negotiation)
24+
implementation(libs.ktor.logging.plugin)
2425
implementation(libs.jetbrains.kotlinx.serialization.json)
2526
implementation(libs.jsonapi.converter)
2627

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
1010
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
1111
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
1212
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
13+
ktor-logging-plugin = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
1314
ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
1415
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
1516
jetbrains-kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" }

src/main/kotlin/com/ctrlhub/core/Api.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@ import io.ktor.client.*
77
* The facade object through which interaction with the API occurs.
88
*/
99
object Api {
10-
var sessionToken: String? = null
10+
private var sessionToken: String? = null
11+
var httpClient: HttpClient = KtorClientFactory.create()
12+
private set
1113

12-
val httpClient: HttpClient by lazy {
13-
KtorClientFactory.create(sessionToken)
14+
fun withHttpClientConfig(config: HttpClientConfig<*>.() -> Unit) {
15+
httpClient = KtorClientFactory.create(
16+
configBlock = config
17+
)
18+
}
19+
20+
fun applySessionToken(newSessionToken: String) {
21+
sessionToken = newSessionToken
22+
httpClient = KtorClientFactory.createWithExistingConfig(httpClient, sessionToken)
1423
}
1524
}

src/main/kotlin/com/ctrlhub/core/http/KtorClientFactory.kt

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.ctrlhub.core.http
22

33
import com.ctrlhub.core.Config
44
import io.ktor.client.HttpClient
5+
import io.ktor.client.HttpClientConfig
56
import io.ktor.client.engine.cio.CIO
67
import io.ktor.client.plugins.UserAgent
78
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
@@ -10,34 +11,41 @@ import io.ktor.http.HttpHeaders
1011
import io.ktor.serialization.kotlinx.json.json
1112
import io.ktor.util.appendIfNameAbsent
1213
import kotlinx.serialization.json.Json
14+
import java.util.concurrent.atomic.AtomicReference
1315

1416
/**
1517
* A wrapper around the Ktor client. This allows default configuration
1618
* to be applied that is specific to the Ctrl Hub API, but is also flexible
1719
* in that an existing Ktor client can be passed, and config is applied to that.
1820
*/
1921
object KtorClientFactory {
20-
private lateinit var httpClient: HttpClient
21-
22-
fun create(sessionToken: String?): HttpClient {
23-
if (!this::httpClient.isInitialized) {
24-
val httpClient = HttpClient(CIO)
25-
this.httpClient = configureHttpClient(httpClient, baseUrl = Config.apiBaseUrl, sessionToken = sessionToken)
26-
}
27-
28-
return this.httpClient
22+
// Function to create an HttpClient with configuration and optional session token
23+
fun create(
24+
httpClient: HttpClient = HttpClient(CIO),
25+
sessionToken: String? = null,
26+
configBlock: HttpClientConfig<*>.() -> Unit = {}
27+
): HttpClient {
28+
return configureHttpClient(httpClient, sessionToken, configBlock)
2929
}
3030

31-
fun create(httpClient: HttpClient): HttpClient {
32-
this.httpClient = httpClient
33-
return configureHttpClient(httpClient, Config.apiBaseUrl)
31+
// Function to create a new HttpClient based on an existing HttpClient configuration
32+
fun createWithExistingConfig(
33+
existingClient: HttpClient,
34+
sessionToken: String? = null
35+
): HttpClient {
36+
return configureHttpClient(existingClient, sessionToken) // Reapply session token while retaining existing config
3437
}
3538

36-
private fun configureHttpClient(baseClient: HttpClient, baseUrl: String, sessionToken: String? = null): HttpClient {
39+
// Function to configure the HttpClient
40+
private fun configureHttpClient(
41+
baseClient: HttpClient,
42+
sessionToken: String? = null,
43+
configBlock: HttpClientConfig<*>.() -> Unit = {}
44+
): HttpClient {
3745
return baseClient.config {
3846
defaultRequest {
39-
url(baseUrl)
40-
sessionToken?.let { headers.append("X-Session-Token", it) }
47+
url(Config.apiBaseUrl)
48+
sessionToken?.let { headers.append("X-Session-Token", it) } // Apply session token if present
4149
headers.appendIfNameAbsent(HttpHeaders.ContentType, "application/json")
4250
}
4351
expectSuccess = true
@@ -50,6 +58,7 @@ object KtorClientFactory {
5058
install(UserAgent) {
5159
agent = Config.userAgent
5260
}
61+
configBlock() // Apply additional user-defined config
5362
}
5463
}
5564
}

src/main/kotlin/com/ctrlhub/core/router/Router.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ abstract class Router(protected val httpClient: HttpClient) {
2121
}
2222
}
2323

24-
protected suspend inline fun <reified T> performPost(endpoint: String, body: T): HttpResponse {
24+
protected suspend inline fun <reified T> performPost(endpoint: String, body: T, queryString: Map<String, String> = emptyMap()): HttpResponse {
2525
return httpClient.post(endpoint) {
2626
contentType(ContentType.Application.Json)
27+
url {
28+
queryString.forEach { key, value -> parameters.append(key, value) }
29+
}
2730
setBody(body)
2831
}
2932
}

0 commit comments

Comments
 (0)