Updated on 2026-08-14
This commit is contained in:
parent
62d0a771d9
commit
1d7416c58f
11 changed files with 126 additions and 81 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CurrenciesResponse>
|
||||
suspend fun getCurrencyList(
|
||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||
): ApiResponse<CurrenciesResponse>
|
||||
|
||||
@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<QuotesResponse>
|
||||
|
||||
@GET("promotion")
|
||||
suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse<PromotionInfoResponse>
|
||||
suspend fun getPromotionInfo(
|
||||
@Query("programName") name: String,
|
||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||
): ApiResponse<PromotionInfoResponse>
|
||||
|
||||
@GET("settings/{wallet_id}")
|
||||
suspend fun getUserTokensSettings(
|
||||
|
|
@ -131,4 +131,8 @@ interface TangemTechApi {
|
|||
|
||||
@GET("features")
|
||||
suspend fun getFeatures(): ApiResponse<FeaturesResponse>
|
||||
|
||||
@ReadTimeout(duration = 5, unit = TimeUnit.SECONDS)
|
||||
@GET("networks/providers")
|
||||
suspend fun getBlockchainProviders(): Map<String, List<ProviderModel>>
|
||||
}
|
||||
|
|
@ -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<String, List<ProviderModel>>
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -28,5 +28,5 @@ internal object ApiConfigsModule {
|
|||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemTechConfig(): ApiConfig = TangemTech()
|
||||
fun provideTangemTechConfig(appVersionProvider: AppVersionProvider): ApiConfig = TangemTech(appVersionProvider)
|
||||
}
|
||||
|
|
@ -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<RequestHeader> = listOf(CacheControlHeader, AppVersionPlatformHeaders(appVersionProvider)),
|
||||
requestHeaders: List<RequestHeader> = 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, Provider<String>>): OkHttpClient.Builder {
|
||||
return addInterceptor(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<FeatureTogglesStorage>()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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<TangemTechServiceApi>()
|
||||
private val tangemTechServiceApi = mockk<TangemTechApi>()
|
||||
private val assetReader = mockk<AssetReader>()
|
||||
private val moshi = mockk<Moshi>()
|
||||
private val jsonAdapter = mockk<JsonAdapter<BlockchainProvidersResponse>>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue