Updated on 2026-08-14
This commit is contained in:
parent
4fa5989289
commit
2d0bc9d9e6
12 changed files with 141 additions and 74 deletions
|
|
@ -1,12 +1,18 @@
|
|||
package com.tangem.tap.di.data
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.data.card.sdk.CardSdkOwner
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.operations.attestation.CardArtworksProvider
|
||||
import com.tangem.tap.data.DefaultCardSdkProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.io.File
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
|
@ -20,4 +26,22 @@ internal interface CardSdkModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun providerCardSdkLifecycleObserver(defaultCardSdkProvider: DefaultCardSdkProvider): CardSdkOwner
|
||||
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCardArtworksProvider(
|
||||
sdkRepository: CardSdkConfigRepository,
|
||||
@ApplicationContext context: Context,
|
||||
): CardArtworksProvider {
|
||||
return CardArtworksProvider(
|
||||
tangemApiBaseUrlProvider = { sdkRepository.sdk.config.tangemApiBaseUrl },
|
||||
artworksDirectory = File(
|
||||
context.getExternalFilesDir(null) ?: context.filesDir,
|
||||
"card_artworks",
|
||||
).apply { mkdirs() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
|
|||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.domain.card.DefaultDeleteSavedAccessCodesUseCase
|
||||
import com.tangem.tap.domain.card.DefaultResetCardUseCase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -56,7 +55,7 @@ internal object CardDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideDeleteSavedAccessCodesUseCase(tangemSdkManager: TangemSdkManager): DeleteSavedAccessCodesUseCase {
|
||||
return DefaultDeleteSavedAccessCodesUseCase(tangemSdkManager)
|
||||
return DeleteSavedAccessCodesUseCase(tangemSdkManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.tap.domain.card
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
|
||||
internal class DefaultDeleteSavedAccessCodesUseCase(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
) : DeleteSavedAccessCodesUseCase {
|
||||
|
||||
override suspend fun invoke(cardId: String): Either<Throwable, Unit> {
|
||||
tangemSdkManager.deleteSavedUserCodes(setOf(cardId))
|
||||
.doOnFailure { return it.left() }
|
||||
.doOnSuccess { return Unit.right() }
|
||||
|
||||
return Unit.right()
|
||||
}
|
||||
}
|
||||
|
|
@ -36,9 +36,9 @@ import com.tangem.tap.domain.userWalletList.utils.*
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import dagger.Lazy
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import com.tangem.utils.extensions.indexOfFirstOrNull
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
|
@ -96,8 +96,8 @@ internal class DefaultUserWalletsListRepository(
|
|||
?: loadedWallets.firstOrNull()?.also {
|
||||
selectedUserWalletRepository.set(it.walletId)
|
||||
}
|
||||
userWallets.value = loadedWallets
|
||||
setSelectedUserWallet(initialSelection)
|
||||
userWallets.value = loadedWallets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -235,7 +235,6 @@ internal class DefaultUserWalletsListRepository(
|
|||
)
|
||||
}
|
||||
|
||||
userWallets.value = updatedWallets
|
||||
if (currentSelected != null) {
|
||||
if (newSelected == null) {
|
||||
onAllWalletsDeleted()
|
||||
|
|
@ -243,6 +242,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
selectedUserWalletRepository.set(newSelected?.walletId)
|
||||
setSelectedUserWallet(newSelected)
|
||||
}
|
||||
userWallets.value = updatedWallets
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
|
|
|
|||
|
|
@ -197,6 +197,9 @@ internal class ResetCardModel @Inject constructor(
|
|||
|
||||
checkRemainingBackupCards()
|
||||
}
|
||||
.onLeft {
|
||||
TangemLogger.e("Failed to reset card: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ android {
|
|||
namespace = "com.tangem.domain.card"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.error)
|
||||
|
|
@ -25,6 +29,7 @@ dependencies {
|
|||
implementation(projects.domain.visa.models)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(projects.libs.tangemSdkApi)
|
||||
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.blockchain) {
|
||||
|
|
@ -32,9 +37,8 @@ dependencies {
|
|||
}
|
||||
|
||||
/** Testing libraries */
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testRuntimeOnly(deps.test.junit5.vintage.engine)
|
||||
testImplementation(projects.common.test)
|
||||
testImplementation(projects.test.core)
|
||||
}
|
||||
|
|
@ -1,8 +1,33 @@
|
|||
package com.tangem.domain.card
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
||||
interface DeleteSavedAccessCodesUseCase {
|
||||
/**
|
||||
* Removes saved user codes (access code and/or passcode) for a physical Tangem card
|
||||
* from the device's secure storage.
|
||||
*
|
||||
* Typically invoked after a successful factory reset of the card, so that stale codes
|
||||
* for an already-wiped card are not left on the device.
|
||||
*
|
||||
* @property tangemSdkManager Card SDK wrapper that performs the code removal operation
|
||||
*/
|
||||
class DeleteSavedAccessCodesUseCase(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(cardId: String): Either<Throwable, Unit>
|
||||
/**
|
||||
* @param cardId identifier of the card whose saved codes must be removed
|
||||
* @return [Unit] on success; a Card SDK error (as [Throwable]) if removal failed
|
||||
*/
|
||||
suspend operator fun invoke(cardId: String): Either<Throwable, Unit> = either {
|
||||
tangemSdkManager.deleteSavedUserCodes(cardsIds = setOf(cardId))
|
||||
.doOnFailure { error ->
|
||||
TangemLogger.e("Failed to delete saved access codes for card with id: $cardId", error)
|
||||
raise(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.domain.card
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.test.core.assertEitherLeft
|
||||
import com.tangem.test.core.assertEitherRight
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
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)
|
||||
internal class DeleteSavedAccessCodesUseCaseTest {
|
||||
|
||||
private val tangemSdkManager = mockk<TangemSdkManager>()
|
||||
|
||||
private lateinit var useCase: DeleteSavedAccessCodesUseCase
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(tangemSdkManager)
|
||||
useCase = DeleteSavedAccessCodesUseCase(tangemSdkManager = tangemSdkManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Right Unit when sdk deletes codes successfully`() = runTest {
|
||||
// Arrange
|
||||
val cardId = "AA00000000000001"
|
||||
coEvery { tangemSdkManager.deleteSavedUserCodes(setOf(cardId)) } returns CompletionResult.Success(Unit)
|
||||
|
||||
// Act
|
||||
val actual = useCase(cardId = cardId)
|
||||
|
||||
// Assert
|
||||
assertEitherRight(actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Left with sdk error when sdk fails`() = runTest {
|
||||
// Arrange
|
||||
val cardId = "AA00000000000002"
|
||||
val sdkError = TangemSdkError.ExceptionError(RuntimeException("boom"))
|
||||
coEvery { tangemSdkManager.deleteSavedUserCodes(setOf(cardId)) } returns CompletionResult.Failure(sdkError)
|
||||
|
||||
// Act
|
||||
val actual = useCase(cardId = cardId)
|
||||
|
||||
// Assert
|
||||
assertEitherLeft(actual, sdkError)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes exactly the given cardId as a singleton set to sdk`() = runTest {
|
||||
// Arrange
|
||||
val cardId = "AA00000000000003"
|
||||
coEvery { tangemSdkManager.deleteSavedUserCodes(any()) } returns CompletionResult.Success(Unit)
|
||||
|
||||
// Act
|
||||
useCase(cardId = cardId)
|
||||
|
||||
// Assert
|
||||
coVerify(exactly = 1) { tangemSdkManager.deleteSavedUserCodes(setOf(cardId)) }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import arrow.core.Either
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.DeleteWalletError
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
||||
/**
|
||||
* Use case for deleting user wallet
|
||||
|
|
@ -24,8 +25,10 @@ class DeleteWalletUseCase(
|
|||
* @return [Either] with [com.tangem.domain.common.wallets.error.DeleteWalletError] or [Boolean] which indicates that there are still saved wallets.
|
||||
* */
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Boolean> {
|
||||
return userWalletsListRepository.delete(userWalletIds = listOf(userWalletId)).map {
|
||||
userWalletsListRepository.selectedUserWallet.value != null
|
||||
return userWalletsListRepository.delete(userWalletIds = listOf(userWalletId))
|
||||
.map { userWalletsListRepository.selectedUserWallet.value != null }
|
||||
.onLeft {
|
||||
TangemLogger.e("Failed to delete wallet with id ${userWalletId.value}: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,6 +228,7 @@ test-espresso-intents = { module = "androidx.test.espresso:espresso-intents", ve
|
|||
test-junit = { module = "junit:junit", version.ref = "junit" }
|
||||
test-junit5 = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit5" }
|
||||
test-junit5-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" }
|
||||
test-junit5-vintage-engine = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" }
|
||||
test-junit-android = { module = "androidx.test.ext:junit", version.ref = "junitAndroidExt" }
|
||||
test-truth = { module = "com.google.truth:truth", version.ref = "truth" }
|
||||
test-mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ plugins {
|
|||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
|
@ -12,11 +11,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.common)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.card)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.visa.models)
|
||||
|
||||
implementation(projects.core.configToggles)
|
||||
|
|
@ -28,8 +23,6 @@ dependencies {
|
|||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
/** Other libraries */
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.sdk.api.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.operations.attestation.CardArtworksProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.io.File
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object CardSdkModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCardArtworksProvider(
|
||||
sdkRepository: CardSdkConfigRepository,
|
||||
@ApplicationContext context: Context,
|
||||
): CardArtworksProvider {
|
||||
return CardArtworksProvider(
|
||||
tangemApiBaseUrlProvider = { sdkRepository.sdk.config.tangemApiBaseUrl },
|
||||
artworksDirectory = File(
|
||||
context.getExternalFilesDir(null) ?: context.filesDir,
|
||||
"card_artworks",
|
||||
).apply { mkdirs() },
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue