From 2a61e32f0c8d752228343b1df7c897babbda2047 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 27 Apr 2024 00:26:35 +0800 Subject: [PATCH] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../datasource/asset/loader/AssetLoader.kt | 12 ++--- libs/blockchain-sdk/build.gradle.kts | 6 +++ .../BlockchainProvidersResponseLoader.kt | 48 +++++++++++++++++-- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 367aa6465e..fdf0660901 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 367aa6465ec6ee7fdccce6cc7d7abeed5aad1111 +Subproject commit fdf0660901442ade296db42bb867fc5c16a14262 diff --git a/core/datasource/src/main/java/com/tangem/datasource/asset/loader/AssetLoader.kt b/core/datasource/src/main/java/com/tangem/datasource/asset/loader/AssetLoader.kt index e271679df4..042319e1f4 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/asset/loader/AssetLoader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/asset/loader/AssetLoader.kt @@ -31,11 +31,11 @@ class AssetLoader @Inject constructor( } .fold( onSuccess = { parsedConfig -> - if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config is null")) + if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null")) parsedConfig }, onFailure = { - Timber.e(it, "Failed to load config from assets") + Timber.e(it, "Failed to load config [$fileName] from assets") null }, ) @@ -53,11 +53,11 @@ class AssetLoader @Inject constructor( } .fold( onSuccess = { parsedConfig -> - if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config is null")) + if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null")) parsedConfig.orEmpty() }, onFailure = { - Timber.e(it, "Failed to load config from assets") + Timber.e(it, "Failed to load config [$fileName] from assets") emptyList() }, ) @@ -75,11 +75,11 @@ class AssetLoader @Inject constructor( } .fold( onSuccess = { parsedConfig -> - if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config is null")) + if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null")) parsedConfig.orEmpty() }, onFailure = { - Timber.e(it, "Failed to load config from assets") + Timber.e(it, "Failed to load config [$fileName] from assets") emptyMap() }, ) diff --git a/libs/blockchain-sdk/build.gradle.kts b/libs/blockchain-sdk/build.gradle.kts index ecf2e9a795..88d96a0405 100644 --- a/libs/blockchain-sdk/build.gradle.kts +++ b/libs/blockchain-sdk/build.gradle.kts @@ -32,6 +32,12 @@ dependencies { implementation(deps.timber) // endregion + // region Firebase libraries + implementation(platform(deps.firebase.bom)) + implementation(deps.firebase.analytics) + implementation(deps.firebase.crashlytics) + // endregion + // region Tangem libraries implementation(deps.tangem.blockchain) { exclude(module = "joda-time") } implementation(deps.tangem.card.core) 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 7613213ba4..310ec39f9a 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 @@ -1,5 +1,6 @@ package com.tangem.blockchainsdk.loader +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 @@ -28,18 +29,26 @@ internal class BlockchainProvidersResponseLoader @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, ) { + private val firebaseCrashlytics by lazy(FirebaseCrashlytics::getInstance) + /** Load [BlockchainProvidersResponse] */ suspend fun load(): BlockchainProvidersResponse? { + val localResponse = loadLocal() ?: return null + return runCatching(dispatcher = dispatchers.io, block = ::loadRemote) .fold( - onSuccess = { it }, + onSuccess = { remoteResponse -> mergeResponses(local = localResponse, remote = remoteResponse) }, onFailure = { Timber.e(it, "Failed to load blockchain provider types from backend") - loadLocal() + localResponse }, ) } + private suspend fun loadLocal(): BlockchainProvidersResponse? { + return assetLoader.load(fileName = PROVIDER_TYPES_FILE_NAME) + } + private suspend fun loadRemote(): BlockchainProvidersResponse { return tangemTechServiceApi.getBlockchainProviders( cardPublicKey = authProvider.getCardPublicKey(), @@ -47,8 +56,39 @@ internal class BlockchainProvidersResponseLoader @Inject constructor( ) } - private suspend fun loadLocal(): BlockchainProvidersResponse? { - return assetLoader.load(fileName = PROVIDER_TYPES_FILE_NAME) + /** Merge blockchains with non-empty providers [remote] from remote with blockchains from local [local] */ + private fun mergeResponses( + local: BlockchainProvidersResponse, + remote: BlockchainProvidersResponse, + ): BlockchainProvidersResponse { + /* + * Example: + * val remote = mapOf("a" to 1, "b" to 2, "c" to 3) + * val local = mapOf("a" to 11, "e" to 4, "f" to 5) + * + * local + remote // { a = 1, e = 4, f = 5, b = 2, c = 3 } + */ + val result = local + remote.filterValues { it.isNotEmpty() } + + if (result != remote) { + val missingBlockchains = result.keys - remote.keys + val blockchainsWithoutProviders = remote.filterValues { it.isEmpty() }.keys + + recordException(missingBlockchains = missingBlockchains + blockchainsWithoutProviders) + } + + return result + } + + private fun recordException(missingBlockchains: Set) { + val exception = IllegalStateException( + "Remote config does not contain required blockchains or providers information: " + + missingBlockchains.joinToString(), + ) + + Timber.e(exception) + + firebaseCrashlytics.recordException(exception) } private companion object {