Updated on 2026-08-14
This commit is contained in:
parent
3171058cb6
commit
a6e4099935
16 changed files with 242 additions and 33 deletions
|
|
@ -13,4 +13,10 @@ internal class DefaultAuthProvider(private val userWalletsListManager: UserWalle
|
|||
override fun getCardId(): String {
|
||||
return userWalletsListManager.selectedUserWalletSync?.scanResponse?.card?.cardId ?: ""
|
||||
}
|
||||
|
||||
override fun getCardsPublicKeys(): Map<String, String> {
|
||||
return userWalletsListManager.userWalletsSync.associate {
|
||||
it.scanResponse.card.cardId to it.scanResponse.card.cardPublicKey.toHexString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,9 @@ interface AuthProvider {
|
|||
fun getCardPublicKey(): String
|
||||
|
||||
fun getCardId(): String
|
||||
|
||||
/**
|
||||
* Returns map where keys(cardId) associated with cardPublicKey
|
||||
*/
|
||||
fun getCardsPublicKeys(): Map<String, String>
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardInfoBody(
|
||||
@Json(name = "card_id") val cardId: String,
|
||||
@Json(name = "card_public_key") val cardPublicKey: String,
|
||||
)
|
||||
|
|
@ -6,4 +6,6 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class WalletIdBody(
|
||||
@Json(name = "id") val walletId: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "cards") val cards: List<CardInfoBody>,
|
||||
)
|
||||
|
|
@ -4,7 +4,6 @@ import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConv
|
|||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.NotificationApplicationCreateBody
|
||||
import com.tangem.datasource.api.tangemTech.models.WalletIdBody
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
|
|
@ -62,16 +61,6 @@ internal class DefaultNotificationsRepository @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun associateApplicationIdWithWallets(appId: String, wallets: List<String>) =
|
||||
withContext(dispatchers.io) {
|
||||
tangemTechApi.associateApplicationIdWithWallets(
|
||||
applicationId = appId,
|
||||
body = wallets.map {
|
||||
WalletIdBody(it)
|
||||
},
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
override suspend fun sendPushToken(appId: ApplicationId, pushToken: String) {
|
||||
withContext(dispatchers.io) {
|
||||
tangemTechApi.updatePushTokenForApplicationId(
|
||||
|
|
|
|||
|
|
@ -104,25 +104,6 @@ class DefaultNotificationsRepositoryTest {
|
|||
assertThat(result).isEqualTo(expectedAppId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN application id and wallet list WHEN associateApplicationIdWithWallets THEN associates them`() = runTest {
|
||||
// GIVEN
|
||||
val appId = "test-app-id"
|
||||
val wallets = listOf("wallet1", "wallet2")
|
||||
coEvery {
|
||||
tangemTechApi.associateApplicationIdWithWallets(
|
||||
appId,
|
||||
wallets.map { WalletIdBody(it) },
|
||||
)
|
||||
} returns ApiResponse.Success(Unit)
|
||||
|
||||
// WHEN
|
||||
repository.associateApplicationIdWithWallets(appId, wallets)
|
||||
|
||||
// THEN
|
||||
coVerify { tangemTechApi.associateApplicationIdWithWallets(appId, wallets.map { WalletIdBody(it) }) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN application id and push token WHEN sendPushToken THEN updates push token`() = runTest {
|
||||
// GIVEN
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ dependencies {
|
|||
implementation(deps.arrow.core)
|
||||
|
||||
/** tests */
|
||||
testImplementation(projects.domain.models)
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.truth)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,6 @@ interface NotificationsRepository {
|
|||
|
||||
suspend fun incrementTronTokenFeeNotificationShowCounter()
|
||||
|
||||
@Throws
|
||||
suspend fun associateApplicationIdWithWallets(appId: String, wallets: List<String>)
|
||||
|
||||
@Throws
|
||||
suspend fun sendPushToken(appId: ApplicationId, pushToken: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class DefaultUserWalletsSyncDelegate(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO remove dispatchers whnen UserWalletsListManager will be main safe
|
||||
private suspend fun renameUserWallet(
|
||||
userWalletId: UserWalletId,
|
||||
name: String,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.wallets.repository
|
||||
|
||||
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 kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -54,4 +55,7 @@ interface WalletsRepository {
|
|||
|
||||
@Throws
|
||||
suspend fun getWalletsInfo(applicationId: String, updateCache: Boolean = true): List<UserWalletRemoteInfo>
|
||||
|
||||
@Throws
|
||||
suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
||||
class AssociateWalletsWithApplicationIdUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(applicationId: ApplicationId): Either<Throwable, Unit> = Either.catch {
|
||||
val wallets = userWalletsListManager.userWalletsSync
|
||||
walletsRepository.associateWallets(applicationId.value, wallets)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue