Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-20 17:07:45 +03:00
parent ba36b7d26d
commit dfcfce392a
5 changed files with 76 additions and 6 deletions

View file

@ -6,6 +6,8 @@ import java.math.BigDecimal
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class TokenMarketChartResponse( data class TokenMarketChartResponse(
// There is a bug in the API, it returns null values.
// We need to filter them out.
@Json(name = "prices") @Json(name = "prices")
val prices: Map<Long, BigDecimal>, val prices: Map<Long, BigDecimal?>,
) )

View file

@ -103,6 +103,7 @@ internal class DefaultMarketsTokenRepository(
val tokenMarketsUpdateFetcher = MarketsBatchUpdateFetcher( val tokenMarketsUpdateFetcher = MarketsBatchUpdateFetcher(
tangemTechApi = tangemTechApi, tangemTechApi = tangemTechApi,
marketsApi = marketsApi, marketsApi = marketsApi,
analyticsEventHandler = analyticsEventHandler,
onApiError = { onApiError = {
analyticsEventHandler.send(MarketsDataAnalyticsEvent.List.Error.toEvent()) analyticsEventHandler.send(MarketsDataAnalyticsEvent.List.Error.toEvent())
}, },
@ -141,7 +142,19 @@ internal class DefaultMarketsTokenRepository(
response.getOrThrow() response.getOrThrow()
} }
return TokenChartConverter.convert(interval, result) return TokenChartConverter.convert(
interval = interval,
value = result,
// === Analytics ===
onNullPresented = {
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history",
),
)
},
)
} }
override suspend fun getChartPreview( override suspend fun getChartPreview(
@ -163,7 +176,19 @@ internal class DefaultMarketsTokenRepository(
) )
} }
return TokenChartConverter.convert(interval, chart) return TokenChartConverter.convert(
interval = interval,
value = chart,
// === Analytics ===
onNullPresented = {
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history_preview",
),
)
},
)
} }
override suspend fun getTokenInfo( override suspend fun getTokenInfo(

View file

@ -1,6 +1,8 @@
package com.tangem.data.markets package com.tangem.data.markets
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.common.utils.retryOnError import com.tangem.data.common.utils.retryOnError
import com.tangem.data.markets.analytics.MarketsDataAnalyticsEvent
import com.tangem.data.markets.converters.TokenMarketChartsConverter import com.tangem.data.markets.converters.TokenMarketChartsConverter
import com.tangem.data.markets.converters.TokenQuotesShortConverter import com.tangem.data.markets.converters.TokenQuotesShortConverter
import com.tangem.data.markets.converters.toRequestParam import com.tangem.data.markets.converters.toRequestParam
@ -22,6 +24,7 @@ import kotlinx.coroutines.launch
internal class MarketsBatchUpdateFetcher( internal class MarketsBatchUpdateFetcher(
private val marketsApi: TangemTechMarketsApi, private val marketsApi: TangemTechMarketsApi,
private val tangemTechApi: TangemTechApi, private val tangemTechApi: TangemTechApi,
private val analyticsEventHandler: AnalyticsEventHandler,
private val onApiError: () -> Unit, private val onApiError: () -> Unit,
) : BatchUpdateFetcher<Int, List<TokenMarket>, TokenMarketUpdateRequest> { ) : BatchUpdateFetcher<Int, List<TokenMarket>, TokenMarketUpdateRequest> {
@ -52,6 +55,7 @@ internal class MarketsBatchUpdateFetcher(
updateTasks.forEachIndexed { index, deferred -> updateTasks.forEachIndexed { index, deferred ->
launch { launch {
val res = deferred.await() val res = deferred.await()
checkForNulls(res)
val batchToUpdate = toUpdate[index] val batchToUpdate = toUpdate[index]
update { update {
@ -113,6 +117,22 @@ internal class MarketsBatchUpdateFetcher(
) )
} }
private fun checkForNulls(response: TokenMarketChartListResponse) {
response.values.forEach { chart ->
chart.prices.forEach { (_, price) ->
if (price == null) {
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history_preview",
),
)
return
}
}
}
}
private inline fun <T> catchApiError(onError: () -> Unit, block: () -> T): T { private inline fun <T> catchApiError(onError: () -> Unit, block: () -> T): T {
return try { return try {
block() block()

View file

@ -36,7 +36,20 @@ sealed interface MarketsDataAnalyticsEvent {
} }
fun toEvent(): AnalyticsEvent = when (this) { fun toEvent(): AnalyticsEvent = when (this) {
is ChartNullValuesError -> this
is List -> this is List -> this
is Details -> this is Details -> this
} }
data class ChartNullValuesError(
val requestPath: String,
) : AnalyticsEvent(
category = "Markets / Chart",
event = "Data Error",
params = mapOf("Request path" to requestPath),
error = IllegalStateException(
"Chart data contains null values from the API",
),
),
MarketsDataAnalyticsEvent
} }

View file

@ -6,11 +6,21 @@ import com.tangem.domain.markets.TokenChart
internal object TokenChartConverter { internal object TokenChartConverter {
fun convert(interval: PriceChangeInterval, value: TokenMarketChartResponse): TokenChart { fun convert(
interval: PriceChangeInterval,
value: TokenMarketChartResponse,
onNullPresented: () -> Unit = {},
): TokenChart {
val points = value.prices.mapNotNull { p -> p.value?.let { p.key to it } }.toMap()
if (points.size < points.values.size) {
onNullPresented()
}
return TokenChart( return TokenChart(
interval = interval, interval = interval,
priceY = value.prices.values.toList(), priceY = points.values.toList(),
timeStamps = value.prices.keys.toList(), timeStamps = points.keys.toList(),
) )
} }
} }