Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-10 12:10:53 +04:00
parent bfd371bc33
commit c9e78f485d
10 changed files with 164 additions and 47 deletions

View file

@ -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,

View file

@ -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<CryptoCurrency.RawID>): Set<QuoteStatus>? {
return quotesStore.getAllSyncOrNull()?.mapTo(hashSetOf()) {
it.takeIf { it.rawCurrencyId in currenciesIds }
?: QuoteStatus(rawCurrencyId = it.rawCurrencyId)
}
}
}

View file

@ -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
}

View file

@ -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<CryptoCurrency.RawID>): Set<QuoteStatus> {
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)
}
}
}

View file

@ -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<QuotesStatusesStore>()
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<QuoteStatus>?,
val currencyIds: Set<CryptoCurrency.RawID>,
val expected: Set<QuoteStatus>?,
)
}

View file

@ -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<CryptoCurrency.RawID>): Set<QuoteStatus>?

View file

@ -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),

View file

@ -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,

View file

@ -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<CryptoCurrency.RawID>.getQuotesOrEmpty(): Set<QuoteStatus> {
return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = this) }
return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) }
.getOrNull()
.orEmpty()
}

View file

@ -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<CryptoCurrency.ID>.getQuotesOrEmpty(): Set<QuoteStatus> {
val rawIds = mapNotNull { it.rawCurrencyId }.toSet()
return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds) }
return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds) }
.getOrNull()
.orEmpty()
}