From 8ceeb883874c9c255323b539f536880bba7cb3dc Mon Sep 17 00:00:00 2001 From: Shaurya Rehan Date: Wed, 8 May 2024 19:25:27 +0530 Subject: [PATCH] TP-66010 | Litmus Experimentation setup for upi (#10759) --- .../FirebaseRemoteConfigHelper.kt | 2 + .../main/res/xml/default_remote_config.xml | 4 + .../network/LitmusExperimentsResponse.kt | 20 +++ .../pay/common/repository/CommonRepository.kt | 3 + .../usecase/SyncLitmusExperimentsUseCase.kt | 114 ++++++++++++++++++ .../com/navi/pay/entry/NaviPayViewModel.kt | 6 + .../retrofit/NaviPayRetrofitService.kt | 4 + .../com/navi/pay/utils/NaviPayConstants.kt | 3 + 8 files changed, 156 insertions(+) create mode 100644 android/navi-pay/src/main/kotlin/com/navi/pay/common/model/network/LitmusExperimentsResponse.kt create mode 100644 android/navi-pay/src/main/kotlin/com/navi/pay/common/usecase/SyncLitmusExperimentsUseCase.kt diff --git a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt index d294fc46d9..15c422dc91 100644 --- a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt @@ -111,6 +111,8 @@ object FirebaseRemoteConfigHelper { const val NAVI_PAY_REWARDS_NUDGE_ENABLED = "NAVI_PAY_REWARDS_NUDGE_ENABLED" const val NAVI_PAY_REWARDS_GRATIFICATION_ENABLED = "NAVI_PAY_REWARDS_GRATIFICATION_ENABLED" const val NAVI_PAY_CL_INIT_RETRY_COUNT = "NAVI_PAY_CL_INIT_RETRY_COUNT" + const val NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_DURATION_IN_MILLIS = + "NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_DURATION_IN_MILLIS" // BBPS const val NAVI_BBPS_CATEGORIES_CACHE_MILLIS = "NAVI_BBPS_CATEGORIES_CACHE_MILLIS" diff --git a/android/navi-common/src/main/res/xml/default_remote_config.xml b/android/navi-common/src/main/res/xml/default_remote_config.xml index bb9aa01e7c..bc65fe7916 100644 --- a/android/navi-common/src/main/res/xml/default_remote_config.xml +++ b/android/navi-common/src/main/res/xml/default_remote_config.xml @@ -477,4 +477,8 @@ FRONT_LAYER_UPPER_CONTENT_LENGTH 7 + + NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_DURATION_IN_MILLIS + 86400000 + \ No newline at end of file diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/model/network/LitmusExperimentsResponse.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/model/network/LitmusExperimentsResponse.kt new file mode 100644 index 0000000000..b97d163aab --- /dev/null +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/model/network/LitmusExperimentsResponse.kt @@ -0,0 +1,20 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.pay.common.model.network + +import com.google.gson.annotations.SerializedName + +data class LitmusExperimentsResponse( + @SerializedName("experiments") val experiments: List? +) + +data class LitmusExperimentItemResponse( + @SerializedName("name") val name: String, + @SerializedName("isEnabled") val isEnabled: Boolean? = false, + @SerializedName("metaData") val metaData: Map? = emptyMap() +) diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/repository/CommonRepository.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/repository/CommonRepository.kt index 632cf75657..8dfaed3a9d 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/common/repository/CommonRepository.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/repository/CommonRepository.kt @@ -88,4 +88,7 @@ constructor(private val naviPayRetrofitService: NaviPayRetrofitService) : Respon suspend fun liteDeregistration(sendMoneyRequest: SendMoneyRequest) = apiResponseCallback(naviPayRetrofitService.sendMoney(sendMoneyRequest = sendMoneyRequest)) + + suspend fun fetchABTestingExperiments() = + apiResponseCallback(response = naviPayRetrofitService.fetchABTestingExperiments()) } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/usecase/SyncLitmusExperimentsUseCase.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/usecase/SyncLitmusExperimentsUseCase.kt new file mode 100644 index 0000000000..782fbd69fd --- /dev/null +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/usecase/SyncLitmusExperimentsUseCase.kt @@ -0,0 +1,114 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.pay.common.usecase + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.navi.base.cache.model.NaviCacheAltSourceEntity +import com.navi.base.cache.model.NaviCacheEntity +import com.navi.base.cache.repository.NaviCacheRepository +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper +import com.navi.common.network.models.isSuccessWithData +import com.navi.pay.common.model.network.LitmusExperimentItemResponse +import com.navi.pay.common.repository.CommonRepository +import com.navi.pay.common.sync.model.view.SyncEntity +import com.navi.pay.common.sync.repository.SyncRepository +import com.navi.pay.network.di.NaviPayGsonBuilder +import com.navi.pay.utils.NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_KEY +import com.navi.pay.utils.NAVI_PAY_LITMUS_EXPERIMENTS_LAST_REFRESHED_TIMESTAMP +import javax.inject.Inject + +class SyncLitmusExperimentsUseCase +@Inject +constructor( + private val commonRepository: CommonRepository, + private val naviCacheRepository: NaviCacheRepository, + private val syncRepository: SyncRepository, + @NaviPayGsonBuilder private val gson: Gson +) { + + suspend fun execute(skipLastSyncedTimestampCheck: Boolean = true) { + + val firebaseCacheDurationInMillis = + FirebaseRemoteConfigHelper.getLong( + FirebaseRemoteConfigHelper.NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_DURATION_IN_MILLIS + ) + + val lastRefreshTimeStampForABTesting = + syncRepository + .get(key = NAVI_PAY_LITMUS_EXPERIMENTS_LAST_REFRESHED_TIMESTAMP) + ?.lastSyncedTimestamp + ?.toLongOrNull() ?: 0L + + if (!skipLastSyncedTimestampCheck) { + // Refresh is not needed + if ( + System.currentTimeMillis() - lastRefreshTimeStampForABTesting < + firebaseCacheDurationInMillis + ) { + return + } + } + + val abTestingResponse = commonRepository.fetchABTestingExperiments() + if (!abTestingResponse.isSuccessWithData()) { + return + } + + naviCacheRepository.save( + NaviCacheEntity( + value = gson.toJson(abTestingResponse.data?.experiments), + ttl = firebaseCacheDurationInMillis, + version = 1, + key = NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_KEY + ) + ) + + syncRepository.insert( + syncEntity = + SyncEntity( + key = NAVI_PAY_LITMUS_EXPERIMENTS_LAST_REFRESHED_TIMESTAMP, + lastSyncedTimestamp = System.currentTimeMillis().toString() + ) + ) + } + + private suspend fun getABExperiments(): List { + val experimentsJson = + naviCacheRepository.getDataOrFetchFromAltSource( + key = NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_KEY, + version = 1, + getDataFromAltSource = { + NaviCacheAltSourceEntity( + value = gson.toJson(emptyList()), + version = 1, + isSuccess = false + ) + } + ) + + val type = object : TypeToken>() {}.type + val experiments = + try { + gson + .fromJson>( + experimentsJson?.value.orEmpty(), + type + ) + .let { it.ifEmpty { emptyList() } } + } catch (e: Exception) { + emptyList() + } + + return experiments + } + + private suspend fun getExperimentValues(experimentName: String): LitmusExperimentItemResponse? { + return getABExperiments().firstOrNull { it.name == experimentName } + } +} diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/entry/NaviPayViewModel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/entry/NaviPayViewModel.kt index 8bff167208..4478bde3c1 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/entry/NaviPayViewModel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/entry/NaviPayViewModel.kt @@ -33,6 +33,7 @@ import com.navi.pay.common.usecase.RefreshBankListUseCase import com.navi.pay.common.usecase.RefreshConfigUseCase import com.navi.pay.common.usecase.RefreshLinkedAccountsUseCase import com.navi.pay.common.usecase.RefreshUiTronScreenResponseUseCase +import com.navi.pay.common.usecase.SyncLitmusExperimentsUseCase import com.navi.pay.common.viewmodel.NaviPayBaseVM import com.navi.pay.management.common.model.view.NudgeDetailEntity import com.navi.pay.network.di.NaviPayGsonBuilder @@ -76,6 +77,7 @@ constructor( private val bankUptimeRepository: BankUptimeRepository, private val scratchCardNudgeHelper: ScratchCardNudgeHelper, private val naviCacheRepository: NaviCacheRepository, + private val syncLitmusExperimentsUseCase: SyncLitmusExperimentsUseCase, @NaviPayGsonBuilder private val gson: Gson ) : NaviPayBaseVM(naviPayVmData = NaviPayVmData(screenName = NaviPayAnalytics.NAVI_PAY_ACTIVITY)) { @@ -106,6 +108,10 @@ constructor( taskList.add(async { refreshConfigUseCase.execute() }) + taskList.add( + async { syncLitmusExperimentsUseCase.execute(skipLastSyncedTimestampCheck = false) } + ) + taskList.add( async { if (isUserOnboarded()) { diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/network/retrofit/NaviPayRetrofitService.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/network/retrofit/NaviPayRetrofitService.kt index 995a7d299b..957a55245e 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/network/retrofit/NaviPayRetrofitService.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/network/retrofit/NaviPayRetrofitService.kt @@ -8,6 +8,7 @@ package com.navi.pay.network.retrofit import com.navi.common.network.models.GenericResponse +import com.navi.pay.common.model.network.LitmusExperimentsResponse import com.navi.pay.common.model.network.RequestIdResponse import com.navi.pay.common.model.network.ValidateVpaRequest import com.navi.pay.common.model.network.ValidateVpaResponse @@ -396,4 +397,7 @@ interface NaviPayRetrofitService { suspend fun sendMoneyAdditionalInfo( @Body sendMoneyAdditionalInfoRequest: SendMoneyAdditionalInfoRequest ): Response> + + @GET("/gateway-service/$NAVI_PAY_API_VERSION/navipay/user-experiments") + suspend fun fetchABTestingExperiments(): Response> } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/utils/NaviPayConstants.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/utils/NaviPayConstants.kt index d913805570..3b2724f175 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/utils/NaviPayConstants.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/utils/NaviPayConstants.kt @@ -153,6 +153,7 @@ const val DEFAULT_INITIATION_MODE_DYNAMIC_ATM_QR = "18" // Navi Pay cache keys const val NPCI_KEY = "npciKey" +const val NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_KEY = "NAVI_PAY_LITMUS_EXPERIMENTS_CACHE_KEY" // Navi Common DB cache keys const val NAVI_PAY_HOME_PAGE_CACHE_KEY = "naviPayHomePage" @@ -163,6 +164,8 @@ const val NAVI_PAY_SYNC_TABLE_TRANSACTION_HISTORY_KEY = "transactionHistoryKey" const val NAVI_PAY_SYNC_TABLE_LINKED_ACCOUNTS_KEY = "linkedAccountsKey" const val NAVI_PAY_SYNC_TABLE_SAVED_BENEFICIARY_KEY = "savedBeneficiaryKey" const val NAVI_PAY_SYNC_TABLE_FREQUENT_TRANSACTIONS_KEY = "frequentTransactionsKey" +const val NAVI_PAY_LITMUS_EXPERIMENTS_LAST_REFRESHED_TIMESTAMP = + "litmusExperimentsLastRefreshedTimestamp" // Generic const val NAVIPAY_NETWORK_INFO_TIMEOUT = 65L