From 61e4bd34fb063aa341acd0642a1036366fd20e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Fri, 20 Sep 2024 11:16:06 +0200 Subject: [PATCH] feat: add ability to pass customer defined okhttp client --- .../io/getunleash/android/UnleashConfig.kt | 13 +++++++-- .../getunleash/android/data/DataStrategy.kt | 5 +++- .../getunleash/android/http/ClientBuilder.kt | 5 +++- .../android/http/ClientBuilderTest.kt | 28 +++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/unleashandroidsdk/src/main/java/io/getunleash/android/UnleashConfig.kt b/unleashandroidsdk/src/main/java/io/getunleash/android/UnleashConfig.kt index eb26f54..09c14dd 100644 --- a/unleashandroidsdk/src/main/java/io/getunleash/android/UnleashConfig.kt +++ b/unleashandroidsdk/src/main/java/io/getunleash/android/UnleashConfig.kt @@ -2,6 +2,7 @@ package io.getunleash.android import io.getunleash.android.backup.LocalStorageConfig import io.getunleash.android.data.DataStrategy +import okhttp3.OkHttpClient import java.util.UUID /** @@ -11,6 +12,7 @@ import java.util.UUID * @property appName: name of the underlying application. Will be used as default in the [io.getunleash.android.data.UnleashContext] call (Required). * @property pollingStrategy How to poll for features. (Optional - Defaults to [io.getunleash.android.data.DataStrategy] with poll interval set to 60 seconds). * @property metricsStrategy How to poll for metrics. (Optional - Defaults to [io.getunleash.android.data.DataStrategy] with poll interval set to 60 seconds). + * @property httpClient Custom http client to be used. (Optional - Use if you want to pass in custom SSL certificates). */ data class UnleashConfig( val proxyUrl: String?, @@ -24,7 +26,8 @@ data class UnleashConfig( pauseOnBackground = true, ), val delayedInitialization: Boolean = true, - val forceImpressionData: Boolean = false + val forceImpressionData: Boolean = false, + val httpClient: OkHttpClient? = null, ) { companion object { val instanceId: String = UUID.randomUUID().toString() @@ -62,6 +65,7 @@ data class UnleashConfig( .newBuilder(parent = this) val localStorageConfig: LocalStorageConfig.Builder = LocalStorageConfig() .newBuilder(parent = this) + var httpClient: OkHttpClient? = null fun build(): UnleashConfig { if ((proxyUrl == null || clientKey == null) && (pollingStrategy.enabled || metricsStrategy.enabled)) { throw IllegalStateException("You must either set proxyUrl and clientKey or disable both polling and metrics.") @@ -74,7 +78,8 @@ data class UnleashConfig( metricsStrategy = metricsStrategy.build(), delayedInitialization = delayedInitialization, forceImpressionData = forceImpressionData, - localStorageConfig = localStorageConfig.build() + localStorageConfig = localStorageConfig.build(), + httpClient = httpClient ) } @@ -86,5 +91,9 @@ data class UnleashConfig( fun forceImpressionData(forceImpressionData: Boolean) = apply { this.forceImpressionData = forceImpressionData } + + fun httpClient(httpClient: OkHttpClient) = apply { + this.httpClient = httpClient + } } } diff --git a/unleashandroidsdk/src/main/java/io/getunleash/android/data/DataStrategy.kt b/unleashandroidsdk/src/main/java/io/getunleash/android/data/DataStrategy.kt index 19ca4b2..2ae4136 100644 --- a/unleashandroidsdk/src/main/java/io/getunleash/android/data/DataStrategy.kt +++ b/unleashandroidsdk/src/main/java/io/getunleash/android/data/DataStrategy.kt @@ -1,6 +1,7 @@ package io.getunleash.android.data import io.getunleash.android.UnleashConfig +import okhttp3.OkHttpClient /** * @property enabled Whether the strategy is enabled or not. (Optional - Defaults to true) @@ -8,6 +9,7 @@ import io.getunleash.android.UnleashConfig * @property delay How long to wait before starting the operation in milliseconds. (Optional - Defaults to 0) * @property pauseOnBackground Whether the operation should pause when the app is in background. (Optional - Defaults to true) * @property httpReadTimeout How long to wait for HTTP reads in milliseconds. (Optional - Defaults to 5000) + * @property httpWriteTimeout How long to wait for HTTP writes in milliseconds. (Optional - Defaults to 5000) * @property httpConnectionTimeout How long to wait for HTTP connection in milliseconds. (Optional - Defaults to 2000) * @property httpCacheSize Disk space (in bytes) set aside for http cache. (Optional - Defaults to 10MB) * @property httpCustomHeaders Enables users to override httpCustomHeaders. (Optional - Defaults to empty) @@ -17,8 +19,9 @@ data class DataStrategy( val interval: Long = 60000, val delay: Long = 0, val pauseOnBackground: Boolean = true, - val httpConnectionTimeout: Long = 2000, + val httpConnectionTimeout: Long = 5000, val httpReadTimeout: Long = 5000, + val httpWriteTimeout: Long = 5000, val httpCacheSize: Long = 1024 * 1024 * 10, val httpCustomHeaders: Map = emptyMap(), ) { diff --git a/unleashandroidsdk/src/main/java/io/getunleash/android/http/ClientBuilder.kt b/unleashandroidsdk/src/main/java/io/getunleash/android/http/ClientBuilder.kt index 7256682..5b75b05 100644 --- a/unleashandroidsdk/src/main/java/io/getunleash/android/http/ClientBuilder.kt +++ b/unleashandroidsdk/src/main/java/io/getunleash/android/http/ClientBuilder.kt @@ -13,8 +13,11 @@ class ClientBuilder(private val unleashConfig: UnleashConfig, private val androi clientName: String, strategy: DataStrategy ): OkHttpClient { - val builder = OkHttpClient.Builder() + // either use the provided client or create a new one + val clientBuilder = unleashConfig.httpClient?.newBuilder() ?: OkHttpClient.Builder() + val builder = clientBuilder .readTimeout(strategy.httpReadTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(strategy.httpWriteTimeout, TimeUnit.MILLISECONDS) .connectTimeout(strategy.httpConnectionTimeout, TimeUnit.MILLISECONDS) if (unleashConfig.localStorageConfig.enabled) { builder.cache( diff --git a/unleashandroidsdk/src/test/java/io/getunleash/android/http/ClientBuilderTest.kt b/unleashandroidsdk/src/test/java/io/getunleash/android/http/ClientBuilderTest.kt index c698e7a..a591c6d 100644 --- a/unleashandroidsdk/src/test/java/io/getunleash/android/http/ClientBuilderTest.kt +++ b/unleashandroidsdk/src/test/java/io/getunleash/android/http/ClientBuilderTest.kt @@ -3,9 +3,17 @@ package io.getunleash.android.http import android.content.Context import io.getunleash.android.BaseTest import io.getunleash.android.UnleashConfig +import okhttp3.OkHttpClient import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import java.util.concurrent.TimeUnit import kotlin.io.path.createTempDirectory class ClientBuilderTest : BaseTest() { @@ -38,4 +46,24 @@ class ClientBuilderTest : BaseTest() { assertThat(client.cache).isNull() } + + @Test + fun `when http client is provided by customer we should use it`() { + val builder = spy(OkHttpClient.Builder()) + val customHttpClient = mock(OkHttpClient::class.java) + `when`(customHttpClient.newBuilder()).thenReturn(builder) + val config = + UnleashConfig.newBuilder("my-app") + .proxyUrl("https://localhost:4242/proxy") + .httpClient(customHttpClient) + .localStorageConfig.enabled(false) + .clientKey("some-key").build() + + val clientBuilder = ClientBuilder(config, mock(Context::class.java)) + clientBuilder.build("clientName", config.pollingStrategy) + + verify(builder).readTimeout(5000, TimeUnit.MILLISECONDS) + verify(builder).writeTimeout(5000, TimeUnit.MILLISECONDS) + verify(builder).connectTimeout(5000, TimeUnit.MILLISECONDS) + } } \ No newline at end of file