Updated on 2026-08-14
This commit is contained in:
parent
b5f0c9e097
commit
14c007d93a
4 changed files with 190 additions and 34 deletions
|
|
@ -14,29 +14,45 @@ tasks.withType<Test>().configureEach {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
// region Project - Core
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
api(projects.core.utils)
|
||||
// endregion
|
||||
|
||||
// region Project - Data
|
||||
implementation(projects.data.common)
|
||||
implementation(projects.data.tokens)
|
||||
// endregion
|
||||
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.quotes)
|
||||
implementation(projects.domain.wallets.models)
|
||||
// region Project - Domain
|
||||
api(projects.domain.models)
|
||||
api(projects.domain.quotes)
|
||||
// endregion
|
||||
|
||||
// region Project - Libs
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
// endregion
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
// endregion
|
||||
|
||||
// region Tangem SDKs
|
||||
implementation(tangemDeps.blockchain)
|
||||
// endregion
|
||||
|
||||
// region Other libraries
|
||||
implementation(deps.androidx.datastore)
|
||||
implementation(deps.moshi.kotlin)
|
||||
implementation(deps.timber)
|
||||
// endregion
|
||||
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
// region Tests
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(projects.common.test)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.data.common.api.safeApiCallWithTimeout
|
|||
import com.tangem.data.quotes.store.QuotesStatusesStore
|
||||
import com.tangem.data.quotes.store.setSourceAsCache
|
||||
import com.tangem.data.quotes.store.setSourceAsOnlyCache
|
||||
import com.tangem.data.tokens.utils.QuotesUnsupportedCurrenciesIdAdapter
|
||||
import com.tangem.data.quotes.utils.QuotesUnsupportedCurrenciesIdAdapter
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.appcurrency.AppCurrencyResponseStore
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
|
|
@ -35,8 +35,6 @@ internal class DefaultMultiQuoteFetcher @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MultiQuoteFetcher {
|
||||
|
||||
private val quotesUnsupportedCurrenciesAdapter = QuotesUnsupportedCurrenciesIdAdapter()
|
||||
|
||||
override suspend fun invoke(params: MultiQuoteFetcher.Params) = Either.catchOn(dispatchers.default) {
|
||||
if (params.currenciesIds.isEmpty()) {
|
||||
Timber.d("No currencies to fetch quotes for")
|
||||
|
|
@ -45,7 +43,7 @@ internal class DefaultMultiQuoteFetcher @Inject constructor(
|
|||
|
||||
quotesStore.setSourceAsCache(currenciesIds = params.currenciesIds)
|
||||
|
||||
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(
|
||||
val replacementIdsResult = QuotesUnsupportedCurrenciesIdAdapter.replaceUnsupportedCurrencies(
|
||||
currenciesIds = params.currenciesIds.mapTo(
|
||||
destination = hashSetOf(),
|
||||
transform = CryptoCurrency.RawID::value,
|
||||
|
|
@ -64,7 +62,7 @@ internal class DefaultMultiQuoteFetcher @Inject constructor(
|
|||
onError = { error -> throw error },
|
||||
)
|
||||
|
||||
val updatedResponse = quotesUnsupportedCurrenciesAdapter.getResponseWithUnsupportedCurrencies(
|
||||
val updatedResponse = QuotesUnsupportedCurrenciesIdAdapter.getResponseWithUnsupportedCurrencies(
|
||||
response = response,
|
||||
filteredIds = replacementIdsResult.idsFiltered,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
package com.tangem.data.quotes.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
|
||||
/**
|
||||
* Adapter to replace unsupported currencies for quotes request if it necessary
|
||||
*/
|
||||
class QuotesUnsupportedCurrenciesIdAdapter {
|
||||
/** Adapter to replace unsupported currencies for quotes request if it necessary */
|
||||
internal object QuotesUnsupportedCurrenciesIdAdapter {
|
||||
|
||||
/**
|
||||
* Replaces unsupported currencies id to it replacements for request
|
||||
*/
|
||||
/** Ethereum coin ID */
|
||||
private val ethCoinId = Blockchain.Ethereum.toCoinId()
|
||||
|
||||
/** Map that contains unsupported currencies and their replacement for request */
|
||||
private val UNSUPPORTED_IDS_WITH_REPLACEMENTS = l2BlockchainsCoinIds.associateWith { ethCoinId }
|
||||
|
||||
/** Replaces unsupported currencies id [currenciesIds] to it replacements for request */
|
||||
fun replaceUnsupportedCurrencies(currenciesIds: Set<String>): ReplacementResult {
|
||||
val idsForRequest = mutableSetOf<String>()
|
||||
val idsFiltered = mutableSetOf<String>()
|
||||
|
||||
currenciesIds.forEach { currencyId ->
|
||||
val replacementId = UNSUPPORTED_IDS_WITH_REPLACEMENTS[currencyId]
|
||||
|
||||
if (replacementId != null) {
|
||||
idsFiltered.add(currencyId)
|
||||
idsForRequest.add(replacementId)
|
||||
|
|
@ -25,16 +29,17 @@ class QuotesUnsupportedCurrenciesIdAdapter {
|
|||
idsForRequest.add(currencyId)
|
||||
}
|
||||
}
|
||||
|
||||
return ReplacementResult(idsForRequest = idsForRequest, idsFiltered = idsFiltered)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover previously replaced currencies to response that uses in application
|
||||
*/
|
||||
/** Recover previously replaced currencies [filteredIds] to [response] that uses in application */
|
||||
fun getResponseWithUnsupportedCurrencies(response: QuotesResponse, filteredIds: Set<String>): QuotesResponse {
|
||||
val updatedQuotes = mutableMapOf<String, QuotesResponse.Quote>()
|
||||
|
||||
filteredIds.forEach { filteredId ->
|
||||
val replacementId = UNSUPPORTED_IDS_WITH_REPLACEMENTS[filteredId]
|
||||
|
||||
if (replacementId != null) {
|
||||
val quoteReplacement = response.quotes[replacementId]
|
||||
if (quoteReplacement != null) {
|
||||
|
|
@ -42,16 +47,9 @@ class QuotesUnsupportedCurrenciesIdAdapter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response.copy(quotes = updatedQuotes + response.quotes)
|
||||
}
|
||||
|
||||
data class ReplacementResult(val idsForRequest: Set<String>, val idsFiltered: Set<String>)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Map that contains unsupported currencies and their replacement for request
|
||||
*/
|
||||
private val ethCoinId = Blockchain.Ethereum.toCoinId()
|
||||
private val UNSUPPORTED_IDS_WITH_REPLACEMENTS = l2BlockchainsCoinIds.associateWith { ethCoinId }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
package com.tangem.data.quotes.utils
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.common.test.data.quote.MockQuoteResponseFactory
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import com.tangem.data.quotes.utils.QuotesUnsupportedCurrenciesIdAdapter.ReplacementResult
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse.Quote
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class QuotesUnsupportedCurrenciesIdAdapterTest {
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class ReplaceUnsupportedCurrencies {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun replaceUnsupportedCurrencies(model: ReplaceUnsupportedCurrenciesModel) {
|
||||
// Act
|
||||
val actual = QuotesUnsupportedCurrenciesIdAdapter.replaceUnsupportedCurrencies(
|
||||
currenciesIds = model.currenciesIds,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = model.expected
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
ReplaceUnsupportedCurrenciesModel(
|
||||
currenciesIds = l2BlockchainsCoinIds.toSet(),
|
||||
expected = ReplacementResult(
|
||||
idsForRequest = l2BlockchainsCoinIds.mapTo(hashSetOf()) {
|
||||
Blockchain.Ethereum.toCoinId()
|
||||
},
|
||||
idsFiltered = l2BlockchainsCoinIds.toSet(),
|
||||
),
|
||||
),
|
||||
ReplaceUnsupportedCurrenciesModel(
|
||||
currenciesIds = notL2BlockchainsCoinIds,
|
||||
expected = ReplacementResult(idsForRequest = notL2BlockchainsCoinIds, idsFiltered = emptySet()),
|
||||
),
|
||||
ReplaceUnsupportedCurrenciesModel(
|
||||
currenciesIds = blockchainCoinIds,
|
||||
expected = ReplacementResult(
|
||||
idsForRequest = Blockchain.entries
|
||||
.filterNot(Blockchain::isTestnet)
|
||||
.mapTo(hashSetOf()) {
|
||||
if (it.isL2EthereumNetwork()) {
|
||||
Blockchain.Ethereum.toCoinId()
|
||||
} else {
|
||||
it.toCoinId()
|
||||
}
|
||||
},
|
||||
idsFiltered = l2BlockchainsCoinIds.toSet(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
data class ReplaceUnsupportedCurrenciesModel(
|
||||
val currenciesIds: Set<String>,
|
||||
val expected: ReplacementResult,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetResponseWithUnsupportedCurrencies {
|
||||
|
||||
private val zeroQuote = MockQuoteResponseFactory.createSinglePrice(value = BigDecimal.ZERO)
|
||||
private val ethQuote = "ethereum" to zeroQuote
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getResponseWithUnsupportedCurrencies(model: GetResponseWithUnsupportedCurrenciesModel) {
|
||||
// Act
|
||||
val actual = QuotesUnsupportedCurrenciesIdAdapter.getResponseWithUnsupportedCurrencies(
|
||||
response = model.response,
|
||||
filteredIds = model.filteredIds,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = model.expected
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
GetResponseWithUnsupportedCurrenciesModel(
|
||||
response = QuotesResponse(quotes = emptyMap()),
|
||||
filteredIds = emptySet(),
|
||||
expected = QuotesResponse(quotes = emptyMap()),
|
||||
),
|
||||
GetResponseWithUnsupportedCurrenciesModel(
|
||||
response = createQuotesResponse(ethQuote),
|
||||
filteredIds = emptySet(),
|
||||
expected = createQuotesResponse(ethQuote),
|
||||
),
|
||||
GetResponseWithUnsupportedCurrenciesModel(
|
||||
response = createQuotesResponse(
|
||||
"arbitrum-one" to MockQuoteResponseFactory.createSinglePrice(value = BigDecimal.ONE),
|
||||
ethQuote,
|
||||
),
|
||||
filteredIds = blockchainCoinIds,
|
||||
expected = createQuotesResponse(
|
||||
*l2BlockchainsCoinIds.map { it to zeroQuote }.toTypedArray(),
|
||||
"arbitrum-one" to MockQuoteResponseFactory.createSinglePrice(value = BigDecimal.ONE),
|
||||
ethQuote,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
private fun createQuotesResponse(vararg quotes: Pair<String, Quote>): QuotesResponse {
|
||||
return QuotesResponse(quotes = quotes.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
data class GetResponseWithUnsupportedCurrenciesModel(
|
||||
val response: QuotesResponse,
|
||||
val filteredIds: Set<String>,
|
||||
val expected: QuotesResponse,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
|
||||
val blockchainCoinIds = Blockchain.entries
|
||||
.filterNot(Blockchain::isTestnet)
|
||||
.mapTo(destination = hashSetOf(), transform = Blockchain::toCoinId)
|
||||
|
||||
val notL2BlockchainsCoinIds = Blockchain.entries
|
||||
.filterNot { it.isTestnet() || it.isL2EthereumNetwork() }
|
||||
.mapTo(hashSetOf()) { it.toCoinId() }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue