Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.simprints.infra.eventsync.sync.common.didFailBecauseReloginRequired
import com.simprints.infra.eventsync.sync.common.didFailBecauseTooManyRequests
import com.simprints.infra.eventsync.sync.common.filterByTags
import com.simprints.infra.eventsync.sync.common.getEstimatedOutageTime
import com.simprints.infra.eventsync.sync.common.hasAnyFailureReason
import com.simprints.infra.eventsync.sync.common.sortByScheduledTime
import com.simprints.infra.eventsync.sync.down.workers.extractDownSyncMaxCount
import com.simprints.infra.eventsync.sync.down.workers.extractDownSyncProgress
Expand Down Expand Up @@ -134,7 +135,7 @@ class EventSyncStateProcessor @Inject constructor(
}

private fun WorkInfo.toEventSyncWorkerState(): EventSyncWorkerState = fromWorkInfo(
state = state,
state = if (hasAnyFailureReason()) WorkInfo.State.FAILED else state,
failedBecauseReloginRequired = didFailBecauseReloginRequired(),
failedBecauseCloudIntegration = didFailBecauseCloudIntegration(),
failedBecauseBackendMaintenance = didFailBecauseBackendMaintenance(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ internal fun WorkInfo.didFailBecauseCommCarePermissionMissing(): Boolean =
this.outputData.getBoolean(OUTPUT_FAILED_BECAUSE_COMMCARE_PERMISSION_MISSING, false)

internal fun WorkInfo.getEstimatedOutageTime(): Long = this.outputData.getLong(OUTPUT_ESTIMATED_MAINTENANCE_TIME, 0L)

internal fun WorkInfo.hasAnyFailureReason(): Boolean =
didFailBecauseReloginRequired() ||
didFailBecauseCloudIntegration() ||
didFailBecauseBackendMaintenance() ||
didFailBecauseTooManyRequests() ||
didFailBecauseCommCarePermissionMissing()
Comment thread
luhmirin-s marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ internal abstract class BaseEventDownSyncTask(
val requestId = UUID.randomUUID().toString()
var result: EventFetchResult? = null
var errorType: String? = null
var errorToRethrow: Throwable? = null

try {
result = fetchEvents(operation, scope, requestId)
Expand Down Expand Up @@ -98,18 +99,19 @@ internal abstract class BaseEventDownSyncTask(
lastOperation = lastOperation.copy(state = COMPLETE, lastSyncTime = timeHelper.now().ms)
emitProgress(lastOperation, count, result.totalCount)
} catch (t: Throwable) {
if (shouldRethrowError(t)) {
throw t
}

Simber.i("Down sync error", t, tag = SYNC)
errorType = t.javaClass.simpleName

lastOperation = processBatchedEvents(operation, batchOfEventsToProcess, lastOperation, project)
emitProgress(lastOperation, count, count)

lastOperation = lastOperation.copy(state = FAILED, lastSyncTime = timeHelper.now().ms)
emitProgress(lastOperation, count, count)

if (shouldRethrowError(t)) {
errorToRethrow = t
} else {
Simber.i("Down sync error", t, tag = SYNC)
}
}

if (count > 0 || errorType != null) {
Expand All @@ -136,6 +138,8 @@ internal abstract class BaseEventDownSyncTask(
),
)
}

errorToRethrow?.let { throw it }
}

private suspend fun FlowCollector<EventDownSyncProgress>.emitProgress(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository
import com.simprints.infra.events.EventRepository
import com.simprints.infra.eventsync.event.remote.EventRemoteDataSource
import com.simprints.infra.eventsync.event.remote.exceptions.TooManyRequestsException
import com.simprints.infra.eventsync.status.down.EventDownSyncScopeRepository
import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation
import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory
import com.simprints.infra.network.exceptions.BackendMaintenanceException
import com.simprints.infra.network.exceptions.SyncCloudIntegrationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.consumeAsFlow
Expand Down Expand Up @@ -59,6 +62,9 @@ internal class SimprintsEventDownSyncTask @Inject constructor(

override fun shouldRethrowError(throwable: Throwable): Boolean {
// Return true to re-throw specific exceptions that should not be handled by the base class
return throwable is RemoteDbNotSignedInException
return throwable is RemoteDbNotSignedInException ||
throwable is BackendMaintenanceException ||
throwable is SyncCloudIntegrationException ||
throwable is TooManyRequestsException
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.work.WorkInfo
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.simprints.core.workers.SimCoroutineWorker
import com.simprints.infra.authstore.exceptions.RemoteDbNotSignedInException
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.enrolment.records.repository.local.migration.RealmToRoomMigrationFlagsStore
import com.simprints.infra.events.EventRepository
Expand Down Expand Up @@ -46,7 +47,11 @@ internal abstract class BaseEventDownSyncDownloaderWorker(

abstract fun createDownSyncTask(): BaseEventDownSyncTask

abstract fun handleSyncException(t: Throwable): Result
abstract fun handleSyncException(
t: Throwable,
count: Int,
max: Int?,
): Result

override suspend fun doWork(): Result {
// Check if the migration is in progress before starting the sync
Expand All @@ -66,14 +71,14 @@ internal abstract class BaseEventDownSyncDownloaderWorker(
protected open suspend fun performDownSync(): Result = withContext(dispatcher) {
showProgressNotification()
crashlyticsLog("Started")
val workerId = id.toString()
var count = syncCache.readProgress(workerId)
var max: Int? = syncCache.readMax(workerId)
try {
val workerId = id.toString()
var count = syncCache.readProgress(workerId)
var max: Int? = syncCache.readMax(workerId)
val project = configRepository.getProject()

if (project == null) {
fail(IllegalStateException("User is not signed in"))
throw RemoteDbNotSignedInException()
} else {
createDownSyncTask().downSync(this, getDownSyncOperation(), getEventScope(), project).collect {
count = it.progress
Expand All @@ -93,7 +98,7 @@ internal abstract class BaseEventDownSyncDownloaderWorker(
)
}
} catch (t: Throwable) {
handleSyncException(t)
handleSyncException(t, count, max)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.simprints.core.DispatcherBG
import com.simprints.infra.authstore.exceptions.RemoteDbNotSignedInException
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.enrolment.records.repository.local.migration.RealmToRoomMigrationFlagsStore
import com.simprints.infra.events.EventRepository
import com.simprints.infra.eventsync.status.down.EventDownSyncScopeRepository
import com.simprints.infra.eventsync.sync.common.EventSyncCache
import com.simprints.infra.eventsync.sync.common.OUTPUT_FAILED_BECAUSE_RELOGIN_REQUIRED
import com.simprints.infra.eventsync.sync.common.OUTPUT_FAILED_BECAUSE_COMMCARE_PERMISSION_MISSING
import com.simprints.infra.eventsync.sync.down.tasks.BaseEventDownSyncTask
import com.simprints.infra.eventsync.sync.down.tasks.CommCareEventSyncTask
import com.simprints.infra.logging.Simber
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -40,9 +43,35 @@
) {
override fun createDownSyncTask(): BaseEventDownSyncTask = commCareSyncTask

override fun handleSyncException(t: Throwable) = when (t) {
is IllegalArgumentException -> fail(t, t.message)
is SecurityException -> fail(t, t.message, workDataOf(OUTPUT_FAILED_BECAUSE_COMMCARE_PERMISSION_MISSING to true))
else -> retry(t)
override fun handleSyncException(
t: Throwable,
count: Int,
max: Int?,
): Result {
val outputData = Data.Builder()
.putInt(OUTPUT_DOWN_SYNC, count)
.putInt(OUTPUT_DOWN_MAX_SYNC, max ?: 0)
Comment thread
luhmirin-s marked this conversation as resolved.

when (t) {
is SecurityException -> {
outputData.putBoolean(OUTPUT_FAILED_BECAUSE_COMMCARE_PERMISSION_MISSING, true)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)

Check failure on line 58 in infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/workers/CommCareEventSyncDownloaderWorker.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Down-sync completed with recoverable issue" 3 times.

See more on https://sonarcloud.io/project/issues?id=Simprints_Android-Simprints-ID&issues=AZ_17L49j43-sBOrDljC&open=AZ_17L49j43-sBOrDljC&pullRequest=1783
}

is RemoteDbNotSignedInException -> {
outputData.putBoolean(OUTPUT_FAILED_BECAUSE_RELOGIN_REQUIRED, true)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

is IllegalArgumentException -> {
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

else -> {
Simber.e("Down-sync completed with unexpected issue", t, tag = tag)
}
}

return success(outputData.build(), "Completed with down-sync error: ${t.message}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.simprints.core.DispatcherBG
import com.simprints.infra.authstore.exceptions.RemoteDbNotSignedInException
import com.simprints.infra.config.store.ConfigRepository
Expand All @@ -19,6 +19,7 @@
import com.simprints.infra.eventsync.sync.common.OUTPUT_FAILED_BECAUSE_TOO_MANY_REQUESTS
import com.simprints.infra.eventsync.sync.down.tasks.BaseEventDownSyncTask
import com.simprints.infra.eventsync.sync.down.tasks.SimprintsEventDownSyncTask
import com.simprints.infra.logging.Simber
import com.simprints.infra.network.exceptions.BackendMaintenanceException
import com.simprints.infra.network.exceptions.SyncCloudIntegrationException
import dagger.assisted.Assisted
Expand Down Expand Up @@ -48,21 +49,47 @@
) {
override fun createDownSyncTask(): BaseEventDownSyncTask = downSyncTask

override fun handleSyncException(t: Throwable) = when (t) {
is IllegalArgumentException -> fail(t, t.message)
override fun handleSyncException(
t: Throwable,
count: Int,
max: Int?,
): Result {
val outputData = Data.Builder()
.putInt(OUTPUT_DOWN_SYNC, count)
.putInt(OUTPUT_DOWN_MAX_SYNC, max ?: 0)
Comment thread
luhmirin-s marked this conversation as resolved.

is BackendMaintenanceException -> fail(
t,
t.message,
workDataOf(
OUTPUT_FAILED_BECAUSE_BACKEND_MAINTENANCE to true,
OUTPUT_ESTIMATED_MAINTENANCE_TIME to t.estimatedOutage,
),
)
when (t) {
is BackendMaintenanceException -> {
outputData
.putBoolean(OUTPUT_FAILED_BECAUSE_BACKEND_MAINTENANCE, true)
.putLong(OUTPUT_ESTIMATED_MAINTENANCE_TIME, t.estimatedOutage ?: 0L)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)

Check failure on line 66 in infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/workers/SimprintsEventDownSyncDownloaderWorker.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Down-sync completed with recoverable issue" 5 times.

See more on https://sonarcloud.io/project/issues?id=Simprints_Android-Simprints-ID&issues=AZ_17L9qj43-sBOrDljD&open=AZ_17L9qj43-sBOrDljD&pullRequest=1783
}

is SyncCloudIntegrationException -> fail(t, t.message, workDataOf(OUTPUT_FAILED_BECAUSE_CLOUD_INTEGRATION to true))
is TooManyRequestsException -> fail(t, t.message, workDataOf(OUTPUT_FAILED_BECAUSE_TOO_MANY_REQUESTS to true))
is RemoteDbNotSignedInException -> fail(t, t.message, workDataOf(OUTPUT_FAILED_BECAUSE_RELOGIN_REQUIRED to true))
else -> retry(t)
is SyncCloudIntegrationException -> {
outputData.putBoolean(OUTPUT_FAILED_BECAUSE_CLOUD_INTEGRATION, true)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

is TooManyRequestsException -> {
outputData.putBoolean(OUTPUT_FAILED_BECAUSE_TOO_MANY_REQUESTS, true)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

is RemoteDbNotSignedInException -> {
outputData.putBoolean(OUTPUT_FAILED_BECAUSE_RELOGIN_REQUIRED, true)
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

is IllegalArgumentException -> {
Simber.i("Down-sync completed with recoverable issue", t, tag = tag)
}

else -> {
Simber.e("Down-sync completed with unexpected issue", t, tag = tag)
}
}

return success(outputData.build(), "Completed with down-sync error: ${t.message}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import com.simprints.core.workers.SimCoroutineWorker
import com.simprints.infra.events.EventRepository
import com.simprints.infra.events.event.domain.models.scope.EventScopeEndCause
import com.simprints.infra.eventsync.sync.common.EventSyncCache
import com.simprints.infra.eventsync.sync.common.SyncWorkersInfoProvider
import com.simprints.infra.eventsync.sync.common.hasAnyFailureReason
import com.simprints.infra.logging.Simber
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.withContext

/**
Expand All @@ -25,6 +28,7 @@ internal class EventEndSyncReporterWorker @AssistedInject constructor(
@Assisted params: WorkerParameters,
private val syncCache: EventSyncCache,
private val eventRepository: EventRepository,
private val syncWorkersInfoProvider: SyncWorkersInfoProvider,
private val timeHelper: TimeHelper,
@param:DispatcherBG private val dispatcher: CoroutineDispatcher,
) : SimCoroutineWorker(appContext, params) {
Expand All @@ -46,7 +50,15 @@ internal class EventEndSyncReporterWorker @AssistedInject constructor(
}

if (!syncId.isNullOrEmpty()) {
syncCache.storeLastSuccessfulSyncTime(timeHelper.now())
val hasWorkerFailures = syncWorkersInfoProvider
.getSyncWorkerInfos(syncId)
.firstOrNull()
.orEmpty()
.any { it.hasAnyFailureReason() }

if (!hasWorkerFailures) {
syncCache.storeLastSuccessfulSyncTime(timeHelper.now())
}
success()
} else {
throw IllegalArgumentException("SyncId missed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.simprints.infra.events.event.domain.models.scope.EventScopeType
import com.simprints.infra.eventsync.event.remote.ApiUploadEventsBody
import com.simprints.infra.eventsync.event.remote.EventRemoteDataSource
import com.simprints.infra.eventsync.event.remote.exceptions.TooManyRequestsException
import com.simprints.infra.eventsync.event.remote.models.session.ApiEventScope
import com.simprints.infra.eventsync.event.usecases.MapDomainEventScopeToApiUseCase
import com.simprints.infra.eventsync.exceptions.TryToUploadEventsForNotSignedProject
Expand All @@ -37,7 +38,9 @@
import com.simprints.infra.eventsync.sync.up.EventUpSyncProgress
import com.simprints.infra.logging.LoggingConstants.CrashReportTag.SYNC
import com.simprints.infra.logging.Simber
import com.simprints.infra.network.exceptions.BackendMaintenanceException
import com.simprints.infra.network.exceptions.NetworkConnectionException
import com.simprints.infra.network.exceptions.SyncCloudIntegrationException
import com.simprints.infra.serialization.SimJson
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -173,17 +176,16 @@

emitProgress(lastOperation, 0)
} catch (t: Throwable) {
if (t is RemoteDbNotSignedInException) {
throw t
}

Simber.e("Failed to upload event scopes", t, tag = SYNC)
lastOperation = lastOperation.copy(
lastState = FAILED,
lastSyncTime = timeHelper.now().ms,
)

emitProgress(lastOperation, 0)
if (isTerminalException(t)) {
throw t
}
}
}

Expand All @@ -195,6 +197,11 @@
this.emit(EventUpSyncProgress(lastOperation, count))
}

private fun isTerminalException(t: Throwable): Boolean = t is RemoteDbNotSignedInException ||
t is BackendMaintenanceException ||
t is SyncCloudIntegrationException ||
t is TooManyRequestsException

private fun uploadEventScopeType(
eventScope: EventScope,
project: Project,
Expand Down Expand Up @@ -350,9 +357,9 @@
config.canSyncAllDataToSimprints() -> events

config.canSyncBiometricDataToSimprints() -> events.filter {
it is EnrolmentEventV2 ||

Check warning on line 360 in infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/up/tasks/EventUpSyncTask.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Deprecated code should not be used.

See more on https://sonarcloud.io/project/issues?id=Simprints_Android-Simprints-ID&issues=AZ_17L-Rj43-sBOrDljE&open=AZ_17L-Rj43-sBOrDljE&pullRequest=1783
it is EnrolmentEventV4 ||
it is PersonCreationEvent ||

Check warning on line 362 in infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/up/tasks/EventUpSyncTask.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Deprecated code should not be used.

See more on https://sonarcloud.io/project/issues?id=Simprints_Android-Simprints-ID&issues=AZ_17L-Rj43-sBOrDljF&open=AZ_17L-Rj43-sBOrDljF&pullRequest=1783
it is FingerprintCaptureBiometricsEvent ||
it is FaceCaptureBiometricsEvent ||
it is BiometricReferenceCreationEvent ||
Expand Down
Loading