From 1d7416c58f020401993aee9ec5f7891299ece3c7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 Aug 2024 13:37:27 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../api/common/config/TangemTech.kt | 15 ++++- .../api/tangemTech/TangemTechApi.kt | 26 +++++---- .../api/tangemTech/TangemTechServiceApi.kt | 15 ----- .../api/utils/TimeountAnnotations.kt | 39 +++++++++++++ .../tangem/datasource/di/ApiConfigsModule.kt | 2 +- .../com/tangem/datasource/di/NetworkModule.kt | 58 +++++++------------ .../tangem/datasource/utils/HttpClientExt.kt | 32 ++++++++++ .../managers/ProdApiConfigsManagerTest.kt | 2 +- .../manager/ProdFeatureTogglesManagerTest.kt | 10 +--- .../BlockchainProvidersResponseLoader.kt | 4 +- .../BlockchainProvidersResponseLoaderTest.kt | 4 +- 11 files changed, 126 insertions(+), 81 deletions(-) delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechServiceApi.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/utils/TimeountAnnotations.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt index f34ffb3590..c93ce9af1e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt @@ -1,7 +1,12 @@ package com.tangem.datasource.api.common.config +import com.tangem.datasource.utils.RequestHeader +import com.tangem.utils.version.AppVersionProvider + /** TangemTech [ApiConfig] */ -internal class TangemTech : ApiConfig() { +internal class TangemTech( + private val appVersionProvider: AppVersionProvider, +) : ApiConfig() { override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD @@ -13,12 +18,16 @@ internal class TangemTech : ApiConfig() { private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.PROD, baseUrl = "https://api.tangem-tech.com/v1/", - headers = emptyMap(), // TODO: [REDACTED_JIRA] + headers = createHeaders(), ) private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.DEV, baseUrl = "https://devapi.tangem-tech.com/v1/", - headers = emptyMap(), // TODO: [REDACTED_JIRA] + headers = createHeaders(), ) + + private fun createHeaders() = buildMap { + putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider).values) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index 9fe41bf3a5..23edacd61b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -3,7 +3,10 @@ package com.tangem.datasource.api.tangemTech import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.promotion.models.PromotionInfoResponse import com.tangem.datasource.api.tangemTech.models.* +import com.tangem.datasource.api.utils.ReadTimeout +import com.tangem.datasource.config.models.ProviderModel import retrofit2.http.* +import java.util.concurrent.TimeUnit /** * Interface of Tangem Tech API @@ -15,6 +18,7 @@ interface TangemTechApi { @GET("coins") suspend fun getCoins( + @Header("Cache-Control") cacheControl: String = "max-age=600", @Query("contractAddress") contractAddress: String? = null, @Query("exchangeable") exchangeable: Boolean? = null, @Query("networkIds") networkIds: String? = null, @@ -29,7 +33,9 @@ interface TangemTechApi { suspend fun getRates(@Query("currencyId") currencyId: String, @Query("coinIds") coinIds: String): RatesResponse @GET("currencies") - suspend fun getCurrencyList(): ApiResponse + suspend fun getCurrencyList( + @Header("Cache-Control") cacheControl: String = "max-age=600", + ): ApiResponse @GET("geo") suspend fun getUserCountryCode(): GeoResponse @@ -59,15 +65,6 @@ interface TangemTechApi { @Body startReferralBody: StartReferralBody, ): ReferralResponse - @GET("shops") - suspend fun getShopInfo(@Query(value = "name") name: String): ShopResponse - - @GET("sales") - suspend fun getSalesInfo( - @Query(value = "locale") locale: String, - @Query(value = "shops") shops: String, - ): SalesResponse - @GET("quotes") suspend fun getQuotes( @Query("currencyId") currencyId: String, @@ -76,7 +73,10 @@ interface TangemTechApi { ): ApiResponse @GET("promotion") - suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse + suspend fun getPromotionInfo( + @Query("programName") name: String, + @Header("Cache-Control") cacheControl: String = "max-age=600", + ): ApiResponse @GET("settings/{wallet_id}") suspend fun getUserTokensSettings( @@ -131,4 +131,8 @@ interface TangemTechApi { @GET("features") suspend fun getFeatures(): ApiResponse + + @ReadTimeout(duration = 5, unit = TimeUnit.SECONDS) + @GET("networks/providers") + suspend fun getBlockchainProviders(): Map> } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechServiceApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechServiceApi.kt deleted file mode 100644 index 7bd52cc1e2..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechServiceApi.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.datasource.api.tangemTech - -import com.tangem.datasource.config.models.ProviderModel -import retrofit2.http.GET - -/** - * Tangem Tech API for app services - * -[REDACTED_AUTHOR] - */ -interface TangemTechServiceApi { - - @GET("networks/providers") - suspend fun getBlockchainProviders(): Map> -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/utils/TimeountAnnotations.kt b/core/datasource/src/main/java/com/tangem/datasource/api/utils/TimeountAnnotations.kt new file mode 100644 index 0000000000..3e521944cd --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/utils/TimeountAnnotations.kt @@ -0,0 +1,39 @@ +package com.tangem.datasource.api.utils + +import java.util.concurrent.TimeUnit + +/** + * Set connect timeout of request + * + * @property duration duration + * @property unit unit + * + * @see "HttpClientExt.applyTimeoutAnnotations" + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +internal annotation class ConnectTimeout(val duration: Int, val unit: TimeUnit) + +/** + * Set read timeout of request + * + * @property duration duration + * @property unit unit + * + * @see "HttpClientExt.applyTimeoutAnnotations" + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +internal annotation class ReadTimeout(val duration: Int, val unit: TimeUnit) + +/** + * Set write timeout of request + * + * @property duration duration + * @property unit unit + * + * @see "HttpClientExt.applyTimeoutAnnotations" + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +internal annotation class WriteTimeout(val duration: Int, val unit: TimeUnit) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt index 9959cd7ab7..f144cc7450 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt @@ -28,5 +28,5 @@ internal object ApiConfigsModule { @Provides @IntoSet - fun provideTangemTechConfig(): ApiConfig = TangemTech() + fun provideTangemTechConfig(appVersionProvider: AppVersionProvider): ApiConfig = TangemTech(appVersionProvider) } \ No newline at end of file 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 8ab0db4010..f57577e4a5 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 @@ -14,13 +14,10 @@ import com.tangem.datasource.api.markets.TangemTechMarketsApi import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechApiV2 -import com.tangem.datasource.api.tangemTech.TangemTechServiceApi import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.datasource.utils.RequestHeader -import com.tangem.datasource.utils.RequestHeader.* -import com.tangem.datasource.utils.addEnvironmentSwitcher -import com.tangem.datasource.utils.addHeaders -import com.tangem.datasource.utils.addLoggers +import com.tangem.datasource.utils.* +import com.tangem.datasource.utils.RequestHeader.AppVersionPlatformHeaders +import com.tangem.datasource.utils.RequestHeader.StakeKit import com.tangem.lib.auth.StakeKitAuthProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.version.AppVersionProvider @@ -103,16 +100,24 @@ class NetworkModule { fun provideTangemTechApi( @NetworkMoshi moshi: Moshi, @ApplicationContext context: Context, - appVersionProvider: AppVersionProvider, apiConfigsManager: ApiConfigsManager, ): TangemTechApi { - return provideTangemTechApiInternal( - moshi = moshi, - context = context, - appVersionProvider = appVersionProvider, - apiConfigsManager = apiConfigsManager, - baseUrl = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech).baseUrl, - ) + val environmentConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech) + + return Retrofit.Builder() + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) + .baseUrl(environmentConfig.baseUrl) + .client( + OkHttpClient.Builder() + .addEnvironmentSwitcher(id = ApiConfig.ID.TangemTech, apiConfigsManager = apiConfigsManager) + .addHeaders(environmentConfig.headers) + .applyTimeoutAnnotations() + .addLoggers(context) + .build(), + ) + .build() + .create(TangemTechApi::class.java) } @Provides @@ -150,27 +155,6 @@ class NetworkModule { ) } - @Provides - @Singleton - fun provideTangemTechServiceApi( - @NetworkMoshi moshi: Moshi, - @ApplicationContext context: Context, - appVersionProvider: AppVersionProvider, - apiConfigsManager: ApiConfigsManager, - ): TangemTechServiceApi { - return provideTangemTechApiInternal( - moshi = moshi, - context = context, - appVersionProvider = appVersionProvider, - apiConfigsManager = apiConfigsManager, - baseUrl = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech).baseUrl, - timeouts = Timeouts( - callTimeoutSeconds = TANGEM_TECH_SERVICE_TIMEOUT_SECONDS, - ), - requestHeaders = listOf(AppVersionPlatformHeaders(appVersionProvider)), - ) - } - @Provides @DevTangemApi @Singleton @@ -202,10 +186,11 @@ class NetworkModule { apiConfigsManager: ApiConfigsManager, baseUrl: String, timeouts: Timeouts = Timeouts(), - requestHeaders: List = listOf(CacheControlHeader, AppVersionPlatformHeaders(appVersionProvider)), + requestHeaders: List = listOf(AppVersionPlatformHeaders(appVersionProvider)), ): T { val client = OkHttpClient.Builder() .addEnvironmentSwitcher(id = ApiConfig.ID.TangemTech, apiConfigsManager = apiConfigsManager) + .applyTimeoutAnnotations() .let { builder -> var b = builder if (timeouts.callTimeoutSeconds != null) { @@ -253,7 +238,6 @@ class NetworkModule { const val PROD_V2_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v2/" - const val TANGEM_TECH_SERVICE_TIMEOUT_SECONDS = 5L const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt index 09387b4279..f0812aa21c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt @@ -7,9 +7,13 @@ import com.tangem.datasource.api.common.SwitchEnvironmentInterceptor import com.tangem.datasource.api.common.config.ApiConfig import com.tangem.datasource.api.common.config.managers.ApiConfigsManager import com.tangem.datasource.api.common.createNetworkLoggingInterceptor +import com.tangem.datasource.api.utils.ConnectTimeout +import com.tangem.datasource.api.utils.ReadTimeout +import com.tangem.datasource.api.utils.WriteTimeout import com.tangem.utils.Provider import okhttp3.Interceptor import okhttp3.OkHttpClient +import retrofit2.Invocation /** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeader): OkHttpClient.Builder { @@ -26,6 +30,34 @@ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeade ) } +/** + * Apply timeout annotations [Interceptor]. + * Add this [Interceptor] to [OkHttpClient] if use timeout annotations for retrofit requests. + */ +internal fun OkHttpClient.Builder.applyTimeoutAnnotations(): OkHttpClient.Builder { + return addInterceptor( + Interceptor { chain -> + val request = chain.request() + val tag = request.tag(Invocation::class.java) + val connectionTimeout = tag?.method()?.getAnnotation(ConnectTimeout::class.java) + val readTimeout = tag?.method()?.getAnnotation(ReadTimeout::class.java) + val writeTimeout = tag?.method()?.getAnnotation(WriteTimeout::class.java) + + chain + .apply { + connectionTimeout?.let { withConnectTimeout(timeout = it.duration, unit = it.unit) } + } + .apply { + readTimeout?.let { withReadTimeout(timeout = it.duration, unit = it.unit) } + } + .apply { + writeTimeout?.let { withWriteTimeout(timeout = it.duration, unit = it.unit) } + } + .proceed(request) + }, + ) +} + /** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ internal fun OkHttpClient.Builder.addHeaders(requestHeaders: Map>): OkHttpClient.Builder { return addInterceptor( 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 135ca3e209..8023906e1c 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 @@ -36,7 +36,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { val API_CONFIGS = setOf( Express(mockk(), mockk(), mockk()), - TangemTech(), + TangemTech(mockk()), // Don't forget to add new config ) diff --git a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/ProdFeatureTogglesManagerTest.kt b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/ProdFeatureTogglesManagerTest.kt index a8b90c4af8..35af42ae54 100644 --- a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/ProdFeatureTogglesManagerTest.kt +++ b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/ProdFeatureTogglesManagerTest.kt @@ -6,21 +6,13 @@ import com.tangem.core.featuretoggle.storage.FeatureToggle import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.featuretoggle.version.VersionProvider -import io.mockk.Runs -import io.mockk.coEvery -import io.mockk.coVerifyOrder -import io.mockk.every -import io.mockk.just -import io.mockk.mockk -import io.mockk.verifyAll -import kotlinx.coroutines.ExperimentalCoroutinesApi +import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.Test /** [REDACTED_AUTHOR] */ -@OptIn(ExperimentalCoroutinesApi::class) internal class ProdFeatureTogglesManagerTest { private val localFeatureTogglesStorage = mockk() diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt index bfb8f6107d..d0e43508c0 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt @@ -4,7 +4,7 @@ import androidx.core.util.PatternsCompat import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchainsdk.BlockchainProvidersResponse import com.tangem.blockchainsdk.utils.createPrivateProviderType -import com.tangem.datasource.api.tangemTech.TangemTechServiceApi +import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.config.models.ProviderModel import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -24,7 +24,7 @@ import javax.inject.Singleton */ @Singleton internal class BlockchainProvidersResponseLoader @Inject constructor( - private val tangemTechServiceApi: TangemTechServiceApi, + private val tangemTechServiceApi: TangemTechApi, private val assetLoader: AssetLoader, private val dispatchers: CoroutineDispatcherProvider, ) { diff --git a/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt b/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt index afbf3601ee..142425d558 100644 --- a/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt +++ b/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt @@ -7,7 +7,7 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.adapter import com.tangem.blockchainsdk.BlockchainProvidersResponse -import com.tangem.datasource.api.tangemTech.TangemTechServiceApi +import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.asset.reader.AssetReader import com.tangem.datasource.config.models.ProviderModel @@ -24,7 +24,7 @@ import org.junit.Test @OptIn(ExperimentalStdlibApi::class) internal class BlockchainProvidersResponseLoaderTest { - private val tangemTechServiceApi = mockk() + private val tangemTechServiceApi = mockk() private val assetReader = mockk() private val moshi = mockk() private val jsonAdapter = mockk>()