Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-14 18:26:12 +03:00
parent cba839d8f2
commit b776c208dd
3 changed files with 164 additions and 89 deletions

View file

@ -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<RequestHeader> = 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 <reified T> 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
}
}

View file

@ -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<ConfigModel>, onComplete: ((config: Config) -> Unit)?) = Unit
override fun turnOff(name: String) = Unit
override fun resetToDefault(name: String) = Unit
}

View file

@ -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<AppVersionProvider>()
private val expressAuthProvider = mockk<ExpressAuthProvider>()
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
// 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" },
),
),
)
}
}