diff --git a/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt b/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt index d113493313..784db9dbc2 100644 --- a/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt +++ b/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt @@ -15,6 +15,7 @@ import com.tangem.core.analytics.models.ExceptionAnalyticsEvent import com.tangem.crypto.bip39.Wordlist import com.tangem.data.card.sdk.CardSdkOwner import com.tangem.data.card.sdk.CardSdkProvider +import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.common.config.ApiConfig import com.tangem.datasource.api.common.config.ApiEnvironmentConfig import com.tangem.datasource.api.common.config.managers.ApiConfigsManager @@ -28,6 +29,7 @@ import com.tangem.sdk.nfc.AndroidNfcAvailabilityProvider import com.tangem.sdk.nfc.NfcManager import com.tangem.sdk.storage.create import com.tangem.tap.foregroundActivityObserver +import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider @@ -48,6 +50,7 @@ internal class DefaultCardSdkProvider @Inject constructor( private val apiConfigsManager: ApiConfigsManager, appVersionProvider: AppVersionProvider, appInfoProvider: AppInfoProvider, + authProvider: AuthProvider, ) : CardSdkProvider, CardSdkOwner { private val observer = Observer() @@ -68,13 +71,16 @@ internal class DefaultCardSdkProvider @Inject constructor( }, ) + val apiEnvironment = Provider { + apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.TangemTech).environment + } + val platformHeaders = RequestHeader.AppVersionPlatformHeaders( + appVersionProvider = appVersionProvider, + appInfoProvider = appInfoProvider, + ) + val apiKeyHeader = RequestHeader.TangemApiKeyHeader(authProvider, apiEnvironment) TangemApiServiceSettings.addInterceptors( - AddHeadersInterceptor( - requestHeader = RequestHeader.AppVersionPlatformHeaders( - appVersionProvider = appVersionProvider, - appInfoProvider = appInfoProvider, - ), - ), + AddHeadersInterceptor(platformHeaders.values + apiKeyHeader.values), ) } diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt index 8982e6031a..f873c4683f 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt @@ -2,14 +2,19 @@ package com.tangem.tap.network.auth import com.tangem.common.extensions.toHexString import com.tangem.datasource.api.common.AuthProvider +import com.tangem.datasource.api.common.config.ApiEnvironment +import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.utils.Provider +import com.tangem.utils.ProviderSuspend internal class DefaultAuthProvider( private val userWalletsListManager: UserWalletsListManager, private val userWalletsListRepository: UserWalletsListRepository, private val shouldUseNewListRepository: Boolean = false, + private val environmentConfigStorage: EnvironmentConfigStorage, ) : AuthProvider { override suspend fun getCardPublicKey(): String { @@ -38,6 +43,20 @@ internal class DefaultAuthProvider( } } + override fun getApiKey(apiEnvironment: Provider): ProviderSuspend { + return ProviderSuspend { + when (apiEnvironment.invoke()) { + ApiEnvironment.MOCK, + ApiEnvironment.DEV, + ApiEnvironment.DEV_2, + ApiEnvironment.DEV_3, + -> environmentConfigStorage.getConfigSync().tangemApiKeyDev + ApiEnvironment.STAGE -> environmentConfigStorage.getConfigSync().tangemApiKeyStage + ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().tangemApiKey + } ?: error("No tangem tech api config provided") + } + } + private suspend fun getWallets(): List { return if (shouldUseNewListRepository) { userWalletsListRepository.userWalletsSync() diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index f1aa594af8..f1ba2de154 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -28,11 +28,13 @@ internal class AuthModule { userWalletsListManager: UserWalletsListManager, userWalletsListRepository: UserWalletsListRepository, hotWalletFeatureToggles: HotWalletFeatureToggles, + environmentConfigStorage: EnvironmentConfigStorage, ): AuthProvider { return DefaultAuthProvider( userWalletsListManager = userWalletsListManager, userWalletsListRepository = userWalletsListRepository, shouldUseNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled, + environmentConfigStorage = environmentConfigStorage, ) } diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt index da1b1aca5f..002f8656ed 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt @@ -1,5 +1,9 @@ package com.tangem.datasource.api.common +import com.tangem.datasource.api.common.config.ApiEnvironment +import com.tangem.utils.Provider +import com.tangem.utils.ProviderSuspend + /** * Provides auth for tangemTech API */ @@ -12,6 +16,8 @@ interface AuthProvider { suspend fun getCardId(): String + fun getApiKey(apiEnvironment: Provider): ProviderSuspend + /** * Returns map where keys(cardId) associated with cardPublicKey */ 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 8d44291374..96ef551d23 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 @@ -2,15 +2,13 @@ package com.tangem.datasource.api.common.config import com.tangem.datasource.BuildConfig import com.tangem.datasource.api.common.AuthProvider -import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.utils.RequestHeader -import com.tangem.utils.ProviderSuspend +import com.tangem.utils.Provider import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider /** TangemTech [ApiConfig] */ internal class TangemTech( - private val environmentConfigStorage: EnvironmentConfigStorage, private val appVersionProvider: AppVersionProvider, private val authProvider: AuthProvider, private val appInfoProvider: AppInfoProvider, @@ -63,20 +61,8 @@ internal class TangemTech( ) private fun createHeaders(apiEnvironment: ApiEnvironment) = buildMap { - put(key = "api-key", value = ProviderSuspend { getApiKey(apiEnvironment) }) + putAll(from = RequestHeader.TangemApiKeyHeader(authProvider, Provider { apiEnvironment }).values) putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values) putAll(from = RequestHeader.AuthenticationHeader(authProvider).values) } - - private fun getApiKey(apiEnvironment: ApiEnvironment): String { - return when (apiEnvironment) { - ApiEnvironment.MOCK, - ApiEnvironment.DEV, - ApiEnvironment.DEV_2, - ApiEnvironment.DEV_3, - -> environmentConfigStorage.getConfigSync().tangemApiKeyDev - ApiEnvironment.STAGE -> environmentConfigStorage.getConfigSync().tangemApiKeyStage - ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().tangemApiKey - } ?: error("No tangem tech api config provided") - } } \ 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 57c3c53a26..83ac850e26 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 @@ -42,12 +42,10 @@ internal object ApiConfigsModule { @Provides @IntoSet fun provideTangemTechConfig( - environmentConfigStorage: EnvironmentConfigStorage, appVersionProvider: AppVersionProvider, authProvider: AuthProvider, appInfoProvider: AppInfoProvider, ): ApiConfig = TangemTech( - environmentConfigStorage = environmentConfigStorage, appVersionProvider = appVersionProvider, authProvider = authProvider, appInfoProvider = appInfoProvider, diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt index 5a896ff3e1..3f05524682 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt @@ -1,5 +1,6 @@ package com.tangem.datasource.utils +import com.tangem.utils.ProviderSuspend import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response @@ -12,21 +13,17 @@ import okhttp3.Response [REDACTED_AUTHOR] */ class AddHeadersInterceptor( - private val requestHeaders: Set, + private val requestHeaders: Map>, ) : Interceptor { - constructor(requestHeader: RequestHeader) : this(requestHeaders = setOf(requestHeader)) - override fun intercept(chain: Interceptor.Chain): Response { val requestBuilder = chain.request().newBuilder() - requestHeaders - .flatMap { it.values.toList() } - .forEach { (name, valueProvider) -> - val value = runBlocking { valueProvider() } + requestHeaders.forEach { (name, valueProvider) -> + val value = runBlocking { valueProvider() } - requestBuilder.addHeader(name = name, value = value) - } + requestBuilder.addHeader(name = name, value = value) + } val request = requestBuilder.build() 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 eb7727aea3..dcc9855b55 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 @@ -5,13 +5,6 @@ import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.OkHttpClient -/** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ -internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeader): OkHttpClient.Builder { - return addInterceptor( - interceptor = AddHeadersInterceptor(requestHeaders = requestHeaders.toSet()), - ) -} - /** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ internal fun OkHttpClient.Builder.addHeaders( requestHeaders: Map>, diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt index 9e6b000399..ef2b83b6df 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt @@ -2,7 +2,9 @@ package com.tangem.datasource.utils import android.os.Build import com.tangem.datasource.api.common.AuthProvider +import com.tangem.datasource.api.common.config.ApiEnvironment import com.tangem.datasource.utils.RequestHeader.CacheControlHeader.checkHeaderValueOrEmpty +import com.tangem.utils.Provider import com.tangem.utils.ProviderSuspend import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider @@ -40,6 +42,13 @@ sealed class RequestHeader(vararg pairs: Pair>) "device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() }, ) + /** + * Use ONLY for tangemApi (not express or yields) + */ + class TangemApiKeyHeader(authProvider: AuthProvider, apiEnvironment: Provider) : RequestHeader( + "api-key" to authProvider.getApiKey(apiEnvironment), + ) + /** * Use it to avoid crash in okhttp headers */ diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt index 53392207a9..0aa707e8cb 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt @@ -1,7 +1,12 @@ package com.tangem.datasource.api.common.config import com.google.common.truth.Truth +import com.tangem.datasource.api.common.AuthProvider +import com.tangem.utils.ProviderSuspend +import io.mockk.clearMocks +import io.mockk.every import io.mockk.mockk +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import timber.log.Timber @@ -12,6 +17,17 @@ import timber.log.Timber @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ApiConfigTest { + private val appAuthProvider = mockk() + private val apiKeyProvider = mockk>() + + @BeforeEach + fun setup() { + clearMocks( + appAuthProvider, + ) + every { appAuthProvider.getApiKey(any()) } returns apiKeyProvider + } + @Test fun `all baseUrls ends with slash`() { // Arrange @@ -41,15 +57,14 @@ class ApiConfigTest { YieldSupply( environmentConfigStorage = mockk(), appVersionProvider = mockk(), - authProvider = mockk(), + authProvider = appAuthProvider, appInfoProvider = mockk(), ) } ApiConfig.ID.TangemTech -> { TangemTech( - environmentConfigStorage = mockk(), appVersionProvider = mockk(), - authProvider = mockk(), + authProvider = appAuthProvider, appInfoProvider = mockk(), ) } 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 cb64ca1ea5..de6ac569e4 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 @@ -12,6 +12,7 @@ import com.tangem.datasource.api.common.config.ApiConfig.Companion.INTERNAL_BUIL 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.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_API_KEY +import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.TANGEM_API_KEY import com.tangem.lib.auth.ExpressAuthProvider import com.tangem.lib.auth.StakeKitAuthProvider import com.tangem.utils.ProviderSuspend @@ -40,8 +41,9 @@ internal class ProdApiConfigsManagerTest { private val stakeKitAuthProvider = mockk() private val appAuthProvider = mockk() private val appInfoProvider = mockk() + private val tangemApiKeyProvider = mockk>() - private val manager = ProdApiConfigsManager(apiConfigs = createApiConfigs()) + private lateinit var manager: ProdApiConfigsManager @BeforeEach fun setup() { @@ -56,9 +58,14 @@ internal class ProdApiConfigsManagerTest { every { appVersionProvider.versionName } returns VERSION_NAME every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY + every { appAuthProvider.getApiKey(any()) } returns tangemApiKeyProvider + coEvery { tangemApiKeyProvider.invoke() } returns TANGEM_API_KEY coEvery { appAuthProvider.getCardId() } returns APP_CARD_ID coEvery { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY + every { appInfoProvider.osVersion } returns "Android 16" + + manager = ProdApiConfigsManager(apiConfigs = createApiConfigs()) } @ParameterizedTest @@ -94,7 +101,6 @@ internal class ProdApiConfigsManagerTest { } ApiConfig.ID.TangemTech -> { TangemTech( - environmentConfigStorage = environmentConfigStorage, appVersionProvider = appVersionProvider, authProvider = appAuthProvider, appInfoProvider = appInfoProvider, @@ -175,7 +181,7 @@ internal class ProdApiConfigsManagerTest { environment = ApiEnvironment.PROD, baseUrl = "https://api.tangem.org/", headers = mapOf( - "api-key" to ProviderSuspend { MockEnvironmentConfigStorage.TANGEM_API_KEY }, + "api-key" to ProviderSuspend { TANGEM_API_KEY }, "card_id" to ProviderSuspend { APP_CARD_ID }, "card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY }, "version" to ProviderSuspend { VERSION_NAME },