Retry route actions after photo upload

This commit is contained in:
admin
2026-07-08 02:01:37 +02:00
parent d6b36a1d3a
commit 4a06e9da8b
6 changed files with 66 additions and 3 deletions
@@ -14,7 +14,10 @@ class PhotoUploadWorker(
appContext: Context, appContext: Context,
params: WorkerParameters, params: WorkerParameters,
) : CoroutineWorker(appContext, params) { ) : CoroutineWorker(appContext, params) {
private val dao = DriverDatabase.get(appContext).photoUploadDao() private val database = DriverDatabase.get(appContext)
private val dao = database.photoUploadDao()
private val routeActionDao = database.routeActionDao()
private val routeActionOutbox = RouteActionOutbox(appContext)
private val repository = DriverRepository(appContext) private val repository = DriverRepository(appContext)
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
@@ -62,6 +65,7 @@ class PhotoUploadWorker(
} }
dao.markConfirmed(clientRequestId, receipt.serverPhotoId) dao.markConfirmed(clientRequestId, receipt.serverPhotoId)
enqueueRouteActionsWaitingForPhoto(clientRequestId)
Result.success() Result.success()
}.getOrElse { throwable -> }.getOrElse { throwable ->
val error = ApiErrorMapper.map(throwable) val error = ApiErrorMapper.map(throwable)
@@ -90,6 +94,12 @@ class PhotoUploadWorker(
} }
} }
private suspend fun enqueueRouteActionsWaitingForPhoto(clientRequestId: String) {
routeActionDao.waitingForPhotosActions()
.filter { routeActionReferencesPhotoClientRequest(it, clientRequestId) }
.forEach { routeActionOutbox.enqueueWorker(it.clientActionId) }
}
companion object { companion object {
const val KEY_CLIENT_REQUEST_ID = "clientRequestId" const val KEY_CLIENT_REQUEST_ID = "clientRequestId"
const val KEY_PROGRESS = "progress" const val KEY_PROGRESS = "progress"
@@ -14,6 +14,9 @@ interface RouteActionDao {
@Query("SELECT * FROM route_actions WHERE clientActionId = :clientActionId LIMIT 1") @Query("SELECT * FROM route_actions WHERE clientActionId = :clientActionId LIMIT 1")
suspend fun find(clientActionId: String): RouteActionEntity? suspend fun find(clientActionId: String): RouteActionEntity?
@Query("SELECT * FROM route_actions WHERE status = 'WAITING_FOR_PHOTOS'")
suspend fun waitingForPhotosActions(): List<RouteActionEntity>
@Query( @Query(
""" """
SELECT * FROM route_actions SELECT * FROM route_actions
@@ -53,7 +53,7 @@ class RouteActionOutbox(
return entity return entity
} }
private fun enqueueWorker(clientActionId: String) { fun enqueueWorker(clientActionId: String) {
val request = OneTimeWorkRequestBuilder<RouteActionWorker>() val request = OneTimeWorkRequestBuilder<RouteActionWorker>()
.setInputData(workDataOf(RouteActionWorker.KEY_CLIENT_ACTION_ID to clientActionId)) .setInputData(workDataOf(RouteActionWorker.KEY_CLIENT_ACTION_ID to clientActionId))
.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()) .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
@@ -0,0 +1,17 @@
package pl.firmatpp.kierowca.data.upload
import com.google.gson.Gson
private val routeActionPhotoDependencyGson = Gson()
fun routeActionReferencesPhotoClientRequest(action: RouteActionEntity, clientRequestId: String): Boolean {
val needle = clientRequestId.trim()
if (needle.isBlank()) return false
return runCatching {
routeActionPhotoDependencyGson
.fromJson(action.photoClientRequestIdsJson, Array<String>::class.java)
?.any { it.trim() == needle }
?: false
}.getOrDefault(false)
}
@@ -114,8 +114,10 @@ fun routeSyncCallout(actions: List<RouteActionEntity>): RouteSyncCalloutUi? {
} else { } else {
when (unresolved.status) { when (unresolved.status) {
RouteActionStatus.WaitingForPhotos -> "Czekam na wysłanie zdjęć etapu, potem wyślę zmianę statusu." RouteActionStatus.WaitingForPhotos -> "Czekam na wysłanie zdjęć etapu, potem wyślę zmianę statusu."
RouteActionStatus.Pending,
RouteActionStatus.Syncing -> "Trwa wysyłanie zmiany statusu kursu."
RouteActionStatus.FailedRetryable -> "Nie udało się wysłać. Aplikacja spróbuje ponownie." RouteActionStatus.FailedRetryable -> "Nie udało się wysłać. Aplikacja spróbuje ponownie."
else -> "Zmiana zapisana w telefonie. Wyślemypo odzyskaniu internetu." else -> "Zmiana zapisana w telefonie. Aplikacja wyśle ją automatycznie."
} }
}, },
isConflict = isConflict, isConflict = isConflict,
@@ -0,0 +1,31 @@
package pl.firmatpp.kierowca.data.upload
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class RouteActionPhotoDependenciesTest {
@Test
fun detectsRouteActionWaitingForConfirmedPhotoClientRequest() {
val action = action("""["photo-a","photo-b"]""")
assertTrue(routeActionReferencesPhotoClientRequest(action, "photo-b"))
assertFalse(routeActionReferencesPhotoClientRequest(action, "photo-c"))
}
@Test
fun malformedPhotoRequestJsonDoesNotMatch() {
assertFalse(routeActionReferencesPhotoClientRequest(action("not-json"), "photo-a"))
}
private fun action(photoClientRequestIdsJson: String): RouteActionEntity =
RouteActionEntity(
clientActionId = "action-1",
routeId = "route-1",
action = RouteActionType.Start,
weight = 12.5,
occurredAt = "2026-06-30T07:40:00Z",
photoClientRequestIdsJson = photoClientRequestIdsJson,
status = RouteActionStatus.WaitingForPhotos,
)
}