From aa23ec76022d80455ccc7bebf3aee9dc538da261 Mon Sep 17 00:00:00 2001 From: Gabriel Moro Date: Sun, 29 Mar 2026 15:33:29 -0300 Subject: [PATCH 1/4] Refactor HttpClient creation into a builder and simplify NetworkModule - Create `HttpClientBuilder` to centralize Ktor `HttpClient` configuration, including negotiation, timeouts, authentication, and error validation. - Refactor `NetworkModule` to use `HttpClientBuilder` and remove redundant host/profile qualifiers. - Clean up unused interceptor qualifiers in `QualifierNetworking.kt`. --- .../core_networking/HttpClientBuilder.kt | 94 ++++++++++++++++ .../core_networking/di/NetworkModule.kt | 104 +----------------- .../core_networking/di/QualifierNetworking.kt | 10 -- 3 files changed, 99 insertions(+), 109 deletions(-) create mode 100644 core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientBuilder.kt diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientBuilder.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientBuilder.kt new file mode 100644 index 00000000..40d86e73 --- /dev/null +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientBuilder.kt @@ -0,0 +1,94 @@ +package com.codandotv.streamplayerapp.core_networking + +import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT +import com.codandotv.streamplayerapp.core_networking.handleError.Failure +import core.networking.BuildKonfig +import io.ktor.client.HttpClient +import io.ktor.client.plugins.ClientRequestException +import io.ktor.client.plugins.HttpResponseValidator +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.auth.Auth +import io.ktor.client.plugins.auth.providers.BearerTokens +import io.ktor.client.plugins.auth.providers.bearer +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.accept +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.io.IOException +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json + +internal object HttpClientBuilder { + fun build(baseUrl: String) : HttpClient { + return HttpClient(engine = httpClientEnginePlatform()) { + expectSuccess = false + + install(ContentNegotiation) { + json(Json { + explicitNulls = false + ignoreUnknownKeys = true + }) + } + + install(HttpTimeout) { + socketTimeoutMillis = TIMEOUT + requestTimeoutMillis = TIMEOUT + connectTimeoutMillis = TIMEOUT + } + + defaultRequest { + url(baseUrl) + contentType(ContentType.Application.Json) + accept(ContentType.Application.Json) + } + + install(Auth) { + bearer { + loadTokens { + BearerTokens( + accessToken = BuildKonfig.API_BEARER_AUTH, + refreshToken = "" + ) + } + } + } + + install(Logging) { + level = LogLevel.ALL + logger = object : Logger { + override fun log(message: String) { + //TODO: Migrar Logs para Utilizar Kermit + println("HttpClient${message}") + } + } + } + + HttpResponseValidator { + handleResponseExceptionWithRequest { cause, _ -> + throw when (cause) { + is ClientRequestException -> { + Failure.ServerError(codeStatus = cause.response.status.value) + } + + is SerializationException -> { + Failure.UnparsableResponseException(throwable = cause) + } + + is IOException -> { + Failure.NetworkError(throwable = cause) + } + + else -> { + Failure.UnknownError(throwable = cause) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index b4c07c92..a4af3e97 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -1,117 +1,23 @@ package com.codandotv.streamplayerapp.core_networking.di -import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT -import com.codandotv.streamplayerapp.core_networking.handleError.Failure -import com.codandotv.streamplayerapp.core_networking.httpClientEnginePlatform +import com.codandotv.streamplayerapp.core_networking.HttpClientBuilder import core.networking.BuildKonfig -import io.ktor.client.HttpClient -import io.ktor.client.plugins.ClientRequestException -import io.ktor.client.plugins.HttpResponseValidator -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.auth.Auth -import io.ktor.client.plugins.auth.providers.BearerTokens -import io.ktor.client.plugins.auth.providers.bearer -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.defaultRequest -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.client.request.accept -import io.ktor.http.ContentType -import io.ktor.http.contentType -import io.ktor.serialization.kotlinx.json.json -import kotlinx.io.IOException -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json import org.koin.dsl.module object NetworkModule { val module = module { - single(QualifierHost) { BuildKonfig.HOST } - single(QualifierProfile) { BuildKonfig.PROFILE } - single { - provideKtorHttpClient( - baseUrl = get(QualifierHost), + HttpClientBuilder.build( + baseUrl = BuildKonfig.HOST, ) } single(QualifierProfileHttpClient) { - provideKtorHttpClient( - baseUrl = get(QualifierProfile), + HttpClientBuilder.build( + baseUrl = BuildKonfig.PROFILE, ) } } - - private fun provideKtorHttpClient( - baseUrl: String, - ): HttpClient { - return HttpClient(engine = httpClientEnginePlatform()) { - expectSuccess = false - - install(ContentNegotiation) { - json(Json { - explicitNulls = false - ignoreUnknownKeys = true - }) - } - - install(HttpTimeout) { - socketTimeoutMillis = TIMEOUT - requestTimeoutMillis = TIMEOUT - connectTimeoutMillis = TIMEOUT - } - - defaultRequest { - url(baseUrl) - contentType(ContentType.Application.Json) - accept(ContentType.Application.Json) - } - - install(Auth) { - bearer { - loadTokens { - BearerTokens( - accessToken = BuildKonfig.API_BEARER_AUTH, - refreshToken = "" - ) - } - } - } - - install(Logging) { - level = LogLevel.ALL - logger = object : Logger { - override fun log(message: String) { - //TODO: Migrar Logs para Utilizar Kermit - println("HttpClient${message}") - } - } - } - - HttpResponseValidator { - handleResponseExceptionWithRequest { cause, _ -> - throw when (cause) { - is ClientRequestException -> { - Failure.ServerError(codeStatus = cause.response.status.value) - } - - is SerializationException -> { - Failure.UnparsableResponseException(throwable = cause) - } - - is IOException -> { - Failure.NetworkError(throwable = cause) - } - - else -> { - Failure.UnknownError(throwable = cause) - } - } - } - } - } - } } internal object Network { diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt index b21a5e27..f9c5afb0 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt @@ -18,13 +18,3 @@ object QualifierProfileHttpClient : Qualifier { get() = "QualifierProfileRetrofit" } -object QualifierLoggerInterceptor : Qualifier { - override val value: QualifierValue - get() = "QualifierLoggerInterceptor" -} - -object QualifierAuthInterceptor : Qualifier { - override val value: QualifierValue - get() = "QualifierAuthInterceptor" -} - From 7941e0b4bfdc195c7ec225f72dd51410535ff892 Mon Sep 17 00:00:00 2001 From: Gabriel Moro Date: Sun, 29 Mar 2026 15:49:10 -0300 Subject: [PATCH 2/4] Add Koin Annotations to core-networking and refactor NetworkModule - Apply `koin-annotations-setup` plugin to `core-networking` module. - Refactor `NetworkModule` from a DSL-based object to an annotated class using `@Module`, `@Single`, and `@Qualifier`. - Update `AppModule` to instantiate the generated `NetworkModule` class. --- .../di/AppModule.kt | 3 +- core-networking/build.gradle.kts | 1 + .../core_networking/di/NetworkModule.kt | 32 +++++++++++-------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt index 8267f699..1a833be2 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt @@ -8,10 +8,11 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModu import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import org.koin.dsl.module +import org.koin.ksp.generated.module object AppModule { private val module = module { single(QualifierDispatcherIO) { Dispatchers.IO } } - val list = module + NetworkModule.module + LocalStorageModule.module + SyncModule.module + ListStreamModule.module + val list = module + NetworkModule().module + LocalStorageModule.module + SyncModule.module + ListStreamModule.module } \ No newline at end of file diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index 4f28c950..c9c6e98f 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) alias(libs.plugins.buildkonfig.plugin) + id("com.streamplayer.koin-annotations-setup") } buildkonfig { diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index a4af3e97..0dcef65e 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -2,21 +2,27 @@ package com.codandotv.streamplayerapp.core_networking.di import com.codandotv.streamplayerapp.core_networking.HttpClientBuilder import core.networking.BuildKonfig -import org.koin.dsl.module +import io.ktor.client.HttpClient +import org.koin.core.annotation.Module +import org.koin.core.annotation.Qualifier +import org.koin.core.annotation.Single -object NetworkModule { - val module = module { - single { - HttpClientBuilder.build( - baseUrl = BuildKonfig.HOST, - ) - } +@Module +class NetworkModule { - single(QualifierProfileHttpClient) { - HttpClientBuilder.build( - baseUrl = BuildKonfig.PROFILE, - ) - } + @Single + fun provideHttpClient() : HttpClient { + return HttpClientBuilder.build( + baseUrl = BuildKonfig.HOST, + ) + } + + @Single + @Qualifier(QualifierProfileHttpClient::class) + fun provideProfileHttpClient(): HttpClient { + return HttpClientBuilder.build( + baseUrl = BuildKonfig.PROFILE, + ) } } From 45f800487f0e7df52bc536122fe58b0d24115bc6 Mon Sep 17 00:00:00 2001 From: Gabriel Moro Date: Sun, 29 Mar 2026 16:18:08 -0300 Subject: [PATCH 3/4] Remove unused networking qualifiers - Remove `QualifierHost` and `QualifierProfile` from `QualifierNetworking.kt`. --- .../core_networking/di/QualifierNetworking.kt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt index f9c5afb0..f5c85fdf 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt @@ -3,16 +3,6 @@ package com.codandotv.streamplayerapp.core_networking.di import org.koin.core.qualifier.Qualifier import org.koin.core.qualifier.QualifierValue -object QualifierHost : Qualifier { - override val value: QualifierValue - get() = "QualifierHost" -} - -object QualifierProfile : Qualifier { - override val value: QualifierValue - get() = "QualifierProfile" -} - object QualifierProfileHttpClient : Qualifier { override val value: QualifierValue get() = "QualifierProfileRetrofit" From 5cd7b77ca6211292820eda3209728413a95f90a7 Mon Sep 17 00:00:00 2001 From: Gabriel Moro Date: Sun, 29 Mar 2026 17:05:06 -0300 Subject: [PATCH 4/4] Update versionName to 1.3 --- build-logic/src/main/java/Config.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 0617bf47..6c70860f 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -9,7 +9,7 @@ object Config { const val compileSdkVersion = 36 const val minSdkVersion = 28 const val targetSdkVersion = 36 - const val versionName = "1.2" + const val versionName = "1.3" const val versionCode = 1 const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"