Updated on 2026-08-14
This commit is contained in:
parent
9e948f7703
commit
147625bda1
40 changed files with 254 additions and 131 deletions
|
|
@ -1,20 +1,20 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.core.analytics.api.ErrorEventLogger
|
||||
import com.tangem.core.analytics.api.ExceptionLogger
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
import timber.log.Timber
|
||||
|
||||
class AnalyticsEventsLogger(
|
||||
private val name: String,
|
||||
private val jsonConverter: MoshiJsonConverter,
|
||||
) : EventLogger, ErrorEventLogger {
|
||||
) : EventLogger, ExceptionLogger {
|
||||
|
||||
override fun logEvent(event: String, params: Map<String, String>) {
|
||||
Timber.d(jsonConverter.prettyPrint(PrintEventModel(name, event, params)))
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
Timber.e(error, jsonConverter.prettyPrint(PrintEventModel(name, "error", params)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ internal sealed class Push(event: String) : AnalyticsEvent(
|
|||
category = "Push",
|
||||
event = event,
|
||||
params = emptyMap(),
|
||||
error = null,
|
||||
) {
|
||||
|
||||
data object PushNotificationOpened : Push(event = "Push Notification Opened")
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ sealed class Settings(
|
|||
category: String = "Settings",
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class ScreenOpened : Settings(event = "Settings Screen Opened")
|
||||
class ButtonStartWalletConnectSession : Settings(event = "Button - Start Wallet Connect Session")
|
||||
|
|
@ -19,16 +18,14 @@ sealed class Settings(
|
|||
sealed class CardSettings(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : Settings("Settings / Card Settings", event, params, error) {
|
||||
) : Settings("Settings / Card Settings", event, params) {
|
||||
|
||||
class ButtonFactoryReset : CardSettings("Button - Factory Reset")
|
||||
class FactoryResetFinished(cardsCount: Int? = null, error: Throwable? = null) : CardSettings(
|
||||
class FactoryResetFinished(cardsCount: Int? = null) : CardSettings(
|
||||
event = "Factory Reset Finished",
|
||||
params = buildMap {
|
||||
cardsCount?.let { put("Cards Count", "$it") }
|
||||
},
|
||||
error = error,
|
||||
)
|
||||
|
||||
class FactoryResetCanceled(cardsCount: Int) : CardSettings(
|
||||
|
|
@ -44,10 +41,9 @@ sealed class Settings(
|
|||
params = mapOf("Type" to type.value),
|
||||
)
|
||||
|
||||
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode, error: Throwable? = null) : CardSettings(
|
||||
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode) : CardSettings(
|
||||
event = "Security Mode Changed",
|
||||
params = mapOf("Mode" to mode.value),
|
||||
error = error,
|
||||
)
|
||||
|
||||
class AccessCodeRecoveryChanged(status: AnalyticsParam.AccessCodeRecoveryStatus) : CardSettings(
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Sign In", event, params, error) {
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
|
||||
class ScreenOpened : SignIn(event = "Sign In Screen Opened")
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ sealed class Token(
|
|||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
sealed class Receive(
|
||||
event: String,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.O
|
|||
internal sealed class WalletConnect(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Wallet Connect", event, params, error) {
|
||||
) : AnalyticsEvent("Wallet Connect", event, params) {
|
||||
|
||||
class ScreenOpened : WalletConnect(event = "WC Screen Opened")
|
||||
class NewSessionInitiated(source: SourceType) : WalletConnect(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.tap.common.analytics.handlers.amplitude
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
|
||||
class AmplitudeAnalyticsHandler(
|
||||
private val client: AmplitudeAnalyticsClient,
|
||||
) : AnalyticsHandler {
|
||||
) : AnalyticsHandler, AnalyticsErrorHandler {
|
||||
|
||||
override fun id(): String = ID
|
||||
|
||||
|
|
@ -13,6 +15,10 @@ class AmplitudeAnalyticsHandler(
|
|||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun sendErrorEvent(event: AnalyticsEvent) {
|
||||
send(event.id, event.params)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ID = "Amplitude"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
package com.tangem.tap.common.analytics.handlers.firebase
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.api.ErrorEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
|
||||
class FirebaseAnalyticsHandler(
|
||||
private val client: FirebaseAnalyticsClient,
|
||||
) : AnalyticsHandler, ErrorEventHandler {
|
||||
) : AnalyticsHandler, AnalyticsErrorHandler, AnalyticsExceptionHandler {
|
||||
|
||||
private val errorConverter = AnalyticsErrorConverter()
|
||||
|
||||
override fun id(): String = ID
|
||||
|
||||
|
|
@ -18,30 +20,17 @@ class FirebaseAnalyticsHandler(
|
|||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
val error = event.error
|
||||
when {
|
||||
error != null -> {
|
||||
val errorConverter = AnalyticsErrorConverter()
|
||||
if (!errorConverter.canBeHandled(error)) return
|
||||
override fun sendException(event: ExceptionAnalyticsEvent) {
|
||||
if (!errorConverter.canBeHandled(event.exception)) return
|
||||
|
||||
val errorParams = errorConverter.convert(error).toMutableMap()
|
||||
errorParams["Category"] = event.category
|
||||
errorParams["Event"] = event.event
|
||||
errorParams.putAll(event.params)
|
||||
send(error, errorParams)
|
||||
}
|
||||
event is Shop.Purchased -> {
|
||||
send(FirebaseAnalytics.Event.PURCHASE, event.params)
|
||||
}
|
||||
else -> {
|
||||
super.send(event)
|
||||
}
|
||||
}
|
||||
val errorParams = errorConverter.convert(event.exception).toMutableMap()
|
||||
errorParams.putAll(event.params)
|
||||
|
||||
client.logException(event.exception, event.params)
|
||||
}
|
||||
|
||||
override fun send(error: Throwable, params: Map<String, String>) {
|
||||
client.logErrorEvent(error, params)
|
||||
override fun sendErrorEvent(event: AnalyticsEvent) {
|
||||
send(event)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@ import android.os.Bundle
|
|||
import androidx.core.os.bundleOf
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||
import com.google.firebase.crashlytics.recordException
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.tangem.core.analytics.api.ErrorEventLogger
|
||||
import com.tangem.core.analytics.api.ExceptionLogger
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface FirebaseAnalyticsClient : EventLogger, ErrorEventLogger
|
||||
interface FirebaseAnalyticsClient : EventLogger, ExceptionLogger
|
||||
|
||||
internal class FirebaseClient : FirebaseAnalyticsClient {
|
||||
|
||||
|
|
@ -27,11 +28,12 @@ internal class FirebaseClient : FirebaseAnalyticsClient {
|
|||
)
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
eventConverter.convertEventParams(params)
|
||||
.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
|
||||
|
||||
fbCrashlytics.recordException(error)
|
||||
// TODO [REDACTED_TASK_KEY] fix recordException usages in the app
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
fbCrashlytics.recordException(error) {
|
||||
eventConverter.convertEventParams(params)
|
||||
.forEach { key(it.key, it.value) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<String, String>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ internal class FirebaseLogClient(
|
|||
logger.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
logger.logErrorEvent(error, params)
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
logger.logException(error, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@ import com.tangem.common.authentication.AuthenticationManager
|
|||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.Config
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.crypto.bip39.Wordlist
|
||||
import com.tangem.data.card.sdk.CardSdkOwner
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
|
|
@ -33,7 +34,7 @@ import javax.inject.Singleton
|
|||
*/
|
||||
@Singleton
|
||||
internal class DefaultCardSdkProvider @Inject constructor(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardSdkFeatureToggles: CardSdkFeatureToggles,
|
||||
) : CardSdkProvider, CardSdkOwner {
|
||||
|
|
@ -48,7 +49,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
override fun register(activity: FragmentActivity) = runBlocking(dispatchers.mainImmediate) {
|
||||
if (activity.isDestroyed || activity.isFinishing || activity.isChangingConfigurations) {
|
||||
val message = "Tangem SDK owner registration skipped: activity is destroyed or finishing"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(message))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(message),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.info { message }
|
||||
return@runBlocking
|
||||
}
|
||||
|
|
@ -66,14 +72,24 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
|
||||
private fun tryToRegisterWithForegroundActivity(): TangemSdk = runBlocking(dispatchers.mainImmediate) {
|
||||
val warning = "Tangem SDK holder is null, trying to recreate it with foreground activity"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(warning))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(warning),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.warning { warning }
|
||||
|
||||
val activity = foregroundActivityObserver.foregroundActivity
|
||||
|
||||
if (activity == null) {
|
||||
val error = "Tangem SDK holder is null and foreground activity is null"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(error))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(error),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.error { error }
|
||||
error(error)
|
||||
}
|
||||
|
|
@ -84,7 +100,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
|
||||
if (sdk == null) {
|
||||
val error = "Tangem SDK is null after re-registering with foreground activity"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(error))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(error),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.error { error }
|
||||
error(error)
|
||||
}
|
||||
|
|
@ -174,5 +195,10 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
),
|
||||
),
|
||||
)
|
||||
|
||||
val errorParams = mapOf(
|
||||
"Category" to "Tangem SDK",
|
||||
"Event" to "Warning",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.tap.data
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
internal class TangemSdkWarningEvent(message: String) : AnalyticsEvent(
|
||||
category = "Tangem SDK",
|
||||
event = "Warning",
|
||||
error = IllegalStateException(message),
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.di.routing
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.tap.routing.ProxyAppRouter
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.tap.routing.configurator.MutableAppRouterConfig
|
||||
|
|
@ -17,8 +18,15 @@ internal object AppRouterModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRouter(config: AppRouterConfig, dispatchers: CoroutineDispatcherProvider): AppRouter =
|
||||
ProxyAppRouter(config, dispatchers)
|
||||
fun provideAppRouter(
|
||||
config: AppRouterConfig,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
): AppRouter = ProxyAppRouter(
|
||||
config = config,
|
||||
dispatchers = dispatchers,
|
||||
analyticsExceptionHandler = analyticsExceptionHandler,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.domain.card.ScanCardException
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
|
|
@ -34,7 +35,12 @@ internal object UseCaseScanProcessor {
|
|||
ifLeft = {
|
||||
val error = scanCardExceptionConverter.convertBack(it)
|
||||
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = error,
|
||||
params = mapOf("Event" to "Scan"),
|
||||
),
|
||||
)
|
||||
CompletionResult.Failure(error)
|
||||
},
|
||||
ifRight = { CompletionResult.Success(it) },
|
||||
|
|
@ -97,7 +103,12 @@ internal object UseCaseScanProcessor {
|
|||
-> {
|
||||
val error = scanCardExceptionConverter.convertBack(exception)
|
||||
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = error,
|
||||
params = mapOf("Event" to "Scan"),
|
||||
),
|
||||
)
|
||||
onFailure(error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import com.tangem.common.extensions.ByteArrayKey
|
|||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.common.usersCode.UserCodeRepository
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
|
|
@ -224,7 +224,12 @@ internal class DefaultTangemSdkManager(
|
|||
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
|
||||
if (result is CompletionResult.Failure) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = error,
|
||||
params = mapOf("Event" to "Scan"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import androidx.compose.runtime.Stable
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -30,6 +32,8 @@ internal class SecurityModeModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val cardSettingsInteractor: CardSettingsInteractor,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : Model() {
|
||||
|
||||
private val scannedScanResponse = cardSettingsInteractor.scannedScanResponse.value
|
||||
|
|
@ -85,14 +89,19 @@ internal class SecurityModeModel @Inject constructor(
|
|||
val paramValue = AnalyticsParam.SecurityMode.from(selectedOption)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue))
|
||||
analyticsEventHandler.send(Settings.CardSettings.SecurityModeChanged(paramValue))
|
||||
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
val error = result.error
|
||||
if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
|
||||
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue, error))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = error,
|
||||
params = mapOf("Event" to "Security Mode Changed"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
|
@ -12,6 +11,7 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.common.services.Result
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.ScreensSources
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.toWrappedList
|
||||
|
|
@ -454,8 +454,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
store.dispatchOnMain(BackupAction.AddBackupCard.Success(result.data))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
val crashlytics = FirebaseCrashlytics.getInstance()
|
||||
|
||||
when (val error = result.error) {
|
||||
is TangemSdkError.CardVerificationFailed -> {
|
||||
Analytics.send(
|
||||
|
|
@ -492,7 +490,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
GlobalAction.ShowDialog(BackupDialog.AttestationFailed),
|
||||
)
|
||||
}
|
||||
else -> crashlytics.recordException(error)
|
||||
else -> Analytics.sendException(ExceptionAnalyticsEvent(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.tap.routing
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -15,6 +16,7 @@ import kotlin.reflect.KClass
|
|||
internal class ProxyAppRouter(
|
||||
private val config: AppRouterConfig,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : AppRouter {
|
||||
|
||||
private val routerScope: CoroutineScope
|
||||
|
|
@ -76,7 +78,7 @@ internal class ProxyAppRouter(
|
|||
|
||||
override fun defaultCompletionHandler(isSuccess: Boolean, errorMessage: String) {
|
||||
if (!isSuccess) {
|
||||
FirebaseCrashlytics.getInstance().recordException(RuntimeException(errorMessage))
|
||||
analyticsExceptionHandler.sendException(ExceptionAnalyticsEvent(RuntimeException(errorMessage)))
|
||||
Timber.w(errorMessage)
|
||||
|
||||
with(receiver = config.snackbarHandler ?: return) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ open class AnalyticsEvent(
|
|||
val category: String,
|
||||
val event: String,
|
||||
var params: Map<String, String> = mapOf(),
|
||||
val error: Throwable? = null,
|
||||
) {
|
||||
|
||||
val id: String = "[$category] $event"
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ package com.tangem.core.analytics.models
|
|||
sealed class Basic(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Basic", event, params, error) {
|
||||
) : AnalyticsEvent("Basic", event, params) {
|
||||
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScreensSources,
|
||||
|
|
@ -73,11 +72,6 @@ sealed class Basic(
|
|||
}
|
||||
}
|
||||
|
||||
class ScanError(error: Throwable) : Basic(
|
||||
event = "Scan",
|
||||
error = error,
|
||||
)
|
||||
|
||||
class ButtonSupport(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Request Support",
|
||||
params = mapOf(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.analytics.models
|
||||
|
||||
class ExceptionAnalyticsEvent(
|
||||
val exception: Throwable,
|
||||
// val category: String,
|
||||
// val event: String,
|
||||
var params: Map<String, String> = mapOf(),
|
||||
)
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.core.analytics
|
|||
|
||||
import com.tangem.core.analytics.api.*
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
|
@ -16,7 +17,9 @@ interface GlobalAnalyticsEventHandler :
|
|||
AnalyticsEventHandler,
|
||||
AnalyticsHandlerHolder,
|
||||
AnalyticsFilterHolder,
|
||||
ParamsInterceptorHolder
|
||||
ParamsInterceptorHolder,
|
||||
AnalyticsErrorHandler,
|
||||
AnalyticsExceptionHandler
|
||||
|
||||
object Analytics : GlobalAnalyticsEventHandler {
|
||||
|
||||
|
|
@ -72,6 +75,25 @@ object Analytics : GlobalAnalyticsEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun sendErrorEvent(event: AnalyticsEvent) {
|
||||
analyticsScope.launch {
|
||||
event.params = applyParamsInterceptors(event)
|
||||
analyticsMutex.withLock {
|
||||
analyticsHandlers.filterIsInstance<AnalyticsErrorHandler>()
|
||||
.forEach { handler -> handler.sendErrorEvent(event) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendException(event: ExceptionAnalyticsEvent) {
|
||||
analyticsScope.launch {
|
||||
analyticsMutex.withLock {
|
||||
analyticsHandlers.filterIsInstance<AnalyticsExceptionHandler>()
|
||||
.forEach { it.sendException(event) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun applyParamsInterceptors(event: AnalyticsEvent): MutableMap<String, String> {
|
||||
val interceptedParams = event.params.toMutableMap()
|
||||
analyticsMutex.withLock {
|
||||
|
|
|
|||
|
|
@ -8,5 +8,9 @@ interface EventLogger {
|
|||
}
|
||||
|
||||
interface ErrorEventLogger {
|
||||
fun logErrorEvent(error: Throwable, params: Map<String, String> = emptyMap())
|
||||
fun logErrorEvent(event: String, params: Map<String, String> = emptyMap())
|
||||
}
|
||||
|
||||
interface ExceptionLogger {
|
||||
fun logException(error: Throwable, params: Map<String, String> = emptyMap())
|
||||
}
|
||||
|
|
@ -1,15 +1,25 @@
|
|||
package com.tangem.core.analytics.api
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
* Sends usual analytics events
|
||||
*/
|
||||
interface AnalyticsEventHandler {
|
||||
fun send(event: AnalyticsEvent)
|
||||
}
|
||||
|
||||
interface AnalyticsErrorHandler {
|
||||
fun sendErrorEvent(event: AnalyticsEvent)
|
||||
}
|
||||
|
||||
interface AnalyticsExceptionHandler {
|
||||
fun sendException(event: ExceptionAnalyticsEvent)
|
||||
}
|
||||
|
||||
interface AnalyticsHandler : AnalyticsEventHandler {
|
||||
|
||||
fun id(): String
|
||||
|
||||
fun send(eventId: String, params: Map<String, String> = emptyMap())
|
||||
|
|
@ -19,10 +29,6 @@ interface AnalyticsHandler : AnalyticsEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
interface ErrorEventHandler {
|
||||
fun send(error: Throwable, params: Map<String, String> = emptyMap())
|
||||
}
|
||||
|
||||
interface AnalyticsHandlerHolder {
|
||||
fun addHandler(name: String, handler: AnalyticsHandler)
|
||||
fun removeHandler(name: String): AnalyticsHandler?
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.core.analytics.di
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.api.ParamsInterceptorHolder
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.domain.analytics.repository.AnalyticsRepository
|
||||
|
|
@ -21,6 +23,18 @@ internal object AnalyticsModule {
|
|||
return Analytics // todo replace after refactoring calling Analytics in whole project
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAnalyticsErrorHandler(): AnalyticsErrorHandler {
|
||||
return Analytics
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAnalyticsExceptionHandler(): AnalyticsExceptionHandler {
|
||||
return Analytics
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideParamsInterceptorHolder(): ParamsInterceptorHolder {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
|||
class TokenExchangeAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Token", event, params, null) {
|
||||
) : AnalyticsEvent("Token", event, params) {
|
||||
|
||||
class CexTxStatusOpened(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Swap Status Opened",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
|||
class TokenOnrampAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Onramp", event, params, null) {
|
||||
) : AnalyticsEvent("Onramp", event, params) {
|
||||
|
||||
class OnrampStatusOpened(tokenSymbol: String, provider: String, fiatCurrency: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Onramp Status Opened",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
|||
sealed class TokenReceiveAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Token / Receive", event, params, null) {
|
||||
) : AnalyticsEvent("Token / Receive", event, params) {
|
||||
|
||||
class ReceiveScreenOpened(token: String) : TokenReceiveAnalyticsEvent(
|
||||
event = "Receive Screen Opened",
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
|||
sealed class TokenScreenAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Token", event, params, error) {
|
||||
) : AnalyticsEvent("Token", event, params) {
|
||||
|
||||
/** Legacy event. It has a unique category, but it also is sent on TokenScreen */
|
||||
class DetailsScreenOpened(token: String) : AnalyticsEvent(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -48,6 +49,7 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val cardRepository: CardRepository,
|
||||
|
|
@ -241,7 +243,7 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
else -> FirebaseCrashlytics.getInstance().recordException(result.error)
|
||||
else -> analyticsExceptionHandler.sendException(ExceptionAnalyticsEvent(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ sealed class SwapEvents(
|
|||
"Error Code" to errorCode.toString(),
|
||||
),
|
||||
)
|
||||
// TODO parameters
|
||||
|
||||
// region Promo activity
|
||||
data class ChangellyActivity(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ internal sealed class Settings(
|
|||
category: String = "Settings",
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
data object ButtonCreateBackup : Settings(event = "Button - Create Backup")
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
sealed class Basic(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category = "Basic", event = event, params = params, error = error) {
|
||||
) : AnalyticsEvent(category = "Basic", event = event, params = params) {
|
||||
|
||||
class WalletToppedUp(userWalletId: UserWalletId, walletType: AnalyticsParam.WalletType) :
|
||||
Basic(
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ coil = "2.1.0"
|
|||
compose-shimmer = "1.0.3"
|
||||
coroutine = "1.7.2" # 1.8+ is not compatible with tangem-sdk
|
||||
desugarJdkLibs = "1.1.5"
|
||||
firebase = "33.7.0"
|
||||
firebase = "33.10.0"
|
||||
googleMaterialComponent = "1.6.1"
|
||||
googlePlayReview = "2.0.1"
|
||||
googlePlayReviewKtx = "2.0.1"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ dependencies {
|
|||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.analytics)
|
||||
// endregion
|
||||
|
||||
// region AndroidX libraries
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.blockchainsdk.di
|
||||
|
||||
import com.tangem.blockchainsdk.providers.BlockchainProvidersResponseMerger
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -17,4 +19,12 @@ internal object ExcludedBlockchainsModule {
|
|||
fun bindExcludedBlockchains(excludedBlockchainsManager: ExcludedBlockchainsManager): ExcludedBlockchains {
|
||||
return ExcludedBlockchains(excludedBlockchainsManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun bindBlockchainProvidersResponseMerger(
|
||||
analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
): BlockchainProvidersResponseMerger {
|
||||
return BlockchainProvidersResponseMerger(analyticsExceptionHandler)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import javax.inject.Singleton
|
|||
internal class BlockchainProvidersResponseLoader @Inject constructor(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val blockchainProvidersStorage: BlockchainProvidersStorage,
|
||||
private val blockchainProvidersResponseMerger: BlockchainProvidersResponseMerger,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ internal class BlockchainProvidersResponseLoader @Inject constructor(
|
|||
|
||||
return loadRemote().fold(
|
||||
onSuccess = { remoteResponse ->
|
||||
BlockchainProvidersResponseMerger.merge(
|
||||
blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteResponse,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
package com.tangem.blockchainsdk.providers
|
||||
|
||||
import androidx.core.util.PatternsCompat
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchainsdk.BlockchainProvidersResponse
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Merger of [BlockchainProvidersResponse]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object BlockchainProvidersResponseMerger {
|
||||
|
||||
private val firebaseCrashlytics by lazy(FirebaseCrashlytics::getInstance)
|
||||
internal class BlockchainProvidersResponseMerger @Inject internal constructor(
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) {
|
||||
|
||||
private val forbiddenSchemes = listOf("wss://")
|
||||
|
||||
|
|
@ -41,7 +43,7 @@ internal object BlockchainProvidersResponseMerger {
|
|||
val missingBlockchains = result.keys - remote.keys
|
||||
val blockchainsWithoutProviders = remote.filterValues { it.isEmpty() }.keys
|
||||
|
||||
recordException(missingBlockchains = missingBlockchains + blockchainsWithoutProviders)
|
||||
logException(missingBlockchains = missingBlockchains + blockchainsWithoutProviders)
|
||||
}
|
||||
|
||||
return result.guaranteeUrlsEndWithSlash()
|
||||
|
|
@ -74,15 +76,19 @@ internal object BlockchainProvidersResponseMerger {
|
|||
return PatternsCompat.WEB_URL.matcher(inputUrl).matches()
|
||||
}
|
||||
|
||||
private fun recordException(missingBlockchains: Set<String>) {
|
||||
private fun logException(missingBlockchains: Set<String>) {
|
||||
val exception = IllegalStateException(
|
||||
"Remote config does not contain required blockchains or providers information: " +
|
||||
missingBlockchains.joinToString(),
|
||||
"Remote config does not contain some blockchains or providers information",
|
||||
)
|
||||
|
||||
Timber.e(exception)
|
||||
|
||||
firebaseCrashlytics.recordException(exception)
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = exception,
|
||||
params = mapOf("Missing blockchains" to missingBlockchains.joinToString()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import com.google.common.truth.Truth
|
|||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchainsdk.providers.BlockchainProvidersResponseMergerTest.Companion.localResponse
|
||||
import com.tangem.blockchainsdk.providers.BlockchainProvidersResponseMergerTest.Companion.remoteResponse
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.config.providers.BlockchainProvidersStorage
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
|
|
@ -20,10 +22,16 @@ internal class BlockchainProvidersResponseLoaderTest {
|
|||
|
||||
private val tangemTechApi = mockk<TangemTechApi>()
|
||||
private val blockchainProvidersStorage = mockk<BlockchainProvidersStorage>()
|
||||
private val analyticsExceptionHandler = object : AnalyticsExceptionHandler {
|
||||
override fun sendException(event: ExceptionAnalyticsEvent) {
|
||||
FirebaseCrashlytics.getInstance().recordException(event.exception)
|
||||
}
|
||||
}
|
||||
|
||||
private val loader = BlockchainProvidersResponseLoader(
|
||||
tangemTechApi = tangemTechApi,
|
||||
blockchainProvidersStorage = blockchainProvidersStorage,
|
||||
blockchainProvidersResponseMerger = BlockchainProvidersResponseMerger(analyticsExceptionHandler),
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package com.tangem.blockchainsdk.providers
|
|||
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.firebase.crashlytics.recordException
|
||||
import com.tangem.blockchainsdk.BlockchainProvidersResponse
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import io.mockk.*
|
||||
import org.junit.Before
|
||||
|
|
@ -13,6 +16,14 @@ import org.junit.Test
|
|||
*/
|
||||
internal class BlockchainProvidersResponseMergerTest {
|
||||
|
||||
private val blockchainProvidersResponseMerger = BlockchainProvidersResponseMerger(
|
||||
object : AnalyticsExceptionHandler {
|
||||
override fun sendException(event: ExceptionAnalyticsEvent) {
|
||||
FirebaseCrashlytics.getInstance().recordException(event.exception)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic(FirebaseCrashlytics::class)
|
||||
|
|
@ -25,7 +36,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
fun test_if_both_configs_are_empty() {
|
||||
val expected = emptyMap<String, List<ProviderModel>>()
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = emptyMap(),
|
||||
remote = emptyMap(),
|
||||
)
|
||||
|
|
@ -37,7 +48,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
fun test_if_local_config_is_empty() {
|
||||
val expected = remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = emptyMap(),
|
||||
remote = remoteResponse,
|
||||
)
|
||||
|
|
@ -49,7 +60,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
fun test_if_remote_config_is_empty() {
|
||||
val expected = localResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = emptyMap(),
|
||||
)
|
||||
|
|
@ -61,7 +72,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
fun test_if_both_configs_are_not_empty() {
|
||||
val expected = remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteResponse,
|
||||
)
|
||||
|
|
@ -73,7 +84,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
fun test_if_configs_are_equal() {
|
||||
val expected = remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = remoteResponse,
|
||||
remote = remoteResponse,
|
||||
)
|
||||
|
|
@ -88,7 +99,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
|
||||
val expected = localResponseWithEth + remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponseWithEth,
|
||||
remote = remoteResponse,
|
||||
)
|
||||
|
|
@ -103,7 +114,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
val eth = "ethereum" to emptyList<ProviderModel>()
|
||||
val remoteWithEth = remoteResponse + eth
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteWithEth,
|
||||
)
|
||||
|
|
@ -121,7 +132,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
*/
|
||||
val expected = remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteWithoutLocal,
|
||||
)
|
||||
|
|
@ -137,7 +148,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
|
||||
val expected = remoteResponse + ("ethereum" to listOf(nowNodesProvider))
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteWithEth,
|
||||
)
|
||||
|
|
@ -152,7 +163,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
|
||||
val expected = remoteResponse
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localResponse,
|
||||
remote = remoteWithEth,
|
||||
)
|
||||
|
|
@ -170,7 +181,7 @@ internal class BlockchainProvidersResponseMergerTest {
|
|||
|
||||
val expected = remoteResponse + eth.addSlash() + kaspa.addSlash()
|
||||
|
||||
val actual = BlockchainProvidersResponseMerger.merge(
|
||||
val actual = blockchainProvidersResponseMerger.merge(
|
||||
local = localWithKaspa,
|
||||
remote = remoteWithEth,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue