From fab3f77cf84eea0f6dfaef3fc45e188541f4eb13 Mon Sep 17 00:00:00 2001 From: Marinov Date: Mon, 22 Sep 2025 19:22:59 +0300 Subject: [PATCH] Don't require connection for event sync if CommCare downsync source is configured --- .../feature/dashboard/debug/DebugFragment.kt | 8 +++++-- .../RunBlockingEventSyncUseCaseTest.kt | 11 +++++----- .../simprints/infra/sync/SyncOrchestrator.kt | 4 ++-- .../infra/sync/SyncOrchestratorImpl.kt | 22 ++++++++++++++----- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/debug/DebugFragment.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/debug/DebugFragment.kt index 3358fe7f4a..48ff415af0 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/debug/DebugFragment.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/debug/DebugFragment.kt @@ -79,7 +79,9 @@ internal class DebugFragment : Fragment(R.layout.fragment_debug) { } binding.syncStart.setOnClickListener { - syncOrchestrator.startEventSync() + lifecycleScope.launch(dispatcher) { + syncOrchestrator.startEventSync() + } } binding.syncStop.setOnClickListener { @@ -87,7 +89,9 @@ internal class DebugFragment : Fragment(R.layout.fragment_debug) { } binding.syncSchedule.setOnClickListener { - syncOrchestrator.rescheduleEventSync() + lifecycleScope.launch(dispatcher) { + syncOrchestrator.rescheduleEventSync() + } } binding.clearFirebaseToken.setOnClickListener { diff --git a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/RunBlockingEventSyncUseCaseTest.kt b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/RunBlockingEventSyncUseCaseTest.kt index 69fb54869f..534f489536 100644 --- a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/RunBlockingEventSyncUseCaseTest.kt +++ b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/RunBlockingEventSyncUseCaseTest.kt @@ -9,9 +9,10 @@ import com.simprints.infra.eventsync.status.models.EventSyncWorkerType import com.simprints.infra.sync.SyncOrchestrator import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.MockKAnnotations +import io.mockk.coJustRun +import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.justRun import io.mockk.verify import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest @@ -38,7 +39,7 @@ class RunBlockingEventSyncUseCaseTest { fun setUp() { MockKAnnotations.init(this) - justRun { syncOrchestrator.startEventSync() } + coJustRun { syncOrchestrator.startEventSync() } usecase = RunBlockingEventSyncUseCase( syncManager, @@ -58,7 +59,7 @@ class RunBlockingEventSyncUseCaseTest { liveData.postValue(createSyncState("sync", EventSyncWorkerState.Succeeded)) testScheduler.advanceUntilIdle() - verify { syncOrchestrator.startEventSync() } + coVerify { syncOrchestrator.startEventSync() } verify { syncManager.getLastSyncState() } } @@ -74,7 +75,7 @@ class RunBlockingEventSyncUseCaseTest { liveData.postValue(createSyncState("sync", EventSyncWorkerState.Failed())) testScheduler.advanceUntilIdle() - verify { syncOrchestrator.startEventSync() } + coVerify { syncOrchestrator.startEventSync() } verify { syncManager.getLastSyncState() } } @@ -90,7 +91,7 @@ class RunBlockingEventSyncUseCaseTest { liveData.postValue(createSyncState("sync", EventSyncWorkerState.Cancelled)) testScheduler.advanceUntilIdle() - verify { syncOrchestrator.startEventSync() } + coVerify { syncOrchestrator.startEventSync() } verify { syncManager.getLastSyncState() } } diff --git a/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestrator.kt b/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestrator.kt index 0f171a4c0d..3f127229b5 100644 --- a/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestrator.kt +++ b/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestrator.kt @@ -13,11 +13,11 @@ interface SyncOrchestrator { */ fun refreshConfiguration(): Flow - fun rescheduleEventSync(withDelay: Boolean = false) + suspend fun rescheduleEventSync(withDelay: Boolean = false) fun cancelEventSync() - fun startEventSync(isDownSyncAllowed: Boolean = true) + suspend fun startEventSync(isDownSyncAllowed: Boolean = true) fun stopEventSync() diff --git a/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestratorImpl.kt b/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestratorImpl.kt index f6429d7d56..78557017f4 100644 --- a/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestratorImpl.kt +++ b/infra/sync/src/main/java/com/simprints/infra/sync/SyncOrchestratorImpl.kt @@ -10,6 +10,7 @@ import androidx.work.workDataOf import com.simprints.core.AppScope import com.simprints.infra.authstore.AuthStore import com.simprints.infra.config.store.models.imagesUploadRequiresUnmeteredConnection +import com.simprints.infra.config.store.models.isCommCareEventDownSyncAllowed import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.eventsync.sync.master.EventSyncMasterWorker @@ -112,11 +113,12 @@ internal class SyncOrchestratorImpl @Inject constructor( }.map { } // Converts flow emissions to Unit value as we only care about when it happens, not the value } - override fun rescheduleEventSync(withDelay: Boolean) { + override suspend fun rescheduleEventSync(withDelay: Boolean) { workManager.schedulePeriodicWorker( - SyncConstants.EVENT_SYNC_WORK_NAME, - SyncConstants.EVENT_SYNC_WORKER_INTERVAL, + workName = SyncConstants.EVENT_SYNC_WORK_NAME, + repeatInterval = SyncConstants.EVENT_SYNC_WORKER_INTERVAL, initialDelay = if (withDelay) SyncConstants.EVENT_SYNC_WORKER_INTERVAL else 0, + constraints = getEventSyncConstraints(), tags = eventSyncManager.getPeriodicWorkTags(), ) } @@ -126,9 +128,10 @@ internal class SyncOrchestratorImpl @Inject constructor( stopEventSync() } - override fun startEventSync(isDownSyncAllowed: Boolean) { + override suspend fun startEventSync(isDownSyncAllowed: Boolean) { workManager.startWorker( - SyncConstants.EVENT_SYNC_WORK_NAME_ONE_TIME, + workName = SyncConstants.EVENT_SYNC_WORK_NAME_ONE_TIME, + constraints = getEventSyncConstraints(), tags = eventSyncManager.getOneTimeWorkTags(), inputData = workDataOf(EventSyncMasterWorker.IS_DOWN_SYNC_ALLOWED to isDownSyncAllowed), ) @@ -238,4 +241,13 @@ internal class SyncOrchestratorImpl @Inject constructor( .let { if (it) NetworkType.UNMETERED else NetworkType.CONNECTED } return Constraints.Builder().setRequiredNetworkType(networkType).build() } + + private suspend fun getEventSyncConstraints(): Constraints { + // CommCare doesn't require network connection + val networkType = configManager + .getProjectConfiguration() + .isCommCareEventDownSyncAllowed() + .let { if (it) NetworkType.NOT_REQUIRED else NetworkType.CONNECTED } + return Constraints.Builder().setRequiredNetworkType(networkType).build() + } }