diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/main/sync/SyncViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/main/sync/SyncViewModel.kt index a2e49ab11e..93e207d2ff 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/main/sync/SyncViewModel.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/main/sync/SyncViewModel.kt @@ -187,7 +187,7 @@ internal class SyncViewModel @Inject constructor( _syncToBFSIDAllowed.postValue(configuration.canSyncDataToSimprints() || configuration.isEventDownSyncAllowed()) } eventSyncManager - .countEventsToUpload(EventType.ENROLMENT_V2) + .countEventsToUpload(listOf(EventType.ENROLMENT_V2, EventType.ENROLMENT_V4)) .collect { upSyncCountLiveData.postValue(it) } } diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/about/AboutViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/about/AboutViewModel.kt index 5d2b227c82..63d43cb55c 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/about/AboutViewModel.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/about/AboutViewModel.kt @@ -78,7 +78,7 @@ internal class AboutViewModel @Inject constructor( } } - private suspend fun hasEventsToUpload(): Boolean = eventSyncManager.countEventsToUpload(type = null).first() > 0 + private suspend fun hasEventsToUpload(): Boolean = eventSyncManager.countEventsToUpload().first() > 0 private suspend fun canSyncDataToSimprints(): Boolean = configManager.getProjectConfiguration().canSyncDataToSimprints() diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModel.kt index 59d238cea5..1b2325ea5d 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModel.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModel.kt @@ -35,7 +35,6 @@ import com.simprints.infra.sync.SyncOrchestrator import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import javax.inject.Inject @@ -123,11 +122,11 @@ internal class SyncInfoViewModel @Inject constructor( _isReloginRequired.addSource(lastSyncState) { lastSyncStateValue -> _isReloginRequired.postValue(lastSyncStateValue.isSyncFailedBecauseReloginRequired()) } + viewModelScope.launch { getRecordsToUpSync() } } fun refreshInformation() { _recordsInLocal.postValue(null) - _recordsToUpSync.postValue(null) _recordsToDownSync.postValue(null) _imagesToUpload.postValue(null) _moduleCounts.postValue(listOf()) @@ -179,7 +178,6 @@ internal class SyncInfoViewModel @Inject constructor( awaitAll( async { _configuration.postValue(configManager.getProjectConfiguration()) }, async { _recordsInLocal.postValue(getRecordsInLocal(projectId)) }, - async { _recordsToUpSync.postValue(getRecordsToUpSync()) }, async { _recordsToDownSync.postValue(fetchRecordsToCreateAndDeleteCount()) }, async { _imagesToUpload.postValue(imageRepository.getNumberOfImagesToUpload(projectId)) }, async { _moduleCounts.postValue(getModuleCounts(projectId)) }, @@ -207,10 +205,9 @@ internal class SyncInfoViewModel @Inject constructor( private suspend fun getRecordsInLocal(projectId: String): Int = enrolmentRecordRepository.count(SubjectQuery(projectId = projectId)) - private suspend fun getRecordsToUpSync(): Int = eventSyncManager - .countEventsToUpload(EventType.ENROLMENT_V2) - .firstOrNull() - ?: 0 + private suspend fun getRecordsToUpSync() = eventSyncManager + .countEventsToUpload(listOf(EventType.ENROLMENT_V2, EventType.ENROLMENT_V4)) + .collect { _recordsToUpSync.postValue(it) } private suspend fun fetchRecordsToCreateAndDeleteCount(): DownSyncCounts = if (configManager.getProjectConfiguration().isEventDownSyncAllowed()) { diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/main/sync/SyncViewModelTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/main/sync/SyncViewModelTest.kt index b5610d217c..e143e70416 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/main/sync/SyncViewModelTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/main/sync/SyncViewModelTest.kt @@ -28,6 +28,7 @@ import com.simprints.infra.config.store.models.UpSynchronizationConfiguration import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.SimprintsUpSynchronizationConfiguration import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.UpSynchronizationKind.ALL import com.simprints.infra.config.sync.ConfigManager +import com.simprints.infra.events.event.domain.models.EventType import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.eventsync.status.models.EventSyncState import com.simprints.infra.eventsync.status.models.EventSyncWorkerState @@ -234,7 +235,7 @@ internal class SyncViewModelTest { @Test fun `should post a SyncPendingUpload card state if there are records to upload`() { coEvery { configManager.getDeviceConfiguration() } returns deviceConfiguration - coEvery { eventSyncManager.countEventsToUpload(any()) }.returns(flowOf(2)) + coEvery { eventSyncManager.countEventsToUpload(any>()) }.returns(flowOf(2)) isConnected.value = true syncState.value = EventSyncState( diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/about/AboutViewModelTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/about/AboutViewModelTest.kt index 6b26a357ae..8fd01ffb9b 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/about/AboutViewModelTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/about/AboutViewModelTest.kt @@ -203,9 +203,7 @@ class AboutViewModelTest { true -> 1 false -> 0 } - coEvery { eventSyncManager.countEventsToUpload(any()) } returns flowOf( - countEventsToUpload, - ) + coEvery { eventSyncManager.countEventsToUpload() } returns flowOf(countEventsToUpload) coEvery { configManager.getProjectConfiguration() } returns buildProjectConfigurationMock( upSyncKind, ) diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModelTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModelTest.kt index c4c55f0229..a49f77c8a2 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModelTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoViewModelTest.kt @@ -101,19 +101,21 @@ class SyncInfoViewModelTest { stateLiveData = MutableLiveData() every { eventSyncManager.getLastSyncState() } returns stateLiveData coEvery { configManager.getProject(PROJECT_ID) } returns project - viewModel = SyncInfoViewModel( - configManager = configManager, - connectivityTracker = connectivityTracker, - enrolmentRecordRepository = enrolmentRecordRepository, - authStore = authStore, - imageRepository = imageRepository, - eventSyncManager = eventSyncManager, - syncOrchestrator = syncOrchestrator, - tokenizationProcessor = tokenizationProcessor, - recentUserActivityManager = recentUserActivityManager, - ) + viewModel = createViewModel() } + private fun createViewModel() = SyncInfoViewModel( + configManager = configManager, + connectivityTracker = connectivityTracker, + enrolmentRecordRepository = enrolmentRecordRepository, + authStore = authStore, + imageRepository = imageRepository, + eventSyncManager = eventSyncManager, + syncOrchestrator = syncOrchestrator, + tokenizationProcessor = tokenizationProcessor, + recentUserActivityManager = recentUserActivityManager, + ) + @Test fun `should initialize the configuration live data correctly`() = runTest { val configuration = mockk(relaxed = true) @@ -138,10 +140,11 @@ class SyncInfoViewModelTest { fun `should initialize the recordsToUpSync live data correctly`() = runTest { val number = 10 coEvery { - eventSyncManager.countEventsToUpload(EventType.ENROLMENT_V2) + eventSyncManager.countEventsToUpload(listOf(EventType.ENROLMENT_V2, EventType.ENROLMENT_V4)) } returns flowOf(number) - viewModel.refreshInformation() + // upSync count collected on init, so need to rebuild for mocking to take effect + viewModel = createViewModel() assertThat(viewModel.recordsToUpSync.getOrAwaitValue()).isEqualTo(number) } diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManager.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManager.kt index a516c9a323..9288ccb834 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManager.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManager.kt @@ -18,7 +18,9 @@ interface EventSyncManager { fun getLastSyncState(): LiveData - suspend fun countEventsToUpload(type: EventType?): Flow + suspend fun countEventsToUpload(): Flow + + suspend fun countEventsToUpload(types: List): Flow suspend fun countEventsToDownload(): DownSyncCounts diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManagerImpl.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManagerImpl.kt index 4389d8e86b..ffbcaffcf1 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManagerImpl.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/EventSyncManagerImpl.kt @@ -27,6 +27,7 @@ import com.simprints.infra.eventsync.sync.common.TAG_SUBJECTS_SYNC_ALL_WORKERS import com.simprints.infra.eventsync.sync.down.tasks.EventDownSyncTask import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withContext import javax.inject.Inject @@ -61,7 +62,11 @@ internal class EventSyncManagerImpl @Inject constructor( override fun getAllWorkerTag(): String = TAG_SUBJECTS_SYNC_ALL_WORKERS - override suspend fun countEventsToUpload(type: EventType?): Flow = eventRepository.observeEventCount(type) + override suspend fun countEventsToUpload(): Flow = eventRepository.observeEventCount(null) + + override suspend fun countEventsToUpload(types: List): Flow = combine( + types.map { eventRepository.observeEventCount(it) }, + ) { it.sum() } override suspend fun countEventsToDownload(): DownSyncCounts { val projectConfig = configRepository.getProjectConfiguration() diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/EventSyncManagerTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/EventSyncManagerTest.kt index bb19c40035..776c8c9105 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/EventSyncManagerTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/EventSyncManagerTest.kt @@ -9,6 +9,7 @@ import com.simprints.infra.config.store.ConfigRepository import com.simprints.infra.config.store.models.Project import com.simprints.infra.events.EventRepository import com.simprints.infra.events.event.domain.EventCount +import com.simprints.infra.events.event.domain.models.EventType import com.simprints.infra.events.event.domain.models.scope.EventScope import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_MODULE_ID import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_MODULE_ID_2 @@ -113,10 +114,17 @@ internal class EventSyncManagerTest { } @Test - fun `countEventsToUpload should call event repo`() = runTest { - eventSyncManagerImpl.countEventsToUpload(null).toList() + fun `countEventsToUpload without types should call event repo`() = runTest { + eventSyncManagerImpl.countEventsToUpload().toList() - coVerify { eventRepository.observeEventCount(any()) } + coVerify { eventRepository.observeEventCount(null) } + } + + @Test + fun `countEventsToUpload with types should call event repo per type`() = runTest { + eventSyncManagerImpl.countEventsToUpload(listOf(EventType.ENROLMENT_V2, EventType.EVENT_UP_SYNC_REQUEST)).toList() + + coVerify(exactly = 2) { eventRepository.observeEventCount(any()) } } @Test diff --git a/infra/sync/src/main/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCase.kt b/infra/sync/src/main/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCase.kt index 01b1065500..cead58d503 100644 --- a/infra/sync/src/main/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCase.kt +++ b/infra/sync/src/main/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCase.kt @@ -18,7 +18,7 @@ internal class HandleProjectStateUseCase @Inject constructor( private suspend fun shouldSignOut(projectState: ProjectState): Boolean { val isProjectEnded = projectState == ProjectState.PROJECT_ENDED val isProjectEnding = projectState == ProjectState.PROJECT_ENDING - val hasNoEventsToUpload = eventSyncManager.countEventsToUpload(null).first() == 0 + val hasNoEventsToUpload = eventSyncManager.countEventsToUpload().first() == 0 return isProjectEnded || (isProjectEnding && hasNoEventsToUpload) } diff --git a/infra/sync/src/test/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCaseTest.kt b/infra/sync/src/test/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCaseTest.kt index 31537699dc..93a006fefd 100644 --- a/infra/sync/src/test/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCaseTest.kt +++ b/infra/sync/src/test/java/com/simprints/infra/sync/config/usecase/HandleProjectStateUseCaseTest.kt @@ -32,7 +32,7 @@ internal class HandleProjectStateUseCaseTest { @Test fun `Fully logs out when project has ended`() = runTest { - coEvery { eventSyncManager.countEventsToUpload(null) } returns flowOf(0) + coEvery { eventSyncManager.countEventsToUpload() } returns flowOf(0) useCase(ProjectState.PROJECT_ENDED) @@ -41,7 +41,7 @@ internal class HandleProjectStateUseCaseTest { @Test fun `Logs out when project has ending and no items to upload`() = runTest { - coEvery { eventSyncManager.countEventsToUpload(null) } returns flowOf(0) + coEvery { eventSyncManager.countEventsToUpload() } returns flowOf(0) useCase(ProjectState.PROJECT_ENDING) @@ -50,7 +50,7 @@ internal class HandleProjectStateUseCaseTest { @Test fun `Does not logs out when project has ending and has items to upload`() = runTest { - coEvery { eventSyncManager.countEventsToUpload(null) } returns flowOf(5) + coEvery { eventSyncManager.countEventsToUpload() } returns flowOf(5) useCase(ProjectState.PROJECT_ENDING) @@ -59,7 +59,7 @@ internal class HandleProjectStateUseCaseTest { @Test fun `Does not logs out when project is running`() = runTest { - coEvery { eventSyncManager.countEventsToUpload(null) } returns flowOf(0) + coEvery { eventSyncManager.countEventsToUpload() } returns flowOf(0) useCase(ProjectState.RUNNING)