Coverage Summary for Class: InAppInteractorImpl (cloud.mindbox.mobile_sdk.inapp.domain)

Class Method, % Branch, % Line, % Instruction, %
InAppInteractorImpl 33.3% (4/12) 72.2% (13/18) 74.4% (58/78) 67.3% (290/431)
InAppInteractorImpl$listenToTargetingEvents$1
InAppInteractorImpl$listenToTargetingEvents$2 0% (0/1) 0% (0/8) 0% (0/5) 0% (0/69)
InAppInteractorImpl$listenToTargetingEvents$2$emit$1
InAppInteractorImpl$processEventAndConfig$$inlined$filter$1 0% (0/2)
InAppInteractorImpl$processEventAndConfig$$inlined$filter$1$2 0% (0/1)
InAppInteractorImpl$processEventAndConfig$$inlined$filter$1$2$1
InAppInteractorImpl$processEventAndConfig$$inlined$map$1 0% (0/2)
InAppInteractorImpl$processEventAndConfig$$inlined$map$1$2 0% (0/1)
InAppInteractorImpl$processEventAndConfig$$inlined$map$1$2$1
InAppInteractorImpl$processEventAndConfig$1
InAppInteractorImpl$processEventAndConfig$3 100% (1/1) 100% (1/1) 100% (6/6)
InAppInteractorImpl$processEventAndConfig$5 100% (1/1) 50% (2/4) 50% (1/2) 82.9% (34/41)
Total 28.6% (6/21) 50% (15/30) 69.8% (60/86) 60.3% (330/547)


 package cloud.mindbox.mobile_sdk.inapp.domain
 
 import cloud.mindbox.mobile_sdk.InitializeLock
 import cloud.mindbox.mobile_sdk.abtests.InAppABTestLogic
 import cloud.mindbox.mobile_sdk.inapp.data.managers.SessionStorageManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.checkers.Checker
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.interactors.InAppInteractor
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppEventManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppFilteringManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppFrequencyManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppProcessingManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppRepository
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.MobileConfigRepository
 import cloud.mindbox.mobile_sdk.inapp.domain.models.InApp
 import cloud.mindbox.mobile_sdk.logger.MindboxLog
 import cloud.mindbox.mobile_sdk.models.Milliseconds
 import cloud.mindbox.mobile_sdk.logger.mindboxLogD
 import cloud.mindbox.mobile_sdk.logger.mindboxLogI
 import cloud.mindbox.mobile_sdk.models.InAppEventType
 import cloud.mindbox.mobile_sdk.models.toTimestamp
 import cloud.mindbox.mobile_sdk.sortByPriority
 import cloud.mindbox.mobile_sdk.utils.TimeProvider
 import cloud.mindbox.mobile_sdk.utils.allAllow
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.*
 
 internal class InAppInteractorImpl(
     private val mobileConfigRepository: MobileConfigRepository,
     private val inAppRepository: InAppRepository,
     private val inAppFilteringManager: InAppFilteringManager,
     private val inAppEventManager: InAppEventManager,
     private val inAppProcessingManager: InAppProcessingManager,
     private val inAppABTestLogic: InAppABTestLogic,
     private val inAppFrequencyManager: InAppFrequencyManager,
     private val maxInappsPerSessionLimitChecker: Checker,
     private val maxInappsPerDayLimitChecker: Checker,
     private val minIntervalBetweenShowsLimitChecker: Checker,
     private val timeProvider: TimeProvider,
     private val sessionStorageManager: SessionStorageManager
 ) : InAppInteractor, MindboxLog {
 
     private val inAppTargetingChannel = Channel<InAppEventType>(Channel.UNLIMITED)
 
     override suspend fun processEventAndConfig(): Flow<Pair<InApp, Milliseconds>> {
         val inApps: List<InApp> = mobileConfigRepository.getInAppsSection()
             .let { inApps ->
                 inAppRepository.saveCurrentSessionInApps(inApps)
                 for (inApp in inApps) {
                     for (operation in inApp.targeting.getOperationsSet()) {
                         inAppRepository.saveOperationalInApp(operation.lowercase(), inApp)
                     }
                 }
                 val inAppIds = inAppABTestLogic.getInAppsPool(inApps.map { it.id })
                 inAppFilteringManager.filterABTestsInApps(inApps, inAppIds).also { filteredInApps ->
                     logI("InApps after abtest logic ${filteredInApps.map { it.id }}")
                 }
             }.also { unShownInApps ->
                 logI("Filtered config has ${unShownInApps.size} inapps")
                 for (inApp in unShownInApps) {
                     for (operation in inApp.targeting.getOperationsSet()) {
                         inAppRepository.saveUnShownOperationalInApp(operation.lowercase(), inApp)
                     }
                 }
             }
         return inAppRepository.listenInAppEvents()
             .filter { event -> inAppEventManager.isValidInAppEvent(event) }
             .onEach { event ->
                 mindboxLogD("Event triggered: ${event.name}")
             }.map { event ->
                 val triggerTimeMillis = timeProvider.currentTimestamp()
                 val filteredInApps = inAppFilteringManager.filterUnShownInAppsByEvent(inApps, event).let {
                     inAppFrequencyManager.filterInAppsFrequency(it)
                 }
                 mindboxLogI("Event: ${event.name} combined with $filteredInApps")
                 val prioritySortedInApps = filteredInApps.sortByPriority()
                 val inApp: InApp? = inAppProcessingManager.chooseInAppToShow(
                     prioritySortedInApps,
                     event
                 ).also {
                     inAppTargetingChannel.send(event)
                     if (event == InAppEventType.AppStartup) {
                         InitializeLock.complete(InitializeLock.State.APP_STARTED)
                     }
                 }
                 inApp?.let {
                     sessionStorageManager.inAppTriggerEvent = event
                 }
                 inApp?.let { inapp -> inapp to timeProvider.elapsedSince(triggerTimeMillis) }
             }
             .onEach { pair ->
                 pair?.let { (inApp, preparedTime) -> mindboxLogI("InApp ${inApp.id} isPriority=${inApp.isPriority}, delayTime=${inApp.delayTime}, skipLimitChecks=${inApp.isPriority}, preparedTime = ${preparedTime.interval} ms") }
                     ?: mindboxLogI("No inapps to show found")
             }
             .filterNotNull()
     }
 
     override fun areShowAndFrequencyLimitsAllowed(inApp: InApp): Boolean {
         val isAllowedByFrequency = inAppFrequencyManager.filterInAppsFrequency(listOf(inApp)).isNotEmpty()
         if (!isAllowedByFrequency) {
             return false
         }
         return inApp.isPriority || allAllow(
             maxInappsPerSessionLimitChecker,
             maxInappsPerDayLimitChecker,
             minIntervalBetweenShowsLimitChecker
         )
     }
 
     override fun saveShownInApp(
         id: String,
         timeStamp: Long,
         timeToDisplay: String,
         tags: Map<String, String>?
     ) {
         inAppRepository.setInAppShown(id)
         inAppRepository.sendInAppShown(id, timeToDisplay, tags)
         inAppRepository.saveShownInApp(id, timeStamp)
         inAppRepository.saveInAppStateChangeTime(timeStamp.toTimestamp())
     }
 
     override fun sendInAppClicked(inAppId: String) {
         inAppRepository.sendInAppClicked(inAppId)
     }
 
     override suspend fun listenToTargetingEvents() {
         val inApps = mobileConfigRepository.getInAppsSection()
         val inAppsMap = inAppRepository.getTargetedInApps()
         logI("Whole InApp list = $inApps")
         logI("InApps that has already sent targeting ${inAppsMap.entries}")
         inAppTargetingChannel.receiveAsFlow().collect { event ->
             val filteredInApps = inAppFilteringManager.filterInAppsByEvent(inApps, event)
             logI("inapps for event $event are = $filteredInApps")
             for (inApp in filteredInApps) {
                 if (inAppsMap[inApp.id]?.contains(event.hashCode()) != true) {
                     inAppProcessingManager.sendTargetedInApp(inApp, event)
                 }
             }
         }
     }
 
     override fun setInAppShown(inAppId: String) {
         inAppRepository.setInAppShown(inAppId)
     }
 
     override suspend fun fetchMobileConfig() {
         mobileConfigRepository.fetchMobileConfig()
     }
 
     override fun resetInAppConfigAndEvents() {
         mobileConfigRepository.resetCurrentConfig()
         inAppRepository.clearInAppEvents()
     }
 
     override fun isTimeDelayInapp(inAppId: String): Boolean {
         return inAppRepository.isTimeDelayInapp(inAppId)
     }
 
     override fun saveInAppDismissTime() {
         val timeStamp = timeProvider.currentTimestamp()
         mindboxLogI("Last in-app display duration ${(timeStamp - inAppRepository.getLastInappDismissTime()).ms} ms")
         inAppRepository.saveInAppStateChangeTime(timeStamp = timeStamp)
     }
 }