From b776c208dda6c942194da26d663b02502aa891b0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 Aug 2024 18:26:12 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/datasource/di/NetworkModule.kt | 108 +++++++--------- .../config/managers/MockConfigManager.kt | 24 ++++ .../managers/ProdApiConfigsManagerTest.kt | 121 +++++++++++++----- 3 files changed, 164 insertions(+), 89 deletions(-) create mode 100644 core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index a393f14ee7..b43900233f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -32,7 +32,11 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -class NetworkModule { +internal object NetworkModule { + + private const val DEV_V1_TANGEM_TECH_BASE_URL = "https://devapi.tangem-tech.com/v1/" + private const val PROD_V2_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v2/" + private const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L @Provides @Singleton @@ -55,20 +59,12 @@ class NetworkModule { @ApplicationContext context: Context, apiConfigsManager: ApiConfigsManager, ): TangemExpressApi { - val environmentConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.Express) - - return Retrofit.Builder() - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) - .baseUrl(environmentConfig.baseUrl) - .client( - OkHttpClient.Builder() - .applyApiConfig(ApiConfig.ID.Express, apiConfigsManager) - .addLoggers(context) - .build(), - ) - .build() - .create(TangemExpressApi::class.java) + return createApi( + id = ApiConfig.ID.Express, + moshi = moshi, + context = context, + apiConfigsManager = apiConfigsManager, + ) } @Provides @@ -78,20 +74,12 @@ class NetworkModule { @ApplicationContext context: Context, apiConfigsManager: ApiConfigsManager, ): StakeKitApi { - val environmentConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.StakeKit) - - return Retrofit.Builder() - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) - .baseUrl(environmentConfig.baseUrl) - .client( - OkHttpClient.Builder() - .applyApiConfig(id = ApiConfig.ID.StakeKit, apiConfigsManager = apiConfigsManager) - .addLoggers(context) - .build(), - ) - .build() - .create(StakeKitApi::class.java) + return createApi( + id = ApiConfig.ID.StakeKit, + moshi = moshi, + context = context, + apiConfigsManager = apiConfigsManager, + ) } @Provides @@ -101,36 +89,26 @@ class NetworkModule { @ApplicationContext context: Context, apiConfigsManager: ApiConfigsManager, ): TangemTechApi { - val environmentConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech) - - return Retrofit.Builder() - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) - .baseUrl(environmentConfig.baseUrl) - .client( - OkHttpClient.Builder() - .applyApiConfig(id = ApiConfig.ID.TangemTech, apiConfigsManager = apiConfigsManager) - .applyTimeoutAnnotations() - .addLoggers(context) - .build(), - ) - .build() - .create(TangemTechApi::class.java) + return createApi( + id = ApiConfig.ID.TangemTech, + moshi = moshi, + context = context, + apiConfigsManager = apiConfigsManager, + ) } + // TODO: It will be deleted in the future or refactored using ApiConfig @Provides @Singleton fun provideTangemTechApiV2( @NetworkMoshi moshi: Moshi, @ApplicationContext context: Context, appVersionProvider: AppVersionProvider, - apiConfigsManager: ApiConfigsManager, ): TangemTechApiV2 { return provideTangemTechApiInternal( moshi = moshi, context = context, appVersionProvider = appVersionProvider, - apiConfigsManager = apiConfigsManager, baseUrl = PROD_V2_TANGEM_TECH_BASE_URL, ) } @@ -142,17 +120,16 @@ class NetworkModule { @NetworkMoshi moshi: Moshi, @ApplicationContext context: Context, appVersionProvider: AppVersionProvider, - apiConfigsManager: ApiConfigsManager, ): TangemTechApi { return provideTangemTechApiInternal( moshi = moshi, context = context, appVersionProvider = appVersionProvider, - apiConfigsManager = apiConfigsManager, baseUrl = DEV_V1_TANGEM_TECH_BASE_URL, ) } + // TODO: [REDACTED_JIRA] @Provides @DevTangemApi @Singleton @@ -160,13 +137,11 @@ class NetworkModule { @NetworkMoshi moshi: Moshi, @ApplicationContext context: Context, appVersionProvider: AppVersionProvider, - apiConfigsManager: ApiConfigsManager, ): TangemTechMarketsApi { return provideTangemTechApiInternal( moshi = moshi, context = context, appVersionProvider = appVersionProvider, - apiConfigsManager = apiConfigsManager, baseUrl = DEV_V1_TANGEM_TECH_BASE_URL, timeouts = Timeouts( callTimeoutSeconds = TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS, @@ -181,13 +156,11 @@ class NetworkModule { moshi: Moshi, context: Context, appVersionProvider: AppVersionProvider, - apiConfigsManager: ApiConfigsManager, baseUrl: String, timeouts: Timeouts = Timeouts(), requestHeaders: List = listOf(AppVersionPlatformHeaders(appVersionProvider)), ): T { val client = OkHttpClient.Builder() - .applyApiConfig(id = ApiConfig.ID.TangemTech, apiConfigsManager = apiConfigsManager) .applyTimeoutAnnotations() .let { builder -> var b = builder @@ -222,18 +195,33 @@ class NetworkModule { .create(T::class.java) } + private inline fun createApi( + id: ApiConfig.ID, + moshi: Moshi, + context: Context, + apiConfigsManager: ApiConfigsManager, + ): T { + val environmentConfig = apiConfigsManager.getEnvironmentConfig(id) + + return Retrofit.Builder() + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) + .baseUrl(environmentConfig.baseUrl) + .client( + OkHttpClient.Builder() + .applyApiConfig(id, apiConfigsManager) + .applyTimeoutAnnotations() + .addLoggers(context) + .build(), + ) + .build() + .create(T::class.java) + } + private data class Timeouts( val callTimeoutSeconds: Long? = null, val connectTimeoutSeconds: Long? = null, val readTimeoutSeconds: Long? = null, val writeTimeoutSeconds: Long? = null, ) - - private companion object { - const val DEV_V1_TANGEM_TECH_BASE_URL = "https://devapi.tangem-tech.com/v1/" - - const val PROD_V2_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v2/" - - const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L - } } \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt new file mode 100644 index 0000000000..4e81af810b --- /dev/null +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt @@ -0,0 +1,24 @@ +package com.tangem.datasource.api.common.config.managers + +import com.tangem.datasource.config.ConfigManager +import com.tangem.datasource.config.Loader +import com.tangem.datasource.config.models.Config +import com.tangem.datasource.config.models.ConfigModel +import com.tangem.datasource.config.models.ExpressModel + +/** + * Mock [ConfigManager] implementation for [ProdApiConfigsManagerTest] + * +[REDACTED_AUTHOR] + */ +internal class MockConfigManager : ConfigManager { + + override val config = Config( + express = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_API_KEY, signVerifierPublicKey = ""), + devExpress = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_DEV_API_KEY, signVerifierPublicKey = ""), + ) + + override suspend fun load(configLoader: Loader, onComplete: ((config: Config) -> Unit)?) = Unit + override fun turnOff(name: String) = Unit + override fun resetToDefault(name: String) = Unit +} \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt index f5b88829a3..d818bd6c9f 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt @@ -2,20 +2,35 @@ package com.tangem.datasource.api.common.config.managers import com.google.common.truth.Truth import com.tangem.datasource.BuildConfig -import com.tangem.datasource.api.common.config.ApiConfig +import com.tangem.datasource.api.common.config.* import com.tangem.datasource.api.common.config.ApiConfig.Companion.DEBUG_BUILD_TYPE import com.tangem.datasource.api.common.config.ApiConfig.Companion.EXTERNAL_BUILD_TYPE import com.tangem.datasource.api.common.config.ApiConfig.Companion.INTERNAL_BUILD_TYPE import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE -import com.tangem.datasource.api.common.config.Express -import com.tangem.datasource.api.common.config.StakeKit -import com.tangem.datasource.api.common.config.TangemTech +import com.tangem.lib.auth.ExpressAuthProvider +import com.tangem.lib.auth.StakeKitAuthProvider +import com.tangem.utils.Provider +import com.tangem.utils.version.AppVersionProvider +import io.mockk.every import io.mockk.mockk +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +private val configManager = MockConfigManager() +private val appVersionProvider = mockk() +private val expressAuthProvider = mockk() +private val stakeKitAuthProvider = mockk() + +// Don't forget to add new config !!! +private val API_CONFIGS = setOf( + Express(configManager, expressAuthProvider, appVersionProvider), + TangemTech(appVersionProvider), + StakeKit(stakeKitAuthProvider), +) + /** [REDACTED_AUTHOR] */ @@ -24,24 +39,35 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { private val manager = ProdApiConfigsManager(API_CONFIGS) - @Test - fun test_getBaseUrl() { - val actual = manager.getEnvironmentConfig(id = model.id).baseUrl - - Truth.assertThat(actual).isEqualTo(model.expected) + @Before + fun setup() { + every { appVersionProvider.versionName } returns VERSION_NAME + every { expressAuthProvider.getUserId() } returns EXPRESS_USER_ID + every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID + every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY } - data class Model(val id: ApiConfig.ID, val expected: String) + @Test + fun test_getEnvironmentConfig() { + val actual = manager.getEnvironmentConfig(id = model.id) - private companion object { + Truth.assertThat(actual.environment).isEqualTo(model.expected.environment) + Truth.assertThat(actual.baseUrl).isEqualTo(model.expected.baseUrl) - val API_CONFIGS = setOf( - Express(mockk(), mockk(), mockk()), - TangemTech(mockk()), - StakeKit(mockk()), + Truth.assertThat(actual.headers.mapValues { it.value() }) + .isEqualTo(model.expected.headers.mapValues { it.value() }) + } - // Don't forget to add new config - ) + data class Model(val id: ApiConfig.ID, val expected: ApiEnvironmentConfig) + + internal companion object { + + const val VERSION_NAME = "debug" + const val EXPRESS_USER_ID = "express_user_id" + const val EXPRESS_SESSION_ID = "express_session_id" + const val EXPRESS_API_KEY = "express_api_key" + const val EXPRESS_DEV_API_KEY = "express_dev_api_key" + const val STAKE_KIT_API_KEY = "stake_kit_api_key" @JvmStatic @Parameterized.Parameters @@ -54,32 +80,69 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { } private fun createExpressModel(): Model { + val environment = when (BuildConfig.BUILD_TYPE) { + DEBUG_BUILD_TYPE -> ApiEnvironment.DEV + INTERNAL_BUILD_TYPE, + MOCKED_BUILD_TYPE, + -> ApiEnvironment.STAGE + EXTERNAL_BUILD_TYPE, + RELEASE_BUILD_TYPE, + -> ApiEnvironment.PROD + else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]") + } + return Model( id = ApiConfig.ID.Express, - expected = when (BuildConfig.BUILD_TYPE) { - DEBUG_BUILD_TYPE -> "[REDACTED_ENV_URL]" - INTERNAL_BUILD_TYPE, - MOCKED_BUILD_TYPE, - -> "[REDACTED_ENV_URL]" - EXTERNAL_BUILD_TYPE, - RELEASE_BUILD_TYPE, - -> "https://express.tangem.com/v1/" - else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]") - }, + expected = ApiEnvironmentConfig( + environment = environment, + baseUrl = when (BuildConfig.BUILD_TYPE) { + DEBUG_BUILD_TYPE -> "[REDACTED_ENV_URL]" + INTERNAL_BUILD_TYPE, + MOCKED_BUILD_TYPE, + -> "[REDACTED_ENV_URL]" + EXTERNAL_BUILD_TYPE, + RELEASE_BUILD_TYPE, + -> "https://express.tangem.com/v1/" + else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]") + }, + headers = mapOf( + "api-key" to Provider { + if (environment == ApiEnvironment.PROD) EXPRESS_API_KEY else EXPRESS_DEV_API_KEY + }, + "user-id" to Provider { EXPRESS_USER_ID }, + "session-id" to Provider { EXPRESS_SESSION_ID }, + "version" to Provider { VERSION_NAME }, + "platform" to Provider { "android" }, + ), + ), ) } private fun createTangemTechModel(): Model { return Model( id = ApiConfig.ID.TangemTech, - expected = "https://api.tangem-tech.com/v1/", + expected = ApiEnvironmentConfig( + environment = ApiEnvironment.PROD, + baseUrl = "https://api.tangem-tech.com/v1/", + headers = mapOf( + "version" to Provider { VERSION_NAME }, + "platform" to Provider { "android" }, + ), + ), ) } private fun createStakeKitModel(): Model { return Model( id = ApiConfig.ID.StakeKit, - expected = "https://api.stakek.it/v1/", + expected = ApiEnvironmentConfig( + environment = ApiEnvironment.PROD, + baseUrl = "https://api.stakek.it/v1/", + headers = mapOf( + "X-API-KEY" to Provider { STAKE_KIT_API_KEY }, + "accept" to Provider { "application/json" }, + ), + ), ) } }