Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-10 10:15:44 +04:00
parent bb4afdec80
commit 16b33742ca
9 changed files with 686 additions and 12 deletions

View file

@ -119,9 +119,6 @@ interface TangemTechApi {
@Path("application_id") applicationId: String,
@Body body: NotificationApplicationCreateBody,
): ApiResponse<Unit>
@PATCH("v1/user-wallets/wallets/{wallet_id}/notify")
suspend fun setNotificationsEnabled(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit>
// endregion
// region user-wallets
@ -148,12 +145,15 @@ interface TangemTechApi {
// region account
@GET("/v1/wallets/{walletId}/accounts")
suspend fun getWalletAccounts(@Path("walletId") walletId: String): ApiResponse<GetWalletAccountsResponse>
suspend fun getWalletAccounts(
@Path("walletId") walletId: String,
@Header("If-None-Match") eTag: String? = null,
): ApiResponse<GetWalletAccountsResponse>
@PUT("/v1/wallets/{walletId}/accounts")
suspend fun saveWalletAccounts(
@Path("walletId") walletId: String,
@Header("If-Match") ifMatch: String,
@Header("If-Match") eTag: String,
@Body body: SaveWalletAccountsResponse,
): ApiResponse<Unit>

View file

@ -0,0 +1,6 @@
package com.tangem.datasource.utils
import androidx.datastore.core.DataStore
import kotlinx.coroutines.flow.firstOrNull
suspend fun <T> DataStore<T>.getSyncOrNull(): T? = data.firstOrNull()

View file

@ -0,0 +1,163 @@
package com.tangem.data.account.fetcher
import com.tangem.data.account.store.AccountsResponseStore
import com.tangem.data.account.store.AccountsResponseStoreFactory
import com.tangem.data.account.utils.assignTokens
import com.tangem.data.account.utils.toUserTokensResponse
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.account.WalletAccountsSaver
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.common.cache.etag.ETagsStore
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException.Code
import com.tangem.datasource.api.common.response.IF_NONE_MATCH_HEADER
import com.tangem.datasource.api.common.response.isNetworkError
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.datasource.utils.getSyncOrNull
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
/**
* Default implementation of [WalletAccountsFetcher] and [WalletAccountsSaver]
*
* @property tangemTechApi API for network requests
* @property accountsResponseStoreFactory factory to create [AccountsResponseStore]
* @property userTokensSaver saves user tokens to the database
* @property fetchWalletAccountsErrorHandler handles errors during fetching wallet accounts
* @property eTagsStore store for ETags to manage caching
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
internal class DefaultWalletAccountsFetcher(
private val tangemTechApi: TangemTechApi,
private val accountsResponseStoreFactory: AccountsResponseStoreFactory,
private val userTokensSaver: UserTokensSaver,
private val fetchWalletAccountsErrorHandler: FetchWalletAccountsErrorHandler,
private val eTagsStore: ETagsStore,
private val dispatchers: CoroutineDispatcherProvider,
) : WalletAccountsFetcher, WalletAccountsSaver {
override suspend fun fetch(userWalletId: UserWalletId) {
val savedAccountsResponse = getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
val accountsResponse = fetchWalletAccounts(userWalletId, savedAccountsResponse)
val unassignedTokens = accountsResponse?.unassignedTokens
if (!unassignedTokens.isNullOrEmpty()) {
assignTokens(userWalletId, accountsResponse)
}
}
override suspend fun pushAndStore(userWalletId: UserWalletId, response: GetWalletAccountsResponse) {
push(userWalletId = userWalletId, accounts = response.accounts)
store(userWalletId = userWalletId, response = response)
}
override suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse) {
val store = getAccountsResponseStore(userWalletId = userWalletId)
store.updateData { response }
}
override suspend fun push(userWalletId: UserWalletId, accounts: List<WalletAccountDTO>) {
push(userWalletId = userWalletId, body = SaveWalletAccountsResponse(accounts = accounts))
}
override suspend fun push(userWalletId: UserWalletId, body: SaveWalletAccountsResponse) {
safeApiCall(
call = {
var eTag = getETag(userWalletId)
if (eTag == null) {
fetch(userWalletId)
eTag = getETag(userWalletId) ?: error("ETag is null after fetch")
}
val apiResponse = withContext(dispatchers.io) {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = body,
)
}
saveETag(userWalletId, apiResponse)
apiResponse.bind()
},
onError = { error ->
if (error.isNetworkError(code = Code.PRECONDITION_FAILED)) {
throw error
}
},
)
}
private suspend fun fetchWalletAccounts(
userWalletId: UserWalletId,
savedAccountsResponse: GetWalletAccountsResponse?,
): GetWalletAccountsResponse? {
return safeApiCall(
call = {
val apiResponse = withContext(dispatchers.io) {
tangemTechApi.getWalletAccounts(
walletId = userWalletId.stringValue,
eTag = getETag(userWalletId),
)
}
saveETag(userWalletId, apiResponse)
val responseBody = apiResponse.bind()
store(userWalletId = userWalletId, response = responseBody)
responseBody
},
onError = {
// pushWalletAccounts and storeWalletAccounts help to avoid cyclic dependency
fetchWalletAccountsErrorHandler.handle(
error = it,
userWalletId = userWalletId,
savedAccountsResponse = savedAccountsResponse,
pushWalletAccounts = ::push,
storeWalletAccounts = ::store,
)
null
},
)
}
private suspend fun assignTokens(userWalletId: UserWalletId, accountsResponse: GetWalletAccountsResponse) {
val accountsResponseWithTokens = accountsResponse.assignTokens(userWalletId)
pushAndStore(userWalletId = userWalletId, response = accountsResponseWithTokens)
userTokensSaver.push(
userWalletId = userWalletId,
response = accountsResponseWithTokens.toUserTokensResponse(),
)
}
private suspend fun getETag(userWalletId: UserWalletId): String? {
return eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
}
private suspend fun saveETag(userWalletId: UserWalletId, apiResponse: ApiResponse<*>) {
val eTag = apiResponse.headers[IF_NONE_MATCH_HEADER]?.firstOrNull()
if (eTag != null) {
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = eTag)
}
}
private fun getAccountsResponseStore(userWalletId: UserWalletId): AccountsResponseStore {
return accountsResponseStoreFactory.create(userWalletId = userWalletId)
}
}

View file

@ -100,13 +100,13 @@ internal class DefaultAccountsCRUDRepository(
saveAccountsMutex.withLock {
val store = getAccountsResponseStore(userWalletId = accountList.userWallet.walletId)
val version = store.data.firstOrNull()?.wallet?.version ?: 0
store.data.firstOrNull()?.wallet?.version ?: 0
val body = SaveWalletAccountsResponseConverter.convert(value = accountList)
withContext(dispatchers.io) {
tangemTechApi.saveWalletAccounts(
walletId = accountList.userWallet.walletId.stringValue,
ifMatch = version.toString(),
eTag = "", // TODO("[REDACTED_JIRA]")
body = body,
)
.getOrThrow()

View file

@ -46,6 +46,7 @@ internal fun createGetWalletAccountsResponse(
iconColor: String? = null,
derivationIndex: Int? = null,
tokens: List<UserTokensResponse.Token>? = emptyList(),
unassignedTokens: List<UserTokensResponse.Token> = emptyList(),
): GetWalletAccountsResponse {
return GetWalletAccountsResponse(
wallet = GetWalletAccountsResponse.Wallet(
@ -66,7 +67,7 @@ internal fun createGetWalletAccountsResponse(
)
.let(::add)
},
unassignedTokens = emptyList(),
unassignedTokens = unassignedTokens,
)
}

View file

@ -0,0 +1,460 @@
package com.tangem.data.account.fetcher
import com.google.common.truth.Truth
import com.tangem.data.account.converter.createGetWalletAccountsResponse
import com.tangem.data.account.store.AccountsResponseStore
import com.tangem.data.account.store.AccountsResponseStoreFactory
import com.tangem.data.common.cache.etag.ETagsStore
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.IF_NONE_MATCH_HEADER
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.*
/**
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultWalletAccountsFetcherTest {
private val tangemTechApi: TangemTechApi = mockk()
private val accountsResponseStoreFactory: AccountsResponseStoreFactory = mockk()
private val accountsResponseStore: AccountsResponseStore = mockk()
private val accountsResponseStoreFlow = MutableStateFlow<GetWalletAccountsResponse?>(value = null)
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
private val fetchWalletAccountsErrorHandler: FetchWalletAccountsErrorHandler = mockk(relaxUnitFun = true)
private val eTagsStore: ETagsStore = mockk(relaxUnitFun = true)
private val fetcher: DefaultWalletAccountsFetcher = DefaultWalletAccountsFetcher(
tangemTechApi = tangemTechApi,
accountsResponseStoreFactory = accountsResponseStoreFactory,
userTokensSaver = userTokensSaver,
fetchWalletAccountsErrorHandler = fetchWalletAccountsErrorHandler,
eTagsStore = eTagsStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
private val userWalletId = UserWalletId("011")
private val eTag = "etag"
@BeforeAll
fun setUp() {
every { accountsResponseStoreFactory.create(userWalletId) } returns accountsResponseStore
every { accountsResponseStore.data } returns accountsResponseStoreFlow
coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag
}
@AfterEach
fun tearDown() {
clearMocks(
tangemTechApi,
userTokensSaver,
fetchWalletAccountsErrorHandler,
)
accountsResponseStoreFlow.value = null
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Fetch {
@Test
fun `fetch should call assignTokens when unassignedTokens are not empty`() = runTest {
// Arrange
val savedAccountsResponse = null
val unassignedToken = createToken(accountId = null)
val accountsResponse = createGetWalletAccountsResponse(
userWalletId = userWalletId,
unassignedTokens = listOf(unassignedToken),
)
val newETag = "newEtag"
val apiResponse = ApiResponse.Success(
data = accountsResponse,
headers = mapOf(IF_NONE_MATCH_HEADER to listOf(newETag)),
)
accountsResponseStoreFlow.value = savedAccountsResponse
val accountId = "957B88B12730E646E0F33D3618B77DFA579E8231E3C59C7104BE7165611C8027"
val updatedAccountsResponse = accountsResponse.copy(
accounts = accountsResponse.accounts.map {
it.copy(tokens = listOf(unassignedToken.copy(accountId = accountId)))
},
unassignedTokens = emptyList(),
)
coEvery {
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
} returns apiResponse
coEvery { accountsResponseStore.updateData(any()) } returns accountsResponse
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(updatedAccountsResponse.accounts),
)
} returns ApiResponse.Success(data = Unit)
// Act
fetcher.fetch(userWalletId)
// Assert
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.data
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = newETag)
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.updateData(any())
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(updatedAccountsResponse.accounts),
)
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.updateData(any())
}
coVerify(inverse = true) {
fetchWalletAccountsErrorHandler.handle(
error = any(),
userWalletId = any(),
savedAccountsResponse = any(),
pushWalletAccounts = any(),
storeWalletAccounts = any(),
)
}
}
@Test
fun `fetch should not call assignTokens when unassignedTokens are empty`() = runTest {
// Arrange
val savedAccountsResponse = null
val accountsResponse = createGetWalletAccountsResponse(
userWalletId = userWalletId,
unassignedTokens = emptyList(),
)
val newETag = "newEtag"
val apiResponse = ApiResponse.Success(
data = accountsResponse,
headers = mapOf(IF_NONE_MATCH_HEADER to listOf(newETag)),
)
accountsResponseStoreFlow.value = savedAccountsResponse
coEvery {
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
} returns apiResponse
coEvery { accountsResponseStore.updateData(any()) } returns accountsResponse
// Act
fetcher.fetch(userWalletId)
// Assert
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.data
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = newETag)
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.updateData(any())
}
coVerify(inverse = true) {
fetchWalletAccountsErrorHandler.handle(
error = any(),
userWalletId = any(),
savedAccountsResponse = any(),
pushWalletAccounts = any(),
storeWalletAccounts = any(),
)
tangemTechApi.saveWalletAccounts(walletId = any(), eTag = any(), body = any())
userTokensSaver.push(any(), any())
}
}
@Test
fun `fetch should call error handler when getWalletAccounts returns error`() = runTest {
// Arrange
val savedAccountsResponse = null
val apiError = ApiResponse.Error(ApiResponseError.NetworkException)
accountsResponseStoreFlow.value = savedAccountsResponse
coEvery {
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
} returns apiError as ApiResponse<GetWalletAccountsResponse>
// Act
fetcher.fetch(userWalletId)
// Assert
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.data
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
fetchWalletAccountsErrorHandler.handle(
error = apiError.cause,
userWalletId = userWalletId,
savedAccountsResponse = null,
pushWalletAccounts = any(),
storeWalletAccounts = any(),
)
}
coVerify(inverse = true) {
eTagsStore.store(userWalletId = any(), key = any(), value = any())
tangemTechApi.saveWalletAccounts(walletId = any(), eTag = any(), body = any())
userTokensSaver.push(userWalletId = any(), response = any())
}
}
@Test
fun `push should throw error when saveWalletAccounts returns PRECONDITION_FAILED`() = runTest {
// Arrange
val savedAccountsResponse = null
val unassignedToken = createToken(accountId = null)
val accountsResponse = createGetWalletAccountsResponse(
userWalletId = userWalletId,
unassignedTokens = listOf(unassignedToken),
)
val newETag = "newEtag"
val apiResponse = ApiResponse.Success(
data = accountsResponse,
headers = mapOf(IF_NONE_MATCH_HEADER to listOf(newETag)),
)
val apiError = ApiResponseError.HttpException(
code = ApiResponseError.HttpException.Code.PRECONDITION_FAILED,
message = null,
errorBody = null,
)
val saveApiResponse = ApiResponse.Error(apiError)
accountsResponseStoreFlow.value = savedAccountsResponse
val accountId = "957B88B12730E646E0F33D3618B77DFA579E8231E3C59C7104BE7165611C8027"
val updatedAccountsResponse = accountsResponse.copy(
accounts = accountsResponse.accounts.map {
it.copy(tokens = listOf(unassignedToken.copy(accountId = accountId)))
},
unassignedTokens = emptyList(),
)
coEvery {
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
} returns apiResponse
coEvery { accountsResponseStore.updateData(any()) } returns accountsResponse
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(updatedAccountsResponse.accounts),
)
} returns saveApiResponse as ApiResponse<Unit>
// Act
val actual = runCatching { fetcher.fetch(userWalletId) }.exceptionOrNull()!!
// Assert
Truth.assertThat(actual).isEqualTo(apiError)
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.data
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = newETag)
accountsResponseStoreFactory.create(userWalletId = userWalletId)
accountsResponseStore.updateData(any())
eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(updatedAccountsResponse.accounts),
)
}
coVerify(inverse = true) {
fetchWalletAccountsErrorHandler.handle(
error = any(),
userWalletId = any(),
savedAccountsResponse = any(),
pushWalletAccounts = any(),
storeWalletAccounts = any(),
)
}
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Store {
@Test
fun `store should update data in AccountsResponseStore`() = runTest {
// Arrange
val response = createGetWalletAccountsResponse(userWalletId = userWalletId)
coEvery { accountsResponseStore.updateData(any()) } returns response
// Act
fetcher.store(userWalletId = userWalletId, response = response)
// Assert
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId)
accountsResponseStore.updateData(any())
}
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Push {
@Test
fun `push should call saveWalletAccounts with correct params`() = runTest {
// Arrange
val accounts = listOf(
mockk<WalletAccountDTO>(),
)
val response = SaveWalletAccountsResponse(accounts)
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = response,
)
} returns ApiResponse.Success(data = Unit)
// Act
fetcher.push(userWalletId, response)
// Assert
coVerify {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = response,
)
}
}
@Test
fun `push should throw error when saveWalletAccounts returns PRECONDITION_FAILED`() = runTest {
// Arrange
val accounts = listOf(
mockk<WalletAccountDTO>(),
)
val response = SaveWalletAccountsResponse(accounts)
val apiError = ApiResponseError.HttpException(
code = ApiResponseError.HttpException.Code.PRECONDITION_FAILED,
message = null,
errorBody = null,
)
val saveApiResponse = ApiResponse.Error(apiError)
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = response,
)
} returns saveApiResponse as ApiResponse<Unit>
// Act
val actual = runCatching { fetcher.push(userWalletId, response) }.exceptionOrNull()!!
// Assert
Truth.assertThat(actual).isEqualTo(apiError)
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class PushAndStore {
@Test
fun `pushAndStore should call push and store with correct params`() = runTest {
// Arrange
listOf(
mockk<WalletAccountDTO>(),
)
val response = createGetWalletAccountsResponse(userWalletId)
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(accounts = response.accounts),
)
} returns ApiResponse.Success(data = Unit)
coEvery { accountsResponseStore.updateData(any()) } returns mockk()
// Act
fetcher.pushAndStore(userWalletId, response)
// Assert
coVerifyOrder {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
body = SaveWalletAccountsResponse(accounts = response.accounts),
)
accountsResponseStoreFactory.create(userWalletId)
accountsResponseStore.updateData(any())
}
}
}
private fun createToken(
networkId: String = "ethereum",
derivationPath: String = "m/44'/60'/0'/0/0",
list: List<String> = emptyList(),
name: String = "Ethereum",
symbol: String = "ETH",
decimals: Int = 18,
contractAddress: String? = null,
id: String? = null,
accountId: String? = null,
) = UserTokensResponse.Token(
id = id,
accountId = accountId,
networkId = networkId,
derivationPath = derivationPath,
name = name,
symbol = symbol,
decimals = decimals,
contractAddress = contractAddress,
addresses = list,
)
}

View file

@ -585,7 +585,7 @@ class DefaultAccountsCRUDRepositoryTest {
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
ifMatch = version.toString(),
eTag = "",
body = body,
)
} returns apiResponse
@ -609,7 +609,7 @@ class DefaultAccountsCRUDRepositoryTest {
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId)
accountsResponseStore.data
tangemTechApi.saveWalletAccounts(userWalletId.stringValue, version.toString(), body)
tangemTechApi.saveWalletAccounts(userWalletId.stringValue, "", body)
convertersContainer.getWalletAccountsResponseCF.create(userWallet)
converter.convert(accountList)
accountsResponseStore.updateData(any())
@ -638,7 +638,7 @@ class DefaultAccountsCRUDRepositoryTest {
coEvery {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
ifMatch = version.toString(),
eTag = "",
body = body,
)
} returns apiResponse
@ -652,7 +652,7 @@ class DefaultAccountsCRUDRepositoryTest {
coVerifyOrder {
accountsResponseStoreFactory.create(userWalletId)
accountsResponseStore.data
tangemTechApi.saveWalletAccounts(userWalletId.stringValue, version.toString(), body)
tangemTechApi.saveWalletAccounts(userWalletId.stringValue, "", body)
}
coVerify(inverse = true) {

View file

@ -0,0 +1,15 @@
package com.tangem.data.common.account
import com.tangem.domain.models.wallet.UserWalletId
/**
* Component for fetching wallet accounts
*
[REDACTED_AUTHOR]
*/
interface WalletAccountsFetcher {
/** Fetch wallet accounts by [userWalletId] */
@Throws
suspend fun fetch(userWalletId: UserWalletId)
}

View file

@ -0,0 +1,29 @@
package com.tangem.data.common.account
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.domain.models.wallet.UserWalletId
/**
* Saver for wallet accounts
*
[REDACTED_AUTHOR]
*/
interface WalletAccountsSaver {
/** Push and store wallet accounts [response] by [userWalletId] */
@Throws
suspend fun pushAndStore(userWalletId: UserWalletId, response: GetWalletAccountsResponse)
/** Store wallet accounts [response] by [userWalletId] */
suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse)
/** Push wallet accounts [body] by [userWalletId] */
@Throws
suspend fun push(userWalletId: UserWalletId, body: SaveWalletAccountsResponse)
/** Push wallet accounts [accounts] by [userWalletId] */
@Throws
suspend fun push(userWalletId: UserWalletId, accounts: List<WalletAccountDTO>)
}