Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-12 18:42:37 +05:00
parent 3171058cb6
commit a6e4099935
16 changed files with 242 additions and 33 deletions

View file

@ -1,6 +1,8 @@
package com.tangem.data.wallets
import com.tangem.data.wallets.converters.UserWalletRemoteInfoConverter
import com.tangem.data.wallets.converters.WalletIdBodyConverter
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
@ -18,6 +20,7 @@ import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.UserWalletRemoteInfo
import com.tangem.domain.wallets.repository.WalletsRepository
@ -37,6 +40,7 @@ internal class DefaultWalletsRepository(
private val userWalletsStore: UserWalletsStore,
private val seedPhraseNotificationVisibilityStore: RuntimeStateStore<SeedPhraseNotificationsStatuses>,
private val dispatchers: CoroutineDispatcherProvider,
private val authProvider: AuthProvider,
) : WalletsRepository {
override suspend fun shouldSaveUserWalletsSync(): Boolean {
@ -288,6 +292,22 @@ internal class DefaultWalletsRepository(
}
}
override suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>) =
withContext(dispatchers.io) {
val publicKeys = authProvider.getCardsPublicKeys()
val walletsBody = wallets.map { userWallet ->
WalletIdBodyConverter.convert(
userWallet = userWallet,
publicKeys = publicKeys.filterKeys { userWallet.cardsInWallet.contains(it) },
)
}
tangemTechApi.associateApplicationIdWithWallets(
applicationId = applicationId,
body = walletsBody,
).getOrThrow()
}
private suspend fun loadAndSaveNotificationsEnabled(userWalletId: UserWalletId): Boolean {
val walletResponse = tangemTechApi.getWalletById(walletId = userWalletId.stringValue).getOrThrow()
val isEnabled = walletResponse.notifyStatus

View file

@ -0,0 +1,21 @@
package com.tangem.data.wallets.converters
import com.tangem.datasource.api.tangemTech.models.CardInfoBody
import com.tangem.datasource.api.tangemTech.models.WalletIdBody
import com.tangem.domain.wallets.models.UserWallet
internal object WalletIdBodyConverter {
fun convert(userWallet: UserWallet, publicKeys: Map<String, String>): WalletIdBody {
return WalletIdBody(
walletId = userWallet.walletId.stringValue,
name = userWallet.name,
cards = publicKeys.map {
CardInfoBody(
cardId = it.key,
cardPublicKey = it.value,
)
},
)
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.data.wallets.di
import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository
import com.tangem.data.wallets.DefaultWalletsRepository
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.datastore.RuntimeStateStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
@ -26,6 +27,7 @@ internal object WalletsDataModule {
tangemTechApi: TangemTechApi,
userWalletsStore: UserWalletsStore,
dispatchers: CoroutineDispatcherProvider,
authProvider: AuthProvider,
): WalletsRepository {
return DefaultWalletsRepository(
appPreferencesStore = appPreferencesStore,
@ -33,6 +35,7 @@ internal object WalletsDataModule {
userWalletsStore = userWalletsStore,
seedPhraseNotificationVisibilityStore = RuntimeStateStore(defaultValue = emptyMap()),
dispatchers = dispatchers,
authProvider = authProvider,
)
}

View file

@ -18,6 +18,8 @@ import io.mockk.coVerify
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import com.google.common.truth.Truth.assertThat
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.domain.wallets.models.UserWallet
import org.junit.Before
import org.junit.Test
@ -48,6 +50,7 @@ class DefaultWalletsRepositoryTest {
userWalletsStore = mockk(),
seedPhraseNotificationVisibilityStore = mockk(),
dispatchers = dispatchers,
authProvider = mockk(),
)
}
@ -231,4 +234,73 @@ class DefaultWalletsRepositoryTest {
coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) }
coVerify(exactly = 0) { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN user wallets and application ID WHEN associateWallets THEN should convert and send to API`() = runTest {
// GIVEN
val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef"
val wallet2Id = "fedcba0987654321"
val card1PublicKey = "card1_public_key"
val card2PublicKey = "card2_public_key"
val userWallets = listOf(
mockk<UserWallet> {
every { cardsInWallet } returns setOf(card1PublicKey)
every { walletId } returns UserWalletId(wallet1Id)
every { name } returns "Wallet 1"
},
mockk<UserWallet> {
every { cardsInWallet } returns setOf(card2PublicKey)
every { walletId } returns UserWalletId(wallet2Id)
every { name } returns "Wallet 2"
},
)
val publicKeys = mapOf(
card1PublicKey to "public_key_1",
card2PublicKey to "public_key_2",
)
val authProvider = mockk<AuthProvider> {
every { getCardsPublicKeys() } returns publicKeys
}
repository = DefaultWalletsRepository(
appPreferencesStore = appPreferenceStore,
tangemTechApi = tangemTechApi,
userWalletsStore = mockk(),
seedPhraseNotificationVisibilityStore = mockk(),
dispatchers = dispatchers,
authProvider = authProvider,
)
coEvery {
tangemTechApi.associateApplicationIdWithWallets(
eq(applicationId),
any(),
)
} returns ApiResponse.Success(Unit)
// WHEN
repository.associateWallets(applicationId, userWallets)
// THEN
coVerify(exactly = 1) {
tangemTechApi.associateApplicationIdWithWallets(
eq(applicationId),
match { body ->
body.size == 2 &&
body.any {
it.walletId == wallet1Id && it.cards.any { card -> card.cardPublicKey == "public_key_1" } &&
it.name == "Wallet 1"
} &&
body.any {
it.walletId == wallet2Id && it.cards.any { card -> card.cardPublicKey == "public_key_2" } &&
it.name == "Wallet 2"
}
},
)
}
}
}

View file

@ -0,0 +1,80 @@
package com.tangem.data.wallets.converters
import com.tangem.datasource.api.tangemTech.models.CardInfoBody
import com.tangem.datasource.api.tangemTech.models.WalletIdBody
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.google.common.truth.Truth.assertThat
import io.mockk.mockk
import org.junit.Test
class WalletIdBodyConverterTest {
@Test
fun `GIVEN user wallet with cards WHEN convert THEN should return correct WalletIdBody`() {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
val walletName = "Test Wallet"
val userWallet = UserWallet(
walletId = walletId,
name = walletName,
cardsInWallet = setOf("card1", "card2"),
isMultiCurrency = true,
hasBackupError = false,
scanResponse = mockk(),
)
val publicKeys = mapOf(
"card1" to "public_key_1",
"card2" to "public_key_2",
)
// WHEN
val result = WalletIdBodyConverter.convert(userWallet, publicKeys)
// THEN
assertThat(result).isEqualTo(
WalletIdBody(
walletId = walletId.stringValue,
name = walletName,
cards = listOf(
CardInfoBody(
cardId = "card1",
cardPublicKey = "public_key_1",
),
CardInfoBody(
cardId = "card2",
cardPublicKey = "public_key_2",
),
),
),
)
}
@Test
fun `GIVEN user wallet without cards WHEN convert THEN should return WalletIdBody with empty cards list`() {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
val walletName = "Test Wallet"
val userWallet = UserWallet(
walletId = walletId,
name = walletName,
cardsInWallet = emptySet(),
isMultiCurrency = true,
hasBackupError = false,
scanResponse = mockk(),
)
val publicKeys = emptyMap<String, String>()
// WHEN
val result = WalletIdBodyConverter.convert(userWallet, publicKeys)
// THEN
assertThat(result).isEqualTo(
WalletIdBody(
walletId = walletId.stringValue,
name = walletName,
cards = emptyList(),
),
)
}
}