From 94014a43365efe5fd03581d4a845e7758dc2f9f0 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Tue, 19 Nov 2024 19:02:07 +0300 Subject: [PATCH 1/6] feat: SDK-1568 expose okhttp client as a configuration in sdk core --- .../sdk/core/client/BaseRapidClient.kt | 32 ++++- .../sdk/core/client/BaseXapClient.kt | 2 +- .../expediagroup/sdk/core/client/Client.kt | 126 ++++++++++-------- .../sdk/core/client/ExpediaGroupClient.kt | 2 +- .../sdk/core/client/OkHttpEventListener.kt | 61 +++++---- .../configuration/RapidClientConfiguration.kt | 4 +- .../sdk/core/constant/ConfigurationName.kt | 2 + 7 files changed, 144 insertions(+), 85 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt index c1b903440..7a0aff2ea 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt @@ -22,6 +22,8 @@ import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvid import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.OkHttp +import okhttp3.OkHttpClient /** * The integration point between the SDK Core and the product SDKs. @@ -39,7 +41,15 @@ abstract class BaseRapidClient( clientConfiguration.toProvider(), RapidConfigurationProvider ) - private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, httpClientEngine) + + private val _httpClientEngine: HttpClientEngine = clientConfiguration.okHttpClient?.let { + + OkHttp.create { + preconfigured = it + } + } ?: httpClientEngine + + private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, _httpClientEngine) init { finalize() @@ -53,5 +63,23 @@ abstract class BaseRapidClient( /** A [BaseRapidClient] builder. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class Builder> : Client.Builder() + abstract class Builder> : BuilderExtension() + + /** A [BaseRapidClient] builder with ability to pass a custom okhttp client. */ + @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. + abstract class BuilderWithHttpClient> : Client.Builder() { + + protected var okHttpClient: OkHttpClient? = null + + override fun self(): SELF { + return this as SELF + } + + /** Sets the [OkHttpClient] to use for the [BaseRapidClient]. */ + fun okHttpClient(okHttpClient: OkHttpClient): SELF { + this.okHttpClient = okHttpClient + return self() + } + + } } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt index b3f550c53..2fd66f5af 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt @@ -53,5 +53,5 @@ abstract class BaseXapClient( /** A [BaseXapClient] builder. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class Builder> : Client.Builder() + abstract class Builder> : BuilderExtension() } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt index 11ed5ea8d..c3b642d68 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt @@ -61,7 +61,7 @@ val dispatcher = Dispatcher().apply { val DEFAULT_HTTP_CLIENT_ENGINE: HttpClientEngine = OkHttp.create { config { - eventListener(OkHttpEventListener) + eventListenerFactory(OkHttpEventListener.FACTORY) dispatcher(dispatcher) } } @@ -147,7 +147,7 @@ abstract class Client( suspend fun performGet(url: String): HttpResponse = httpHandler.performGet(httpClient, url) /** - * A [Client] builder. + * A [Client] base builder. */ abstract class Builder> { /** Sets the API key to use for authentication. */ @@ -159,33 +159,6 @@ abstract class Client( /** Sets the API endpoint to use for requests. */ protected var endpoint: String? = null - /** - * Sets the request timeout in milliseconds. - * - * Request timeout is the time period from the start of the request to the completion of the response. - * - * Default is infinite - no timeout. - */ - protected var requestTimeout: Long? = null - - /** - * Sets the connection timeout in milliseconds. - * - * Connection timeout is the time period from the start of the request to the establishment of the connection with the server. - * - * Default is 10 seconds (10000 milliseconds). - */ - protected var connectionTimeout: Long? = null - - /** - * Sets the socket timeout in milliseconds. - * - * Socket timeout is the maximum period of inactivity between two consecutive data packets. - * - * Default is 15 seconds (15000 milliseconds). - */ - protected var socketTimeout: Long? = null - /** Sets tne body fields to be masked in logging. */ protected var maskedLoggingHeaders: Set? = null @@ -223,6 +196,70 @@ abstract class Client( return self() } + /** + * Sets tne headers to be masked in logging. + * + * @param headers the headers to be masked in logging. + * @return The [Builder] instance. + */ + fun maskedLoggingHeaders(vararg headers: String): SELF { + this.maskedLoggingHeaders = headers.toSet() + log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.MASKED_LOGGING_HEADERS, headers.joinToString())) + return self() + } + + /** + * Sets tne body fields to be masked in logging. + * + * @param fields the body fields to be masked in logging. + * @return The [Builder] instance. + */ + fun maskedLoggingBodyFields(vararg fields: String): SELF { + this.maskedLoggingBodyFields = fields.toSet() + log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.MASKED_LOGGING_BODY_FIELDS, fields.joinToString())) + return self() + } + + /** Create a [Client] object. */ + abstract fun build(): Client + + @Suppress("UNCHECKED_CAST") // This is safe because of the type parameter + protected open fun self(): SELF = this as SELF + } + + + /** + * A [Client] extension builder. + */ + abstract class BuilderExtension> : Builder() { + + /** + * Sets the request timeout in milliseconds. + * + * Request timeout is the time period from the start of the request to the completion of the response. + * + * Default is infinite - no timeout. + */ + protected var requestTimeout: Long? = null + + /** + * Sets the connection timeout in milliseconds. + * + * Connection timeout is the time period from the start of the request to the establishment of the connection with the server. + * + * Default is 10 seconds (10000 milliseconds). + */ + protected var connectionTimeout: Long? = null + + /** + * Sets the socket timeout in milliseconds. + * + * Socket timeout is the maximum period of inactivity between two consecutive data packets. + * + * Default is 15 seconds (15000 milliseconds). + */ + protected var socketTimeout: Long? = null + /** * Sets the request timeout in milliseconds. * Request timeout is the time period from the start of the request to the completion of the response. @@ -265,37 +302,14 @@ abstract class Client( return self() } - /** - * Sets tne headers to be masked in logging. - * - * @param headers the headers to be masked in logging. - * @return The [Builder] instance. - */ - fun maskedLoggingHeaders(vararg headers: String): SELF { - this.maskedLoggingHeaders = headers.toSet() - log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.MASKED_LOGGING_HEADERS, headers.joinToString())) - return self() - } - - /** - * Sets tne body fields to be masked in logging. - * - * @param fields the body fields to be masked in logging. - * @return The [Builder] instance. - */ - fun maskedLoggingBodyFields(vararg fields: String): SELF { - this.maskedLoggingBodyFields = fields.toSet() - log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.MASKED_LOGGING_BODY_FIELDS, fields.joinToString())) - return self() - } - /** Create a [Client] object. */ - abstract fun build(): Client + abstract override fun build(): Client - @Suppress("UNCHECKED_CAST") // This is safe because of the type parameter - protected open fun self(): SELF = this as SELF } + } /** Executes the hooks for the client. */ fun T.finalize() = Hooks.execute(this) + + diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt index cf28f895d..892a6d9a3 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt @@ -53,7 +53,7 @@ abstract class ExpediaGroupClient( /** An [ExpediaGroupClient] builder. */ @Suppress("unused") // This is used by the generated SDK clients. - abstract class Builder> : Client.Builder() { + abstract class Builder> : BuilderExtension() { /** Sets the API auth endpoint to use for requests. */ protected var authEndpoint: String? = null diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt index 25dbd741a..4845cdf55 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt @@ -19,28 +19,41 @@ import com.expediagroup.sdk.core.constant.HeaderKey import com.expediagroup.sdk.core.plugin.logging.ExpediaGroupLoggerFactory import okhttp3.Call import okhttp3.Connection -import okhttp3.EventListener import okhttp3.Handshake import okhttp3.Protocol import okhttp3.Request import okhttp3.Response +import okhttp3.EventListener import java.io.IOException import java.net.InetSocketAddress import java.net.Proxy +import java.util.UUID +import java.util.concurrent.atomic.AtomicReference + +data class OkHttpEventListener private constructor(private val transactionId: AtomicReference) : EventListener() { -object OkHttpEventListener : EventListener() { private val log = ExpediaGroupLoggerFactory.getLogger(this::class.java) - fun Call.getTransactionId() = request().headers[HeaderKey.TRANSACTION_ID] + // Factory for creating EventListeners with transaction IDs + companion object { + // Expose the factory as a static property + val FACTORY: EventListener.Factory = object : EventListener.Factory { + override fun create(call: Call): EventListener { + val transactionIdHeader = call.request().header(HeaderKey.TRANSACTION_ID) + val transactionId = AtomicReference(UUID.fromString(transactionIdHeader)) + return OkHttpEventListener(transactionId) + } + } + } override fun callStart(call: Call) { super.callStart(call) - log.debug("Call start for transaction-id: [${call.getTransactionId()}]") + log.debug("Call start for transaction-id: [${transactionId.get()}]") } override fun callEnd(call: Call) { super.callEnd(call) - log.debug("Call end for transaction-id: [${call.getTransactionId()}]") + log.debug("Call end for transaction-id: [${transactionId.get()}]") } override fun callFailed( @@ -48,12 +61,12 @@ object OkHttpEventListener : EventListener() { ioe: IOException ) { super.callFailed(call, ioe) - log.debug("Call failed for transaction-id: [${call.getTransactionId()}] with exception message: ${ioe.message}") + log.debug("Call failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") } override fun canceled(call: Call) { super.canceled(call) - log.debug("Call canceled for transaction-id: [${call.getTransactionId()}]") + log.debug("Call canceled for transaction-id: [${transactionId.get()}]") } override fun connectStart( @@ -62,7 +75,7 @@ object OkHttpEventListener : EventListener() { proxy: Proxy ) { super.connectStart(call, inetSocketAddress, proxy) - log.debug("Connect start for transaction-id: [${call.getTransactionId()}]") + log.debug("Connect start for transaction-id: [${transactionId.get()}]") } override fun connectEnd( @@ -72,7 +85,7 @@ object OkHttpEventListener : EventListener() { protocol: Protocol? ) { super.connectEnd(call, inetSocketAddress, proxy, protocol) - log.debug("Connect end for transaction-id: [${call.getTransactionId()}]") + log.debug("Connect end for transaction-id: [${transactionId.get()}]") } override fun connectFailed( @@ -83,7 +96,7 @@ object OkHttpEventListener : EventListener() { ioe: IOException ) { super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) - log.debug("Connect failed for transaction-id: [${call.getTransactionId()}] with exception message: ${ioe.message}") + log.debug("Connect failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") } override fun connectionAcquired( @@ -91,7 +104,7 @@ object OkHttpEventListener : EventListener() { connection: Connection ) { super.connectionAcquired(call, connection) - log.debug("Connection acquired for transaction-id: [${call.getTransactionId()}]") + log.debug("Connection acquired for transaction-id: [${transactionId.get()}]") } override fun connectionReleased( @@ -99,12 +112,12 @@ object OkHttpEventListener : EventListener() { connection: Connection ) { super.connectionReleased(call, connection) - log.debug("Connection released for transaction-id: [${call.getTransactionId()}]") + log.debug("Connection released for transaction-id: [${transactionId.get()}]") } override fun secureConnectStart(call: Call) { super.secureConnectStart(call) - log.debug("Secure connect start for transaction-id: [${call.getTransactionId()}]") + log.debug("Secure connect start for transaction-id: [${transactionId.get()}]") } override fun secureConnectEnd( @@ -112,12 +125,12 @@ object OkHttpEventListener : EventListener() { handshake: Handshake? ) { super.secureConnectEnd(call, handshake) - log.debug("Secure connect end for transaction-id: [${call.getTransactionId()}]") + log.debug("Secure connect end for transaction-id: [${transactionId.get()}]") } override fun requestHeadersStart(call: Call) { super.requestHeadersStart(call) - log.debug("Sending request headers start for transaction-id: [${call.getTransactionId()}]") + log.debug("Sending request headers start for transaction-id: [${transactionId.get()}]") } override fun requestHeadersEnd( @@ -125,12 +138,12 @@ object OkHttpEventListener : EventListener() { request: Request ) { super.requestHeadersEnd(call, request) - log.debug("Sending request headers end for transaction-id: [${call.getTransactionId()}]") + log.debug("Sending request headers end for transaction-id: [${transactionId.get()}]") } override fun requestBodyStart(call: Call) { super.requestBodyStart(call) - log.debug("Sending request body start for transaction-id: [${call.getTransactionId()}]") + log.debug("Sending request body start for transaction-id: [${transactionId.get()}]") } override fun requestBodyEnd( @@ -138,7 +151,7 @@ object OkHttpEventListener : EventListener() { byteCount: Long ) { super.requestBodyEnd(call, byteCount) - log.debug("Sending request body end for transaction-id: [${call.getTransactionId()}] with byte count: $byteCount") + log.debug("Sending request body end for transaction-id: [${transactionId.get()}] with byte count: $byteCount") } override fun requestFailed( @@ -146,12 +159,12 @@ object OkHttpEventListener : EventListener() { ioe: IOException ) { super.requestFailed(call, ioe) - log.debug("Request failed for transaction-id: [${call.getTransactionId()}] with exception message: ${ioe.message}") + log.debug("Request failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") } override fun responseHeadersStart(call: Call) { super.responseHeadersStart(call) - log.debug("Receiving response headers start for transaction-id: [${call.getTransactionId()}]") + log.debug("Receiving response headers start for transaction-id: [${transactionId.get()}]") } override fun responseHeadersEnd( @@ -159,12 +172,12 @@ object OkHttpEventListener : EventListener() { response: Response ) { super.responseHeadersEnd(call, response) - log.debug("Receiving response headers end for transaction-id: [${call.getTransactionId()}]") + log.debug("Receiving response headers end for transaction-id: [${transactionId.get()}]") } override fun responseBodyStart(call: Call) { super.responseBodyStart(call) - log.debug("Receiving response body start for transaction-id: [${call.getTransactionId()}]") + log.debug("Receiving response body start for transaction-id: [${transactionId.get()}]") } override fun responseBodyEnd( @@ -172,7 +185,7 @@ object OkHttpEventListener : EventListener() { byteCount: Long ) { super.responseBodyEnd(call, byteCount) - log.debug("Receiving response body end for transaction-id: [${call.getTransactionId()}] with byte count: $byteCount") + log.debug("Receiving response body end for transaction-id: [${transactionId.get()}] with byte count: $byteCount") } override fun responseFailed( @@ -180,6 +193,6 @@ object OkHttpEventListener : EventListener() { ioe: IOException ) { super.responseFailed(call, ioe) - log.debug("Receiving response failed for transaction-id: [${call.getTransactionId()}] with exception message: ${ioe.message}") + log.debug("Receiving response failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") } } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt index cac3637de..523d116f2 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt @@ -16,6 +16,7 @@ package com.expediagroup.sdk.core.configuration import com.expediagroup.sdk.core.client.BaseRapidClient +import okhttp3.OkHttpClient /** * Configuration for the [BaseRapidClient]. @@ -37,5 +38,6 @@ data class RapidClientConfiguration( override val connectionTimeout: Long? = null, override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, - override val maskedLoggingBodyFields: Set? = null + override val maskedLoggingBodyFields: Set? = null, + val okHttpClient: OkHttpClient? = null ) : ClientConfiguration diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/constant/ConfigurationName.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/constant/ConfigurationName.kt index 0004086b7..0d9b9a955 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/constant/ConfigurationName.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/constant/ConfigurationName.kt @@ -37,4 +37,6 @@ internal object ConfigurationName { const val RUNTIME_CONFIGURATION_PROVIDER = "runtime configuration" const val CONFIGURATION_COLLECTOR = "configuration collector" + + const val OKHTTP_CLIENT = "okhttp client" } From dffad41b1f8f0d5f70bb8fca9ee0d146f5ea1e88 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Mon, 25 Nov 2024 17:55:20 +0300 Subject: [PATCH 2/6] updates based on received comments --- .../sdk/core/client/BaseRapidClient.kt | 13 ++-- .../sdk/core/client/BaseXapClient.kt | 2 +- .../expediagroup/sdk/core/client/Client.kt | 9 ++- .../sdk/core/client/ExpediaGroupClient.kt | 2 +- .../sdk/core/client/OkHttpEventListener.kt | 62 ++++++++++--------- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt index 7a0aff2ea..e032a260f 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt @@ -42,14 +42,13 @@ abstract class BaseRapidClient( RapidConfigurationProvider ) - private val _httpClientEngine: HttpClientEngine = clientConfiguration.okHttpClient?.let { - + private val httpClientEngine: HttpClientEngine = clientConfiguration.okHttpClient?.let { OkHttp.create { preconfigured = it } } ?: httpClientEngine - private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, _httpClientEngine) + private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, httpClientEngine) init { finalize() @@ -63,17 +62,15 @@ abstract class BaseRapidClient( /** A [BaseRapidClient] builder. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class Builder> : BuilderExtension() + abstract class Builder> : HttpConfigurableBuilder() /** A [BaseRapidClient] builder with ability to pass a custom okhttp client. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. abstract class BuilderWithHttpClient> : Client.Builder() { - protected var okHttpClient: OkHttpClient? = null - override fun self(): SELF { - return this as SELF - } + @Suppress("UNCHECKED_CAST") + override fun self(): SELF = this as SELF /** Sets the [OkHttpClient] to use for the [BaseRapidClient]. */ fun okHttpClient(okHttpClient: OkHttpClient): SELF { diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt index 2fd66f5af..a28f33b0d 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt @@ -53,5 +53,5 @@ abstract class BaseXapClient( /** A [BaseXapClient] builder. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class Builder> : BuilderExtension() + abstract class Builder> : HttpConfigurableBuilder() } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt index c3b642d68..d7ef4c7b2 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt @@ -229,9 +229,14 @@ abstract class Client( /** - * A [Client] extension builder. + * A builder class for configuring HTTP-related settings for a [Client]. + * + * This builder class extends the base [Client.Builder] class and provides additional methods + * for configuring HTTP-specific settings such as request timeout, connection timeout, and socket timeout. + * + * @param The type of the builder itself, used for method chaining. */ - abstract class BuilderExtension> : Builder() { + abstract class HttpConfigurableBuilder> : Builder() { /** * Sets the request timeout in milliseconds. diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt index 892a6d9a3..b47306deb 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/ExpediaGroupClient.kt @@ -53,7 +53,7 @@ abstract class ExpediaGroupClient( /** An [ExpediaGroupClient] builder. */ @Suppress("unused") // This is used by the generated SDK clients. - abstract class Builder> : BuilderExtension() { + abstract class Builder> : HttpConfigurableBuilder() { /** Sets the API auth endpoint to use for requests. */ protected var authEndpoint: String? = null diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt index 4845cdf55..acd0044f4 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt @@ -30,30 +30,36 @@ import java.net.Proxy import java.util.UUID import java.util.concurrent.atomic.AtomicReference -data class OkHttpEventListener private constructor(private val transactionId: AtomicReference) : EventListener() { +/** + * An `EventListener` implementation for OkHttp that logs various events during the lifecycle of an HTTP call. + * + * This listener logs events such as call start, call end, connection start, connection end, request headers start, + * request headers end, request body start, request body end, response headers start, response headers end, + * response body start, response body end, and failures. + * + * @property transactionId A reference to the unique transaction ID associated with the HTTP call. + */ +class OkHttpEventListener private constructor(private val transactionId: AtomicReference) : EventListener() { private val log = ExpediaGroupLoggerFactory.getLogger(this::class.java) - // Factory for creating EventListeners with transaction IDs companion object { - // Expose the factory as a static property - val FACTORY: EventListener.Factory = object : EventListener.Factory { - override fun create(call: Call): EventListener { + val FACTORY: Factory = + Factory { call -> val transactionIdHeader = call.request().header(HeaderKey.TRANSACTION_ID) val transactionId = AtomicReference(UUID.fromString(transactionIdHeader)) - return OkHttpEventListener(transactionId) + OkHttpEventListener(transactionId) } - } } override fun callStart(call: Call) { super.callStart(call) - log.debug("Call start for transaction-id: [${transactionId.get()}]") + log.debug("Call start for transaction-id: [{}]", transactionId.get()) } override fun callEnd(call: Call) { super.callEnd(call) - log.debug("Call end for transaction-id: [${transactionId.get()}]") + log.debug("Call end for transaction-id: [{}]", transactionId.get()) } override fun callFailed( @@ -61,12 +67,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At ioe: IOException ) { super.callFailed(call, ioe) - log.debug("Call failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") + log.debug("Call failed for transaction-id: [{}] with exception message: {}", transactionId.get(), ioe.message) } override fun canceled(call: Call) { super.canceled(call) - log.debug("Call canceled for transaction-id: [${transactionId.get()}]") + log.debug("Call canceled for transaction-id: [{}]", transactionId.get()) } override fun connectStart( @@ -75,7 +81,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At proxy: Proxy ) { super.connectStart(call, inetSocketAddress, proxy) - log.debug("Connect start for transaction-id: [${transactionId.get()}]") + log.debug("Connect start for transaction-id: [{}]", transactionId.get()) } override fun connectEnd( @@ -85,7 +91,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At protocol: Protocol? ) { super.connectEnd(call, inetSocketAddress, proxy, protocol) - log.debug("Connect end for transaction-id: [${transactionId.get()}]") + log.debug("Connect end for transaction-id: [{}]", transactionId.get()) } override fun connectFailed( @@ -96,7 +102,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At ioe: IOException ) { super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) - log.debug("Connect failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") + log.debug("Connect failed for transaction-id: [{}] with exception message: {}", transactionId.get(), ioe.message) } override fun connectionAcquired( @@ -104,7 +110,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At connection: Connection ) { super.connectionAcquired(call, connection) - log.debug("Connection acquired for transaction-id: [${transactionId.get()}]") + log.debug("Connection acquired for transaction-id: [{}]", transactionId.get()) } override fun connectionReleased( @@ -112,12 +118,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At connection: Connection ) { super.connectionReleased(call, connection) - log.debug("Connection released for transaction-id: [${transactionId.get()}]") + log.debug("Connection released for transaction-id: [{}]", transactionId.get()) } override fun secureConnectStart(call: Call) { super.secureConnectStart(call) - log.debug("Secure connect start for transaction-id: [${transactionId.get()}]") + log.debug("Secure connect start for transaction-id: [{}]", transactionId.get()) } override fun secureConnectEnd( @@ -125,12 +131,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At handshake: Handshake? ) { super.secureConnectEnd(call, handshake) - log.debug("Secure connect end for transaction-id: [${transactionId.get()}]") + log.debug("Secure connect end for transaction-id: [{}]", transactionId.get()) } override fun requestHeadersStart(call: Call) { super.requestHeadersStart(call) - log.debug("Sending request headers start for transaction-id: [${transactionId.get()}]") + log.debug("Sending request headers start for transaction-id: [{}]", transactionId.get()) } override fun requestHeadersEnd( @@ -138,12 +144,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At request: Request ) { super.requestHeadersEnd(call, request) - log.debug("Sending request headers end for transaction-id: [${transactionId.get()}]") + log.debug("Sending request headers end for transaction-id: [{}]", transactionId.get()) } override fun requestBodyStart(call: Call) { super.requestBodyStart(call) - log.debug("Sending request body start for transaction-id: [${transactionId.get()}]") + log.debug("Sending request body start for transaction-id: [{}]", transactionId.get()) } override fun requestBodyEnd( @@ -151,7 +157,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At byteCount: Long ) { super.requestBodyEnd(call, byteCount) - log.debug("Sending request body end for transaction-id: [${transactionId.get()}] with byte count: $byteCount") + log.debug("Sending request body end for transaction-id: [{}] with byte count: {}", transactionId.get(), byteCount) } override fun requestFailed( @@ -159,12 +165,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At ioe: IOException ) { super.requestFailed(call, ioe) - log.debug("Request failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") + log.debug("Request failed for transaction-id: [{}] with exception message: {}", transactionId.get(), ioe.message) } override fun responseHeadersStart(call: Call) { super.responseHeadersStart(call) - log.debug("Receiving response headers start for transaction-id: [${transactionId.get()}]") + log.debug("Receiving response headers start for transaction-id: [{}]", transactionId.get()) } override fun responseHeadersEnd( @@ -172,12 +178,12 @@ data class OkHttpEventListener private constructor(private val transactionId: At response: Response ) { super.responseHeadersEnd(call, response) - log.debug("Receiving response headers end for transaction-id: [${transactionId.get()}]") + log.debug("Receiving response headers end for transaction-id: [{}]", transactionId.get()) } override fun responseBodyStart(call: Call) { super.responseBodyStart(call) - log.debug("Receiving response body start for transaction-id: [${transactionId.get()}]") + log.debug("Receiving response body start for transaction-id: [{}]", transactionId.get()) } override fun responseBodyEnd( @@ -185,7 +191,7 @@ data class OkHttpEventListener private constructor(private val transactionId: At byteCount: Long ) { super.responseBodyEnd(call, byteCount) - log.debug("Receiving response body end for transaction-id: [${transactionId.get()}] with byte count: $byteCount") + log.debug("Receiving response body end for transaction-id: [{}] with byte count: {}", transactionId.get(), byteCount) } override fun responseFailed( @@ -193,6 +199,6 @@ data class OkHttpEventListener private constructor(private val transactionId: At ioe: IOException ) { super.responseFailed(call, ioe) - log.debug("Receiving response failed for transaction-id: [${transactionId.get()}] with exception message: ${ioe.message}") + log.debug("Receiving response failed for transaction-id: [{}] with exception message: {}", transactionId.get(), ioe.message) } } From 5929f24b871ee77e81ed53b123e2e44038df55a8 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Tue, 26 Nov 2024 12:57:00 +0300 Subject: [PATCH 3/6] enable custom http client configuration for XAP --- .../expediagroup/sdk/core/client/BaseRapidClient.kt | 4 ++-- .../com/expediagroup/sdk/core/client/BaseXapClient.kt | 10 +++++++++- .../sdk/core/configuration/XapClientConfiguration.kt | 4 +++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt index e032a260f..2de1f9b73 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt @@ -42,13 +42,13 @@ abstract class BaseRapidClient( RapidConfigurationProvider ) - private val httpClientEngine: HttpClientEngine = clientConfiguration.okHttpClient?.let { + private val engine: HttpClientEngine = clientConfiguration.okHttpClient?.let { OkHttp.create { preconfigured = it } } ?: httpClientEngine - private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, httpClientEngine) + private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, engine) init { finalize() diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt index a28f33b0d..74cd8c78b 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt @@ -22,6 +22,7 @@ import com.expediagroup.sdk.core.configuration.provider.XapConfigurationProvider import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.* /** * The integration point between the SDK Core and the product SDKs. @@ -39,7 +40,14 @@ abstract class BaseXapClient( clientConfiguration.toProvider(), XapConfigurationProvider ) - private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.BASIC, httpClientEngine) + + private val engine: HttpClientEngine = clientConfiguration.okHttpClient?.let { + OkHttp.create { + preconfigured = it + } + } ?: httpClientEngine + + private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.BASIC, engine) init { finalize() diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt index f17f9d778..f3ca5ad8f 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt @@ -16,6 +16,7 @@ package com.expediagroup.sdk.core.configuration import com.expediagroup.sdk.core.client.BaseXapClient +import okhttp3.OkHttpClient /** * Configuration for the [BaseXapClient]. @@ -37,5 +38,6 @@ data class XapClientConfiguration( override val connectionTimeout: Long? = null, override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, - override val maskedLoggingBodyFields: Set? = null + override val maskedLoggingBodyFields: Set? = null, + val okHttpClient: OkHttpClient? = null ) : ClientConfiguration From f16b78c86d8b3bba71a6003421b96217c6aec295 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Tue, 26 Nov 2024 15:47:23 +0300 Subject: [PATCH 4/6] update eventlistener to use transaction id as string --- .../com/expediagroup/sdk/core/client/OkHttpEventListener.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt index acd0044f4..72d09ca6c 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt @@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicReference * * @property transactionId A reference to the unique transaction ID associated with the HTTP call. */ -class OkHttpEventListener private constructor(private val transactionId: AtomicReference) : EventListener() { +class OkHttpEventListener private constructor(private val transactionId: AtomicReference) : EventListener() { private val log = ExpediaGroupLoggerFactory.getLogger(this::class.java) @@ -47,7 +47,7 @@ class OkHttpEventListener private constructor(private val transactionId: AtomicR val FACTORY: Factory = Factory { call -> val transactionIdHeader = call.request().header(HeaderKey.TRANSACTION_ID) - val transactionId = AtomicReference(UUID.fromString(transactionIdHeader)) + val transactionId = AtomicReference(transactionIdHeader.toString()) OkHttpEventListener(transactionId) } } From 2f270d84fae36df3e0e4db16645a6855ae99f999 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Tue, 26 Nov 2024 16:00:50 +0300 Subject: [PATCH 5/6] update BaseXapClient --- .../sdk/core/client/BaseXapClient.kt | 19 ++++++++++++++++++- .../sdk/core/client/OkHttpEventListener.kt | 1 - 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt index 74cd8c78b..607dae8f3 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt @@ -22,7 +22,8 @@ import com.expediagroup.sdk.core.configuration.provider.XapConfigurationProvider import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.okhttp.* +import io.ktor.client.engine.okhttp.OkHttp +import okhttp3.OkHttpClient /** * The integration point between the SDK Core and the product SDKs. @@ -62,4 +63,20 @@ abstract class BaseXapClient( /** A [BaseXapClient] builder. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. abstract class Builder> : HttpConfigurableBuilder() + + /** A [BaseXapClient] builder with ability to pass a custom okhttp client. */ + @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. + abstract class BuilderWithHttpClient> : Client.Builder() { + protected var okHttpClient: OkHttpClient? = null + + @Suppress("UNCHECKED_CAST") + override fun self(): SELF = this as SELF + + /** Sets the [OkHttpClient] to use for the [BaseXapClient]. */ + fun okHttpClient(okHttpClient: OkHttpClient): SELF { + this.okHttpClient = okHttpClient + return self() + } + + } } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt index 72d09ca6c..3374e9347 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/OkHttpEventListener.kt @@ -27,7 +27,6 @@ import okhttp3.EventListener import java.io.IOException import java.net.InetSocketAddress import java.net.Proxy -import java.util.UUID import java.util.concurrent.atomic.AtomicReference /** From c59a4d647a65c2765f9ddd41ee43aa8d5f971d48 Mon Sep 17 00:00:00 2001 From: Dana AlTayeh Date: Tue, 26 Nov 2024 17:13:22 +0300 Subject: [PATCH 6/6] update based on comments --- .../sdk/core/client/BaseRapidClient.kt | 17 ++----------- .../sdk/core/client/BaseXapClient.kt | 17 ++----------- .../expediagroup/sdk/core/client/Client.kt | 25 ++++++++++++++++--- .../core/configuration/ClientConfiguration.kt | 5 +++- .../ExpediaGroupClientConfiguration.kt | 3 +++ .../configuration/RapidClientConfiguration.kt | 2 +- .../configuration/XapClientConfiguration.kt | 2 +- .../collector/ConfigurationCollector.kt | 2 ++ .../provider/ConfigurationProvider.kt | 5 ++++ .../provider/RuntimeConfigurationProvider.kt | 4 ++- .../ExpediaGroupClientConfigurationTest.kt | 2 ++ .../expediagroup-sdk/client.mustache | 2 +- 12 files changed, 48 insertions(+), 38 deletions(-) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt index 2de1f9b73..52d20daa3 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseRapidClient.kt @@ -23,7 +23,6 @@ import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationSt import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.okhttp.OkHttp -import okhttp3.OkHttpClient /** * The integration point between the SDK Core and the product SDKs. @@ -42,7 +41,7 @@ abstract class BaseRapidClient( RapidConfigurationProvider ) - private val engine: HttpClientEngine = clientConfiguration.okHttpClient?.let { + private val engine: HttpClientEngine = _configurationProvider.okHttpClient?.let { OkHttp.create { preconfigured = it } @@ -66,17 +65,5 @@ abstract class BaseRapidClient( /** A [BaseRapidClient] builder with ability to pass a custom okhttp client. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class BuilderWithHttpClient> : Client.Builder() { - protected var okHttpClient: OkHttpClient? = null - - @Suppress("UNCHECKED_CAST") - override fun self(): SELF = this as SELF - - /** Sets the [OkHttpClient] to use for the [BaseRapidClient]. */ - fun okHttpClient(okHttpClient: OkHttpClient): SELF { - this.okHttpClient = okHttpClient - return self() - } - - } + abstract class BuilderWithHttpClient> : Client.BuilderWithHttpClient() } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt index 607dae8f3..19ef01d16 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/BaseXapClient.kt @@ -23,7 +23,6 @@ import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationSt import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.okhttp.OkHttp -import okhttp3.OkHttpClient /** * The integration point between the SDK Core and the product SDKs. @@ -42,7 +41,7 @@ abstract class BaseXapClient( XapConfigurationProvider ) - private val engine: HttpClientEngine = clientConfiguration.okHttpClient?.let { + private val engine: HttpClientEngine = _configurationProvider.okHttpClient?.let { OkHttp.create { preconfigured = it } @@ -66,17 +65,5 @@ abstract class BaseXapClient( /** A [BaseXapClient] builder with ability to pass a custom okhttp client. */ @Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients. - abstract class BuilderWithHttpClient> : Client.Builder() { - protected var okHttpClient: OkHttpClient? = null - - @Suppress("UNCHECKED_CAST") - override fun self(): SELF = this as SELF - - /** Sets the [OkHttpClient] to use for the [BaseXapClient]. */ - fun okHttpClient(okHttpClient: OkHttpClient): SELF { - this.okHttpClient = okHttpClient - return self() - } - - } + abstract class BuilderWithHttpClient> : Client.BuilderWithHttpClient() } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt index d7ef4c7b2..6487953bc 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt @@ -51,6 +51,7 @@ import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.request import okhttp3.Dispatcher +import okhttp3.OkHttpClient // Create a Dispatcher with limits val dispatcher = Dispatcher().apply { @@ -309,12 +310,30 @@ abstract class Client( /** Create a [Client] object. */ abstract override fun build(): Client - } + + /** + * A builder class for configuring HTTP-related settings for a [Client] with the ability to pass a custom [OkHttpClient]. + * + * This builder class extends the base [Client.Builder] class and provides additional methods + * for setting a configured okhttp client. + * + * @param The type of the builder itself, used for method chaining. + */ + abstract class BuilderWithHttpClient> : Builder() { + protected var okHttpClient: OkHttpClient? = null + + @Suppress("UNCHECKED_CAST") + override fun self(): SELF = this as SELF + + /** Sets the [OkHttpClient] to use for the [Client]. */ + fun okHttpClient(okHttpClient: OkHttpClient): SELF { + this.okHttpClient = okHttpClient + return self() + } + } } /** Executes the hooks for the client. */ fun T.finalize() = Hooks.execute(this) - - diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ClientConfiguration.kt index d8dfdedd9..557db5ed6 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ClientConfiguration.kt @@ -16,6 +16,7 @@ package com.expediagroup.sdk.core.configuration import com.expediagroup.sdk.core.configuration.provider.RuntimeConfigurationProvider +import okhttp3.OkHttpClient interface ClientConfiguration { val key: String? @@ -26,6 +27,7 @@ interface ClientConfiguration { val socketTimeout: Long? val maskedLoggingHeaders: Set? val maskedLoggingBodyFields: Set? + val okHttpClient: OkHttpClient? /** Build a [RuntimeConfigurationProvider] from a [ClientConfiguration]. */ fun toProvider(): RuntimeConfigurationProvider = @@ -37,6 +39,7 @@ interface ClientConfiguration { connectionTimeout = connectionTimeout, socketTimeout = socketTimeout, maskedLoggingHeaders = maskedLoggingHeaders, - maskedLoggingBodyFields = maskedLoggingBodyFields + maskedLoggingBodyFields = maskedLoggingBodyFields, + okHttpClient = okHttpClient ) } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfiguration.kt index e5289d9e9..917604d76 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfiguration.kt @@ -17,6 +17,7 @@ package com.expediagroup.sdk.core.configuration import com.expediagroup.sdk.core.client.ExpediaGroupClient import com.expediagroup.sdk.core.configuration.provider.RuntimeConfigurationProvider +import okhttp3.OkHttpClient /** * Configuration for the [ExpediaGroupClient]. @@ -30,6 +31,7 @@ import com.expediagroup.sdk.core.configuration.provider.RuntimeConfigurationProv * @property maskedLoggingHeaders The headers to be masked in logging. * @property maskedLoggingBodyFields The body fields to be masked in logging. * @property authEndpoint The API endpoint to use for authentication. + * @property okHttpClient The okhttp client to be used by the sdk. */ data class ExpediaGroupClientConfiguration( override val key: String? = null, @@ -40,6 +42,7 @@ data class ExpediaGroupClientConfiguration( override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, override val maskedLoggingBodyFields: Set? = null, + override val okHttpClient: OkHttpClient? = null, val authEndpoint: String? = null ) : ClientConfiguration { /** Build a [RuntimeConfigurationProvider] from an [ExpediaGroupClientConfiguration]. */ diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt index 523d116f2..48a1cef94 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/RapidClientConfiguration.kt @@ -39,5 +39,5 @@ data class RapidClientConfiguration( override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, override val maskedLoggingBodyFields: Set? = null, - val okHttpClient: OkHttpClient? = null + override val okHttpClient: OkHttpClient? = null ) : ClientConfiguration diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt index f3ca5ad8f..ec72f80d7 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/XapClientConfiguration.kt @@ -39,5 +39,5 @@ data class XapClientConfiguration( override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, override val maskedLoggingBodyFields: Set? = null, - val okHttpClient: OkHttpClient? = null + override val okHttpClient: OkHttpClient? = null ) : ClientConfiguration diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/collector/ConfigurationCollector.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/collector/ConfigurationCollector.kt index a83d40fbe..49b674182 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/collector/ConfigurationCollector.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/collector/ConfigurationCollector.kt @@ -28,6 +28,7 @@ import com.expediagroup.sdk.core.constant.ConfigurationName.SECRET import com.expediagroup.sdk.core.constant.ConfigurationName.SOCKET_TIMEOUT_MILLIS import com.expediagroup.sdk.core.constant.provider.LoggingMessageProvider import com.expediagroup.sdk.core.plugin.logging.ExpediaGroupLoggerFactory +import okhttp3.OkHttpClient /** * Configuration collector that collects configuration from all available providers. @@ -66,6 +67,7 @@ internal class ConfigurationCollector private constructor(providers: Configurati override val socketTimeout: Long? = providers.firstWith { it.socketTimeout }.also { it?.log(SOCKET_TIMEOUT_MILLIS) }?.retrieve() override val maskedLoggingHeaders: Set? = providers.firstWith { it.maskedLoggingHeaders }.also { it?.log(MASKED_LOGGING_HEADERS) }?.retrieve() override val maskedLoggingBodyFields: Set? = providers.firstWith { it.maskedLoggingBodyFields }.also { it?.log(MASKED_LOGGING_BODY_FIELDS) }?.retrieve() + override val okHttpClient: OkHttpClient? = providers.firstWith { it.okHttpClient }?.retrieve() private fun ProvidedConfiguration.log(configurationName: String) { log.info(LoggingMessageProvider.getChosenProviderMessage(configurationName, providerName)) diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/ConfigurationProvider.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/ConfigurationProvider.kt index ac7f7ea43..0a14edd19 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/ConfigurationProvider.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/ConfigurationProvider.kt @@ -16,6 +16,7 @@ package com.expediagroup.sdk.core.configuration.provider import com.expediagroup.sdk.core.constant.Constant +import okhttp3.OkHttpClient /** * A configuration provider that can be used to provide configuration values. @@ -55,4 +56,8 @@ interface ConfigurationProvider { /** The body fields to be masked in logging.*/ val maskedLoggingBodyFields: Set? get() = setOf() + + /** The okhttp client to be used by the sdk.*/ + val okHttpClient: OkHttpClient? + get() = null } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/RuntimeConfigurationProvider.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/RuntimeConfigurationProvider.kt index fd72dc58e..d6e607e09 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/RuntimeConfigurationProvider.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/configuration/provider/RuntimeConfigurationProvider.kt @@ -16,6 +16,7 @@ package com.expediagroup.sdk.core.configuration.provider import com.expediagroup.sdk.core.constant.ConfigurationName.RUNTIME_CONFIGURATION_PROVIDER +import okhttp3.OkHttpClient /** * A runtime-built configuration provider. @@ -41,5 +42,6 @@ data class RuntimeConfigurationProvider( override val connectionTimeout: Long? = null, override val socketTimeout: Long? = null, override val maskedLoggingHeaders: Set? = null, - override val maskedLoggingBodyFields: Set? = null + override val maskedLoggingBodyFields: Set? = null, + override val okHttpClient: OkHttpClient? = null ) : ConfigurationProvider diff --git a/core/src/test/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfigurationTest.kt b/core/src/test/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfigurationTest.kt index 3310560e8..a01f7d5bb 100644 --- a/core/src/test/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfigurationTest.kt +++ b/core/src/test/kotlin/com/expediagroup/sdk/core/configuration/ExpediaGroupClientConfigurationTest.kt @@ -32,6 +32,7 @@ class ExpediaGroupClientConfigurationTest { assertNull(it.socketTimeout) assertNull(it.maskedLoggingHeaders) assertNull(it.maskedLoggingBodyFields) + assertNull(it.okHttpClient) } } @@ -72,6 +73,7 @@ class ExpediaGroupClientConfigurationTest { assertNull(it.socketTimeout) assertNull(it.maskedLoggingHeaders) assertNull(it.maskedLoggingBodyFields) + assertNull(it.okHttpClient) } } diff --git a/generator/openapi/src/main/resources/templates/expediagroup-sdk/client.mustache b/generator/openapi/src/main/resources/templates/expediagroup-sdk/client.mustache index 88b5a95e5..5ddbbc8fc 100644 --- a/generator/openapi/src/main/resources/templates/expediagroup-sdk/client.mustache +++ b/generator/openapi/src/main/resources/templates/expediagroup-sdk/client.mustache @@ -22,7 +22,7 @@ import kotlinx.coroutines.runBlocking class {{clientClassname}}Client private constructor(clientConfiguration: ExpediaGroupClientConfiguration) : ExpediaGroupClient("{{namespace}}", clientConfiguration) { class Builder : ExpediaGroupClient.Builder() { override fun build() = {{clientClassname}}Client( - ExpediaGroupClientConfiguration(key, secret, endpoint, requestTimeout, connectionTimeout, socketTimeout, maskedLoggingHeaders, maskedLoggingBodyFields, authEndpoint) + ExpediaGroupClientConfiguration(key, secret, endpoint, requestTimeout, connectionTimeout, socketTimeout, maskedLoggingHeaders, maskedLoggingBodyFields, null, authEndpoint) ) }