Updated on 2026-08-14
This commit is contained in:
parent
55f731f1c3
commit
b89d98aa35
12 changed files with 358 additions and 0 deletions
|
|
@ -8,6 +8,7 @@ import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
|||
import com.tangem.data.networks.repository.DefaultNetworksRepository
|
||||
import com.tangem.data.networks.store.DefaultNetworksStatusesStore
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.data.networks.utils.DefaultNetworksCleaner
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.network.entity.NetworkStatusDM
|
||||
|
|
@ -15,6 +16,7 @@ import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
|||
import com.tangem.datasource.utils.mapWithStringKeyTypes
|
||||
import com.tangem.datasource.utils.setTypes
|
||||
import com.tangem.domain.networks.repository.NetworksRepository
|
||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -67,4 +69,18 @@ internal object NetworkDataModule {
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNetworksCleaner(
|
||||
networksStatusesStore: NetworksStatusesStore,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): NetworksCleaner {
|
||||
return DefaultNetworksCleaner(
|
||||
networksStatusesStore = networksStatusesStore,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,20 @@ internal class DefaultNetworksStatusesStore(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun clear(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
persistenceDataStore.updateData { storedStatuses ->
|
||||
storedStatuses.toMutableMap().apply {
|
||||
val updatedValues = this[userWalletId.stringValue].orEmpty().filterNot {
|
||||
networks.any { network ->
|
||||
it.networkId.value == network.rawId && it.derivationPath.value == network.derivationPath.value
|
||||
}
|
||||
}
|
||||
|
||||
this[userWalletId.stringValue] = updatedValues.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateInRuntime(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
|
|
|
|||
|
|
@ -41,4 +41,7 @@ internal interface NetworksStatusesStore {
|
|||
* See complex methods in `NetworksStatusesStoreExt`.
|
||||
*/
|
||||
suspend fun store(userWalletId: UserWalletId, status: NetworkStatus)
|
||||
|
||||
/** Clear statuses of [networks] by [userWalletId] */
|
||||
suspend fun clear(userWalletId: UserWalletId, networks: Set<Network>)
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.data.networks.utils
|
||||
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Default implementation of [NetworksCleaner].
|
||||
*
|
||||
* @property networksStatusesStore Store to manage network statuses.
|
||||
* @property walletManagersFacade Facade to manage wallet managers.
|
||||
* @property dispatchers Coroutine dispatchers provider.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultNetworksCleaner(
|
||||
private val networksStatusesStore: NetworksStatusesStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : NetworksCleaner {
|
||||
|
||||
override suspend fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
withContext(dispatchers.default) {
|
||||
val (networks, tokens) = currencies.partitionByType()
|
||||
|
||||
coroutineScope {
|
||||
launch { cleanStore(userWalletId = userWalletId, networks = networks) }
|
||||
launch { cleanWalletManager(userWalletId = userWalletId, networks = networks, tokens = tokens) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun cleanStore(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
if (networks.isNotEmpty()) {
|
||||
networksStatusesStore.clear(userWalletId = userWalletId, networks = networks)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun cleanWalletManager(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
tokens: Set<CryptoCurrency.Token>,
|
||||
) {
|
||||
if (networks.isNotEmpty()) {
|
||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = networks)
|
||||
}
|
||||
|
||||
if (tokens.isNotEmpty()) {
|
||||
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = tokens)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrency>.partitionByType(): Pair<Set<Network>, Set<CryptoCurrency.Token>> {
|
||||
val networks = mutableSetOf<Network>()
|
||||
val tokens = mutableSetOf<CryptoCurrency.Token>()
|
||||
|
||||
for (currency in this) {
|
||||
when (currency) {
|
||||
is CryptoCurrency.Coin -> networks.add(currency.network)
|
||||
is CryptoCurrency.Token -> tokens.add(currency)
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(networks, tokens)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package com.tangem.data.networks.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class DefaultNetworksCleanerTest {
|
||||
|
||||
private val networksStatusesStore = mockk<NetworksStatusesStore>(relaxed = true)
|
||||
private val walletManagersFacade = mockk<WalletManagersFacade>(relaxed = true)
|
||||
private val cleaner = DefaultNetworksCleaner(
|
||||
networksStatusesStore = networksStatusesStore,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val network = cryptoCurrencyFactory.ethereum.network
|
||||
private val coin = cryptoCurrencyFactory.ethereum
|
||||
private val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
clearMocks(networksStatusesStore, walletManagersFacade)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should clear networks and remove managers and tokens when called`() = runTest {
|
||||
// Arrange
|
||||
val currencies = listOf(coin, token)
|
||||
|
||||
// Act
|
||||
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||
|
||||
// Assert
|
||||
coVerifyOrder {
|
||||
networksStatusesStore.clear(userWalletId, setOf(network))
|
||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
||||
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle empty currencies`() = runTest {
|
||||
// Act
|
||||
cleaner(userWalletId = userWalletId, currencies = emptyList())
|
||||
|
||||
// Assert
|
||||
coVerifyOrder(inverse = true) {
|
||||
networksStatusesStore.clear(userWalletId = any(), networks = any())
|
||||
walletManagersFacade.remove(userWalletId = any(), networks = any())
|
||||
walletManagersFacade.removeTokens(userWalletId = any(), tokens = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should clear only networks when there are no tokens`() = runTest {
|
||||
val currencies = listOf(coin)
|
||||
|
||||
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||
|
||||
coVerifyOrder {
|
||||
networksStatusesStore.clear(userWalletId, setOf(network))
|
||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
||||
}
|
||||
|
||||
coVerifyOrder(inverse = true) {
|
||||
walletManagersFacade.removeTokens(userWalletId = any(), tokens = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should clear only tokens when there are no networks`() = runTest {
|
||||
// Arrange
|
||||
val currencies = listOf(token)
|
||||
|
||||
// Act
|
||||
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||
|
||||
// Assert
|
||||
coVerifyOrder {
|
||||
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
||||
}
|
||||
|
||||
coVerifyOrder(inverse = true) {
|
||||
networksStatusesStore.clear(userWalletId = any(), networks = any())
|
||||
walletManagersFacade.remove(userWalletId = any(), networks = any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.data.staking.DefaultStakingTransactionHashRepository
|
|||
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles
|
||||
import com.tangem.data.staking.utils.DefaultStakingCleaner
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
|
|
@ -21,6 +22,7 @@ import com.tangem.domain.staking.repositories.StakingErrorResolver
|
|||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -102,4 +104,16 @@ internal object StakingDataModule {
|
|||
fun provideFeatureToggles(featureTogglesManager: FeatureTogglesManager): StakingFeatureToggles {
|
||||
return DefaultStakingFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingCleaner(
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): StakingCleaner {
|
||||
return DefaultStakingCleaner(
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,16 @@ internal class DefaultYieldsBalancesStore(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun clear(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
|
||||
persistenceStore.updateData { current ->
|
||||
current.toMutableMap().apply {
|
||||
this[userWalletId.stringValue] = this[userWalletId.stringValue].orEmpty()
|
||||
.filterNot { it.getStakingId() in stakingIds }
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set<YieldBalanceWrapperDTO>) {
|
||||
val newBalances = YieldBalanceConverter(isCached = false).convertSet(input = values)
|
||||
.filterNotNull()
|
||||
|
|
|
|||
|
|
@ -33,4 +33,7 @@ interface YieldsBalancesStore {
|
|||
|
||||
/** Store error by [userWalletId] and [stakingIds] */
|
||||
suspend fun storeError(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
|
||||
|
||||
/** Clear balances of [stakingIds] by [userWalletId] */
|
||||
suspend fun clear(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.data.staking.utils
|
||||
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
/**
|
||||
* Default implementation of [StakingCleaner].
|
||||
*
|
||||
* @property yieldsBalancesStore Store to manage yields balances.
|
||||
* @property dispatchers Coroutine dispatchers provider.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultStakingCleaner(
|
||||
private val yieldsBalancesStore: YieldsBalancesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : StakingCleaner {
|
||||
|
||||
override suspend fun invoke(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
|
||||
if (stakingIds.isEmpty()) return
|
||||
|
||||
with(dispatchers.default) {
|
||||
yieldsBalancesStore.clear(userWalletId, stakingIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.data.staking.utils
|
||||
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class DefaultStakingCleanerTest {
|
||||
|
||||
private val yieldsBalancesStore = mockk<YieldsBalancesStore>(relaxed = true)
|
||||
private val cleaner = DefaultStakingCleaner(
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val stakingIds = setOf(
|
||||
StakingID(integrationId = StakingIntegrationID.Coin.Cardano.value, address = "0x1"),
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
clearMocks(yieldsBalancesStore)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should clear yields balances when called`() = runTest {
|
||||
// Act
|
||||
cleaner(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||
|
||||
// Assert
|
||||
coVerifyOrder {
|
||||
yieldsBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle empty stakingIds`() = runTest {
|
||||
// Act
|
||||
cleaner(userWalletId = userWalletId, stakingIds = emptySet())
|
||||
|
||||
// Assert
|
||||
coVerifyOrder(inverse = true) {
|
||||
yieldsBalancesStore.clear(userWalletId = any(), stakingIds = any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.domain.networks.utils
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Cleans up network-related data for a specific user wallet and a list of cryptocurrencies.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface NetworksCleaner {
|
||||
|
||||
/**
|
||||
* Cleans up network-related data for the given [userWalletId] and list of [currencies].
|
||||
*
|
||||
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||
* @param currencies The list of cryptocurrencies whose associated network data should be cleaned.
|
||||
*/
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.domain.staking.utils
|
||||
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Cleans up staking-related data for a specific user wallet and a set of staking IDs.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface StakingCleaner {
|
||||
|
||||
/**
|
||||
* Cleans up staking-related data for the given [userWalletId] and set of [stakingIds].
|
||||
*
|
||||
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||
* @param stakingIds The set of staking IDs whose associated data should be cleaned.
|
||||
*/
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue