NTP-47165 | Update code in FCM topic use case (#15438)

This commit is contained in:
Abhinav Gupta
2025-03-20 13:05:04 +05:30
committed by GitHub
parent 21d34e7e27
commit 4db136b5f6
3 changed files with 116 additions and 27 deletions

View File

@@ -9,6 +9,7 @@ package com.naviapp.common.di
import com.google.firebase.messaging.FirebaseMessaging
import com.navi.base.BuildConfig
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.network.BaseUrls
import com.naviapp.app.NaviApplication
import com.naviapp.app.initializers.AnrErrorHandlerInitializer
@@ -46,6 +47,9 @@ object AppModule {
@Singleton
fun provideCoroutineScopeIO(): CoroutineScope = CoroutineScope(Dispatchers.IO)
@Provides
fun provideFirebaseRemoteConfigHelper(): FirebaseRemoteConfigHelper = FirebaseRemoteConfigHelper
@Provides fun provideFirebaseMessaging(): FirebaseMessaging = FirebaseMessaging.getInstance()
@Provides

View File

@@ -10,6 +10,7 @@ package com.naviapp.registration.usecase
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.cache.di.NaviCommonGson
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.FCM_TOPIC_LIST
@@ -28,12 +29,17 @@ import javax.inject.Inject
*
* @property firebaseMessaging The FirebaseMessaging instance used for topic subscription.
*/
class FcmTopicUseCase @Inject constructor(private val firebaseMessaging: FirebaseMessaging) {
class FcmTopicUseCase
@Inject
constructor(
private val firebaseRemoteConfigHelper: FirebaseRemoteConfigHelper,
private val firebaseMessaging: FirebaseMessaging,
@NaviCommonGson private val gson: Gson,
) {
/** Attaches FCM topics by fetching the topic list and subscribing to the enabled topics. */
fun attachFcmTopics() {
val topicList = fetchFcmTopicList() ?: return
topicList.list
?.filter { it.topicName.isNotNullAndNotEmpty() }
?.partition { it.enable }
@@ -50,11 +56,10 @@ class FcmTopicUseCase @Inject constructor(private val firebaseMessaging: Firebas
*/
private fun fetchFcmTopicList(): FcmTopicList? {
return try {
Gson()
.fromJson(
FirebaseRemoteConfigHelper.getString(FCM_TOPIC_LIST),
FcmTopicList::class.java,
)
gson.fromJson(
firebaseRemoteConfigHelper.getString(FCM_TOPIC_LIST),
FcmTopicList::class.java,
)
} catch (e: Exception) {
e.log()
null
@@ -69,29 +74,20 @@ class FcmTopicUseCase @Inject constructor(private val firebaseMessaging: Firebas
private fun subscribeToTopic(topicName: String) {
firebaseMessaging
.subscribeToTopic(topicName)
.addOnSuccessListener {
NaviTrackEvent.trackEvent(FCM_SUBSCRIPTION_SUCCESS, mapOf(TOPIC to topicName))
}
.addOnSuccessListener { trackFcmEvent(FCM_SUBSCRIPTION_SUCCESS, topicName) }
.addOnCompleteListener { task ->
if (!task.isSuccessful) {
NaviTrackEvent.trackEvent(
trackFcmEvent(
FCM_SUBSCRIPTION_FAILED,
mapOf(
TOPIC to topicName,
ERROR to (task.exception?.message ?: UNKNOWN_ERROR),
),
topicName,
task.exception?.message ?: UNKNOWN_ERROR,
)
}
}
.addOnFailureListener { exception ->
NaviTrackEvent.trackEvent(
FCM_SUBSCRIPTION_FAILED,
mapOf(TOPIC to topicName, ERROR to exception.toString()),
)
}
.addOnCanceledListener {
NaviTrackEvent.trackEvent(FCM_SUBSCRIPTION_CANCELED, mapOf(TOPIC to topicName))
trackFcmEvent(FCM_SUBSCRIPTION_FAILED, topicName, exception.toString())
}
.addOnCanceledListener { trackFcmEvent(FCM_SUBSCRIPTION_CANCELED, topicName) }
}
/**
@@ -102,19 +98,24 @@ class FcmTopicUseCase @Inject constructor(private val firebaseMessaging: Firebas
private fun unsubscribeToTopic(topicName: String) {
firebaseMessaging
.unsubscribeFromTopic(topicName)
.addOnSuccessListener {
NaviTrackEvent.trackEvent(FCM_UNSUBSCRIBE_SUCCESS, mapOf(TOPIC to topicName))
}
.addOnSuccessListener { trackFcmEvent(FCM_UNSUBSCRIBE_SUCCESS, topicName) }
.addOnCompleteListener {
if (!it.isSuccessful) {
NaviTrackEvent.trackEvent(
trackFcmEvent(
FCM_UNSUBSCRIBE_FAILED,
mapOf(TOPIC to topicName, ERROR to (it.exception?.message ?: UNKNOWN_ERROR)),
topicName,
it.exception?.message ?: UNKNOWN_ERROR,
)
}
}
}
private fun trackFcmEvent(eventName: String, topicName: String, errorMessage: String? = null) {
val eventData = mutableMapOf(TOPIC to topicName)
errorMessage?.let { eventData[ERROR] = it }
NaviTrackEvent.trackEvent(eventName, eventData)
}
companion object {
private const val FCM_SUBSCRIPTION_SUCCESS = "dev_fcm_subscription_success"
private const val FCM_UNSUBSCRIBE_SUCCESS = "dev_fcm_unsubscribe_success"

View File

@@ -0,0 +1,84 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.utils.log
import com.naviapp.models.FcmTopicData
import com.naviapp.models.FcmTopicList
import com.naviapp.registration.usecase.FcmTopicUseCase
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.verify
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class FcmTopicUseCaseTest {
private lateinit var fcmTopicUseCase: FcmTopicUseCase
private val firebaseRemoteConfigHelper: FirebaseRemoteConfigHelper = mockk()
private val gson: Gson = mockk()
@Before
fun setUp() {
val mockFirebaseMessaging: FirebaseMessaging = mockk(relaxed = true)
fcmTopicUseCase = FcmTopicUseCase(firebaseRemoteConfigHelper, mockFirebaseMessaging, gson)
}
@Test
fun `attachFcmTopics calls subscribeToTopic when enabled list is not empty`() {
val topicName = "test_topic"
val fcmTopicList = FcmTopicList(listOf(FcmTopicData(topicName, enable = true)))
every { firebaseRemoteConfigHelper.getString(any()) } returns "{}"
every { gson.fromJson("{}", FcmTopicList::class.java) } returns fcmTopicList
val subscribeSpy = spyk(fcmTopicUseCase, recordPrivateCalls = true)
subscribeSpy.attachFcmTopics()
verify(exactly = 1) { subscribeSpy["subscribeToTopic"](topicName) }
}
@Test
fun `fetchFcmTopicList returns null for invalid JSON`() {
every { firebaseRemoteConfigHelper.getString(any()) } returns "{}}"
mockkStatic("com.navi.common.utils.LogKt")
every { any<Exception>().log() } just Runs
val result =
fcmTopicUseCase::class
.java
.getDeclaredMethod("fetchFcmTopicList")
.apply { isAccessible = true }
.invoke(fcmTopicUseCase) as FcmTopicList?
assert(result == null)
}
@Test
fun `fetchFcmTopicList returns null for empty string`() {
every { firebaseRemoteConfigHelper.getString(any()) } returns ""
mockkStatic("com.navi.common.utils.LogKt")
every { any<Exception>().log() } just Runs
val result =
fcmTopicUseCase::class
.java
.getDeclaredMethod("fetchFcmTopicList")
.apply { isAccessible = true }
.invoke(fcmTopicUseCase) as FcmTopicList?
assert(result == null)
}
}