Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-08 15:22:15 +04:00
parent ca2804b0e0
commit 133c89dd7e
3 changed files with 38 additions and 23 deletions

View file

@ -94,21 +94,23 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
override suspend fun push(
userWalletId: UserWalletId,
body: SaveWalletAccountsResponse,
): GetWalletAccountsResponse? {
return pushInternal(userWalletId = userWalletId, body = body)
}
private suspend fun pushInternal(
userWalletId: UserWalletId,
body: SaveWalletAccountsResponse,
eTag: String? = null,
): GetWalletAccountsResponse? {
return safeApiCall(
call = {
var eTag = getETag(userWalletId)
if (eTag == null) {
fetch(userWalletId)
eTag = getETag(userWalletId) ?: error("ETag is null after fetch")
}
val resolvedETag = eTag ?: getETagForPush(userWalletId)
val apiResponse = withContext(dispatchers.io) {
tangemTechApi.saveWalletAccounts(
walletId = userWalletId.stringValue,
eTag = eTag,
eTag = resolvedETag,
body = body,
)
}
@ -127,6 +129,19 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
)
}
private suspend fun getETagForPush(userWalletId: UserWalletId): String {
var savedETag = getETag(userWalletId)
if (savedETag == null) {
fetch(userWalletId)
savedETag = getETag(userWalletId)
?: error("Failed to retrieve ETag after fetching wallet accounts for wallet $userWalletId")
}
return savedETag
}
private suspend fun fetchWalletAccounts(
userWalletId: UserWalletId,
savedAccountsResponse: GetWalletAccountsResponse?,
@ -154,7 +169,13 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
error = throwable,
userWalletId = userWalletId,
savedAccountsResponse = savedAccountsResponse,
pushWalletAccounts = ::push,
pushWalletAccounts = { accounts, eTag ->
pushInternal(
userWalletId = userWalletId,
body = SaveWalletAccountsResponse(accounts),
eTag = eTag,
)
},
storeWalletAccounts = ::store,
)
},

View file

@ -2,7 +2,6 @@ package com.tangem.data.account.fetcher
import com.tangem.data.account.fetcher.DefaultWalletAccountsFetcher.FetchResult
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
import com.tangem.data.common.cache.etag.ETagsStore
import com.tangem.data.common.currency.UserTokensResponseAccountIdEnricher
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.datasource.api.common.response.ApiResponse
@ -29,10 +28,10 @@ import javax.inject.Singleton
* Handles errors that occur during the fetching of wallet accounts
*
* @property tangemTechApi API for network requests
* @property userWalletsStore provides access to user wallets storage
* @property userTokensSaver saves user tokens to the storage
* @property userTokensResponseStore provides access to user token responses.
* @property defaultWalletAccountsResponseFactory creates [GetWalletAccountsResponse] from [UserTokensResponse]
* @property eTagsStore store for ETags to manage caching
* @property dispatchers dispatchers
*
* @see DefaultWalletAccountsFetcher
@ -47,7 +46,6 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
private val userTokensSaver: UserTokensSaver,
private val userTokensResponseStore: UserTokensResponseStore,
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory,
private val eTagsStore: ETagsStore,
private val dispatchers: CoroutineDispatcherProvider,
) {
@ -66,7 +64,7 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
error: ApiResponseError,
userWalletId: UserWalletId,
savedAccountsResponse: GetWalletAccountsResponse?,
pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse?,
pushWalletAccounts: suspend (List<WalletAccountDTO>, String) -> GetWalletAccountsResponse?,
storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit,
): FetchResult {
val isResponseUpToDate = error.isNetworkError(code = Code.NOT_MODIFIED)
@ -87,9 +85,7 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
val eTag = createWallet(userWalletId)
if (eTag != null) {
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = eTag)
pushWalletAccounts(userWalletId, accountDTOs)
pushWalletAccounts(accountDTOs, eTag)
userTokensSaver.pushWithRetryer(userWalletId, userTokensResponse)
}
}

View file

@ -3,7 +3,6 @@ package com.tangem.data.account.fetcher
import com.tangem.data.account.converter.createGetWalletAccountsResponse
import com.tangem.data.account.converter.createWalletAccountDTO
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
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
@ -38,7 +37,6 @@ class FetchWalletAccountsErrorHandlerTest {
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true)
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory = mockk()
private val eTagsStore: ETagsStore = mockk(relaxUnitFun = true)
private val handler = FetchWalletAccountsErrorHandler(
tangemTechApi = tangemTechApi,
@ -46,17 +44,18 @@ class FetchWalletAccountsErrorHandlerTest {
userTokensSaver = userTokensSaver,
userTokensResponseStore = userTokensResponseStore,
defaultWalletAccountsResponseFactory = defaultWalletAccountsResponseFactory,
eTagsStore = eTagsStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
private val pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse =
private val pushWalletAccounts: suspend (List<WalletAccountDTO>, String) -> GetWalletAccountsResponse =
mockk(relaxed = true)
private val storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit = mockk(relaxed = true)
@BeforeEach
fun setupEach() {
clearMocks(
tangemTechApi,
userWalletsStore,
userTokensSaver,
userTokensResponseStore,
defaultWalletAccountsResponseFactory,
@ -129,7 +128,7 @@ class FetchWalletAccountsErrorHandlerTest {
),
)
} returns apiResponse
coEvery { pushWalletAccounts(userWalletId, listOf(accountDTO)) } returns savedAccountsResponse
coEvery { pushWalletAccounts(listOf(accountDTO), eTagValue) } returns savedAccountsResponse
// Act
handler.handle(
@ -150,8 +149,7 @@ class FetchWalletAccountsErrorHandlerTest {
walletType = WalletType.COLD,
),
)
eTagsStore.store(userWalletId, ETagsStore.Key.WalletAccounts, eTagValue)
pushWalletAccounts(userWalletId, listOf(accountDTO))
pushWalletAccounts(listOf(accountDTO), eTagValue)
storeWalletAccounts(userWalletId, savedAccountsResponse)
}