Updated on 2026-08-14
This commit is contained in:
parent
84f5d99917
commit
1d8522dca6
5 changed files with 102 additions and 10 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 0a2434d93d67bec1804e3e827b3795a62f961d90
|
||||
Subproject commit 4424b32de5a80305cd3ca6eca219b09a16e940c5
|
||||
|
|
@ -9,6 +9,7 @@ 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.utils.RequestHeader
|
||||
import com.tangem.datasource.utils.RequestHeader.*
|
||||
import com.tangem.datasource.utils.addHeaders
|
||||
import com.tangem.datasource.utils.addLoggers
|
||||
|
|
@ -118,11 +119,12 @@ class NetworkModule {
|
|||
appVersionProvider: AppVersionProvider,
|
||||
): TangemTechServiceApi {
|
||||
return provideTangemTechApiInternal(
|
||||
moshi,
|
||||
context,
|
||||
appVersionProvider,
|
||||
PROD_V1_TANGEM_TECH_BASE_URL,
|
||||
moshi = moshi,
|
||||
context = context,
|
||||
appVersionProvider = appVersionProvider,
|
||||
baseUrl = PROD_V1_TANGEM_TECH_BASE_URL,
|
||||
timeoutSeconds = TANGEM_TECH_SERVICE_TIMEOUT_SECONDS,
|
||||
requestHeaders = listOf(AppVersionPlatformHeaders(appVersionProvider)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +134,7 @@ class NetworkModule {
|
|||
appVersionProvider: AppVersionProvider,
|
||||
baseUrl: String,
|
||||
timeoutSeconds: Long? = null,
|
||||
requestHeaders: List<RequestHeader> = listOf(CacheControlHeader, AppVersionPlatformHeaders(appVersionProvider)),
|
||||
): T {
|
||||
val client = OkHttpClient.Builder()
|
||||
.let { builder ->
|
||||
|
|
@ -142,8 +145,7 @@ class NetworkModule {
|
|||
}
|
||||
}
|
||||
.addHeaders(
|
||||
CacheControlHeader,
|
||||
AppVersionPlatformHeaders(appVersionProvider),
|
||||
*requestHeaders.toTypedArray(),
|
||||
// TODO("refactor header init") get auth data after biometric auth to avoid race condition
|
||||
// AuthenticationHeader(authProvider),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
{
|
||||
"name": "LOCAL_USER_LOGS_ENABLED",
|
||||
"version": "5.11.0"
|
||||
"version": "5.13.0"
|
||||
},
|
||||
{
|
||||
"name": "GENERATE_XPUB_ENABLED",
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
},
|
||||
{
|
||||
"name": "CARDANO_TOKENS_SUPPORT_ENABLED",
|
||||
"version": "5.11.0"
|
||||
"version": "5.12.0"
|
||||
},
|
||||
{
|
||||
"name": "STAKING_ENABLED",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.blockchainsdk.loader
|
||||
|
||||
import androidx.core.util.PatternsCompat
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchainsdk.BlockchainProvidersResponse
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechServiceApi
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.models.ProviderModel
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import timber.log.Timber
|
||||
|
|
@ -68,7 +70,15 @@ internal class BlockchainProvidersResponseLoader @Inject constructor(
|
|||
*
|
||||
* local + remote // { a = 1, e = 4, f = 5, b = 2, c = 3 }
|
||||
*/
|
||||
val result = local + remote.filterValues { it.isNotEmpty() }
|
||||
val remoteWithoutInvalidProviders = remote
|
||||
.mapValues {
|
||||
it.value
|
||||
.filterUnsupportedProviders()
|
||||
.filterInvalidProviders()
|
||||
}
|
||||
.filterValues { it.isNotEmpty() }
|
||||
|
||||
val result = local + remoteWithoutInvalidProviders
|
||||
|
||||
if (result != remote) {
|
||||
val missingBlockchains = result.keys - remote.keys
|
||||
|
|
@ -80,6 +90,23 @@ internal class BlockchainProvidersResponseLoader @Inject constructor(
|
|||
return result
|
||||
}
|
||||
|
||||
private fun List<ProviderModel>.filterUnsupportedProviders() = filter { it !is ProviderModel.UnsupportedType }
|
||||
|
||||
private fun List<ProviderModel>.filterInvalidProviders() = mapNotNull { provider ->
|
||||
if (provider is ProviderModel.Public) {
|
||||
if (isValidUrl(provider.url)) provider else null
|
||||
} else {
|
||||
provider
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidUrl(url: String): Boolean {
|
||||
val forbiddenScheme = forbiddenSchemes.firstOrNull { url.startsWith(prefix = it) }
|
||||
val inputUrl = if (forbiddenScheme != null) url.substringAfter(forbiddenScheme) else url
|
||||
|
||||
return PatternsCompat.WEB_URL.matcher(inputUrl).matches()
|
||||
}
|
||||
|
||||
private fun recordException(missingBlockchains: Set<String>) {
|
||||
val exception = IllegalStateException(
|
||||
"Remote config does not contain required blockchains or providers information: " +
|
||||
|
|
@ -93,5 +120,7 @@ internal class BlockchainProvidersResponseLoader @Inject constructor(
|
|||
|
||||
private companion object {
|
||||
const val PROVIDER_TYPES_FILE_NAME = "tangem-app-config/providers_order"
|
||||
|
||||
val forbiddenSchemes = listOf("wss://")
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +218,67 @@ internal class BlockchainProvidersResponseLoaderTest {
|
|||
Truth.assertThat(actual).isEqualTo(remoteProviders)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_load_if_remote_config_contains_unsupported_types() = runTest {
|
||||
val ethProvider = "ethereum" to listOf(ProviderModel.UnsupportedType, ProviderModel.Private(name = "nownodes"))
|
||||
val remoteProvidersWithEth = remoteProviders + ethProvider
|
||||
|
||||
everyGettingLocalConfig(json = localProvidersJson) returns localProviders
|
||||
everyGettingRemoteConfig() returns remoteProvidersWithEth
|
||||
everyCrashlyticsRecording() just Runs
|
||||
|
||||
val actual = loader.load()
|
||||
|
||||
coVerifyOrder {
|
||||
assetReader.read(LOCAL_CONFIG_FILE_NAME)
|
||||
moshi.adapter<BlockchainProvidersResponse>()
|
||||
jsonAdapter.fromJson(localProvidersJson)
|
||||
authProvider.getCardPublicKey()
|
||||
authProvider.getCardId()
|
||||
tangemTechServiceApi.getBlockchainProviders(
|
||||
cardPublicKey = DEFAULT_CARD_PUBLIC_KEY,
|
||||
cardId = DEFAULT_CARD_ID,
|
||||
)
|
||||
firebaseCrashlytics.recordException(any())
|
||||
}
|
||||
|
||||
val expected = remoteProviders + ("ethereum" to listOf(ProviderModel.Private(name = "nownodes")))
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_load_if_remote_config_contains_invalid_public_providers() = runTest {
|
||||
val localEthProvider = "ethereum" to listOf(ProviderModel.Private(name = "nownodes"))
|
||||
val localProvidersWithEth = localProviders + localEthProvider
|
||||
|
||||
val remoteEthProvider = "ethereum" to listOf(ProviderModel.UnsupportedType, ProviderModel.Public("adbw2138"))
|
||||
val remoteProvidersWithEth = remoteProviders + remoteEthProvider
|
||||
|
||||
everyGettingLocalConfig(json = localProvidersJson) returns localProvidersWithEth
|
||||
everyGettingRemoteConfig() returns remoteProvidersWithEth
|
||||
everyCrashlyticsRecording() just Runs
|
||||
|
||||
val actual = loader.load()
|
||||
|
||||
coVerifyOrder {
|
||||
assetReader.read(LOCAL_CONFIG_FILE_NAME)
|
||||
moshi.adapter<BlockchainProvidersResponse>()
|
||||
jsonAdapter.fromJson(localProvidersJson)
|
||||
authProvider.getCardPublicKey()
|
||||
authProvider.getCardId()
|
||||
tangemTechServiceApi.getBlockchainProviders(
|
||||
cardPublicKey = DEFAULT_CARD_PUBLIC_KEY,
|
||||
cardId = DEFAULT_CARD_ID,
|
||||
)
|
||||
firebaseCrashlytics.recordException(any())
|
||||
}
|
||||
|
||||
val expected = remoteProviders + localEthProvider
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun everyGettingLocalConfig(
|
||||
json: String,
|
||||
): MockKStubScope<BlockchainProvidersResponse?, BlockchainProvidersResponse?> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue