From c9e78f485d0eede75f3f91136a38831a28b1fe4c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Jun 2025 12:10:53 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 10 +- .../data/quotes/DefaultQuotesRepositoryV2.kt | 26 ----- .../tangem/data/quotes/di/QuotesDataModule.kt | 6 +- .../repository/DefaultQuotesRepository.kt | 35 ++++++ .../repository/DefaultQuotesRepositoryTest.kt | 108 ++++++++++++++++++ ...tesRepositoryV2.kt => QuotesRepository.kt} | 2 +- .../BaseCurrencyStatusOperations.kt | 6 +- .../CachedCurrenciesStatusesOperations.kt | 6 +- .../feature/swap/domain/SwapInteractorImpl.kt | 6 +- .../factory/express/ExchangeStatusFactory.kt | 6 +- 10 files changed, 164 insertions(+), 47 deletions(-) delete mode 100644 data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt create mode 100644 data/quotes/src/main/java/com/tangem/data/quotes/repository/DefaultQuotesRepository.kt create mode 100644 data/quotes/src/test/java/com/tangem/data/quotes/repository/DefaultQuotesRepositoryTest.kt rename domain/quotes/src/main/java/com/tangem/domain/quotes/{QuotesRepositoryV2.kt => QuotesRepository.kt} (92%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 185840f10a..f05324a2f1 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -8,7 +8,7 @@ import com.tangem.domain.networks.repository.NetworksRepository import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusSupplier import com.tangem.domain.promo.PromoRepository -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteFetcher import com.tangem.domain.quotes.single.SingleQuoteSupplier import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher @@ -368,7 +368,7 @@ internal object TokensDomainModule { fun provideBaseCurrenciesStatusesOperations( tokensFeatureToggles: TokensFeatureToggles, currenciesRepository: CurrenciesRepository, - quotesRepositoryV2: QuotesRepositoryV2, + quotesRepository: QuotesRepository, stakingRepository: StakingRepository, singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, @@ -380,7 +380,7 @@ internal object TokensDomainModule { ): BaseCurrenciesStatusesOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, - quotesRepositoryV2 = quotesRepositoryV2, + quotesRepository = quotesRepository, stakingRepository = stakingRepository, singleNetworkStatusSupplier = singleNetworkStatusSupplier, multiNetworkStatusSupplier = multiNetworkStatusSupplier, @@ -398,7 +398,7 @@ internal object TokensDomainModule { fun provideBaseCurrencyStatusOperations( tokensFeatureToggles: TokensFeatureToggles, currenciesRepository: CurrenciesRepository, - quotesRepositoryV2: QuotesRepositoryV2, + quotesRepository: QuotesRepository, stakingRepository: StakingRepository, singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, @@ -410,7 +410,7 @@ internal object TokensDomainModule { ): BaseCurrencyStatusOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, - quotesRepositoryV2 = quotesRepositoryV2, + quotesRepository = quotesRepository, stakingRepository = stakingRepository, singleNetworkStatusSupplier = singleNetworkStatusSupplier, multiNetworkStatusSupplier = multiNetworkStatusSupplier, diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt b/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt deleted file mode 100644 index af55873878..0000000000 --- a/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.data.quotes - -import com.tangem.data.quotes.store.QuotesStatusesStore -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.quotes.QuotesRepositoryV2 -import com.tangem.domain.tokens.model.QuoteStatus -import javax.inject.Inject - -/** - * Default implementation of [QuotesRepositoryV2] - * - * @property quotesStore quotes store - * -[REDACTED_AUTHOR] - */ -internal class DefaultQuotesRepositoryV2 @Inject constructor( - private val quotesStore: QuotesStatusesStore, -) : QuotesRepositoryV2 { - - override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? { - return quotesStore.getAllSyncOrNull()?.mapTo(hashSetOf()) { - it.takeIf { it.rawCurrencyId in currenciesIds } - ?: QuoteStatus(rawCurrencyId = it.rawCurrencyId) - } - } -} \ No newline at end of file diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/di/QuotesDataModule.kt b/data/quotes/src/main/java/com/tangem/data/quotes/di/QuotesDataModule.kt index fb43a38727..e25057bc21 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/di/QuotesDataModule.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/di/QuotesDataModule.kt @@ -4,7 +4,7 @@ import android.content.Context import androidx.datastore.core.DataStoreFactory import androidx.datastore.dataStoreFile import com.squareup.moshi.Moshi -import com.tangem.data.quotes.DefaultQuotesRepositoryV2 +import com.tangem.data.quotes.repository.DefaultQuotesRepository import com.tangem.data.quotes.store.DefaultQuotesStatusesStore import com.tangem.data.quotes.store.QuotesStatusesStore import com.tangem.datasource.api.tangemTech.models.QuotesResponse @@ -12,7 +12,7 @@ import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.datasource.utils.MoshiDataStoreSerializer import com.tangem.datasource.utils.mapWithStringKeyTypes -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -51,5 +51,5 @@ internal object QuotesDataModule { @Singleton @Provides - fun providesQuotesRepositoryV2(impl: DefaultQuotesRepositoryV2): QuotesRepositoryV2 = impl + fun providesQuotesRepository(impl: DefaultQuotesRepository): QuotesRepository = impl } \ No newline at end of file diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/repository/DefaultQuotesRepository.kt b/data/quotes/src/main/java/com/tangem/data/quotes/repository/DefaultQuotesRepository.kt new file mode 100644 index 0000000000..b7f600a91a --- /dev/null +++ b/data/quotes/src/main/java/com/tangem/data/quotes/repository/DefaultQuotesRepository.kt @@ -0,0 +1,35 @@ +package com.tangem.data.quotes.repository + +import com.tangem.data.quotes.store.QuotesStatusesStore +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.quotes.QuotesRepository +import com.tangem.domain.tokens.model.QuoteStatus +import timber.log.Timber +import javax.inject.Inject + +/** + * Default implementation of [QuotesRepository] + * + * @property quotesStatusesStore quotes statuses store + * +[REDACTED_AUTHOR] + */ +internal class DefaultQuotesRepository @Inject constructor( + private val quotesStatusesStore: QuotesStatusesStore, +) : QuotesRepository { + + override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set { + if (currenciesIds.isEmpty()) { + Timber.e("currenciesIds are empty") + return emptySet() + } + + val storedQuotes = quotesStatusesStore.getAllSyncOrNull() + ?.filter { it.rawCurrencyId in currenciesIds } + + return currenciesIds.mapTo(hashSetOf()) { currencyId -> + storedQuotes?.firstOrNull { it.rawCurrencyId == currencyId } + ?: QuoteStatus(rawCurrencyId = currencyId) + } + } +} \ No newline at end of file diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/repository/DefaultQuotesRepositoryTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/repository/DefaultQuotesRepositoryTest.kt new file mode 100644 index 0000000000..2484c2b302 --- /dev/null +++ b/data/quotes/src/test/java/com/tangem/data/quotes/repository/DefaultQuotesRepositoryTest.kt @@ -0,0 +1,108 @@ +package com.tangem.data.quotes.repository + +import com.google.common.truth.Truth +import com.tangem.common.test.utils.ProvideTestModels +import com.tangem.data.quotes.store.QuotesStatusesStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.quotes.QuotesRepository +import com.tangem.domain.tokens.model.QuoteStatus +import io.mockk.clearMocks +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class DefaultQuotesRepositoryTest { + + private val quotesStatusesStore = mockk() + private val repository: QuotesRepository = DefaultQuotesRepository(quotesStatusesStore = quotesStatusesStore) + + @BeforeEach + fun resetMocks() { + clearMocks(quotesStatusesStore) + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class GetMultiQuoteSyncOrNull { + + private val btcRawId = CryptoCurrency.RawID(value = "BTC") + private val ethRawId = CryptoCurrency.RawID(value = "ETH") + + private val ethQuote = QuoteStatus( + rawCurrencyId = ethRawId, + value = QuoteStatus.Data( + source = StatusSource.ACTUAL, + fiatRate = BigDecimal.ZERO, + priceChange = BigDecimal.ZERO, + ), + ) + + @ParameterizedTest + @ProvideTestModels + fun getMultiQuoteSyncOrNull(model: GetMultiQuoteSyncOrNullModel) = runTest { + // Arrange + coEvery { quotesStatusesStore.getAllSyncOrNull() } returns model.initialStore + + // Act + val actual = repository.getMultiQuoteSyncOrNull(currenciesIds = model.currencyIds) + + // Assert + val expected = model.expected + Truth.assertThat(actual).isEqualTo(expected) + } + + private fun provideTestModels() = listOf( + GetMultiQuoteSyncOrNullModel( + initialStore = null, + currencyIds = emptySet(), + expected = emptySet(), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = null, + currencyIds = setOf(ethRawId), + expected = setOf(QuoteStatus(rawCurrencyId = ethRawId)), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = emptySet(), + currencyIds = emptySet(), + expected = emptySet(), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = emptySet(), + currencyIds = setOf(ethRawId), + expected = setOf(QuoteStatus(rawCurrencyId = ethRawId)), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = setOf(ethQuote), + currencyIds = setOf(ethRawId), + expected = setOf(ethQuote), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = setOf(QuoteStatus(rawCurrencyId = btcRawId)), + currencyIds = setOf(ethRawId), + expected = setOf(QuoteStatus(rawCurrencyId = ethRawId)), + ), + GetMultiQuoteSyncOrNullModel( + initialStore = setOf(ethQuote, QuoteStatus(rawCurrencyId = btcRawId)), + currencyIds = setOf(ethRawId), + expected = setOf(ethQuote), + ), + ) + } + + data class GetMultiQuoteSyncOrNullModel( + val initialStore: Set?, + val currencyIds: Set, + val expected: Set?, + ) +} \ No newline at end of file diff --git a/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt b/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepository.kt similarity index 92% rename from domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt rename to domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepository.kt index dd446b9f0a..7da2b7f682 100644 --- a/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt +++ b/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepository.kt @@ -8,7 +8,7 @@ import com.tangem.domain.tokens.model.QuoteStatus * [REDACTED_AUTHOR] */ -interface QuotesRepositoryV2 { +interface QuotesRepository { /** Get quotes by [currenciesIds] synchronously or null */ suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 44d9a9574c..543594679c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -13,7 +13,7 @@ import com.tangem.domain.networks.multi.MultiNetworkStatusProducer import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier import com.tangem.domain.networks.single.SingleNetworkStatusProducer import com.tangem.domain.networks.single.SingleNetworkStatusSupplier -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.single.SingleQuoteProducer import com.tangem.domain.quotes.single.SingleQuoteSupplier import com.tangem.domain.staking.model.stakekit.YieldBalance @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.* @Suppress("LargeClass", "LongParameterList") abstract class BaseCurrencyStatusOperations( private val currenciesRepository: CurrenciesRepository, - private val quotesRepositoryV2: QuotesRepositoryV2, + private val quotesRepository: QuotesRepository, private val stakingRepository: StakingRepository, private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, @@ -257,7 +257,7 @@ abstract class BaseCurrencyStatusOperations( val (_, currenciesIds) = getIds(nonEmptyCurrencies) val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet() - val quotes = quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds)?.right() + val quotes = quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds)?.right() val networkStatuses = multiNetworkStatusSupplier( params = MultiNetworkStatusProducer.Params(userWalletId = userWalletId), diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index e547b6549b..5f14c36d45 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -17,7 +17,7 @@ import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier import com.tangem.domain.networks.single.SingleNetworkStatusProducer import com.tangem.domain.networks.single.SingleNetworkStatusSupplier -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteFetcher import com.tangem.domain.quotes.single.SingleQuoteProducer import com.tangem.domain.quotes.single.SingleQuoteSupplier @@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.* @Suppress("LongParameterList", "LargeClass") class CachedCurrenciesStatusesOperations( private val currenciesRepository: CurrenciesRepository, - quotesRepositoryV2: QuotesRepositoryV2, + quotesRepository: QuotesRepository, private val stakingRepository: StakingRepository, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, @@ -56,7 +56,7 @@ class CachedCurrenciesStatusesOperations( ) : BaseCurrenciesStatusesOperations, BaseCurrencyStatusOperations( currenciesRepository = currenciesRepository, - quotesRepositoryV2 = quotesRepositoryV2, + quotesRepository = quotesRepository, stakingRepository = stakingRepository, multiNetworkStatusSupplier = multiNetworkStatusSupplier, singleNetworkStatusSupplier = singleNetworkStatusSupplier, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 672cd819f8..d8f0938dc9 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -19,7 +19,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase @@ -65,7 +65,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( private val createTransactionExtrasUseCase: CreateTransactionDataExtrasUseCase, private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, - private val quotesRepositoryV2: QuotesRepositoryV2, + private val quotesRepository: QuotesRepository, private val swapTransactionRepository: SwapTransactionRepository, private val currencyChecksRepository: CurrencyChecksRepository, private val appCurrencyRepository: AppCurrencyRepository, @@ -1809,7 +1809,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( } private suspend fun Set.getQuotesOrEmpty(): Set { - return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = this) } + return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) } .getOrNull() .orEmpty() } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index 13c9843f25..8bc88d6346 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -6,7 +6,7 @@ import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.quotes.QuotesRepositoryV2 +import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.map internal class ExchangeStatusFactory @AssistedInject constructor( private val swapTransactionRepository: SwapTransactionRepository, private val swapRepository: SwapRepository, - private val quotesRepositoryV2: QuotesRepositoryV2, + private val quotesRepository: QuotesRepository, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val swapTransactionStatusStore: SwapTransactionStatusStore, private val analyticsEventsHandler: AnalyticsEventHandler, @@ -190,7 +190,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( private suspend fun Set.getQuotesOrEmpty(): Set { val rawIds = mapNotNull { it.rawCurrencyId }.toSet() - return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds) } + return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds) } .getOrNull() .orEmpty() }