fix(sync): erros 5xx viram Transient com retry; SSE multilinha; trata Failed/NotConfigured
- SyncOutcome.Transient para 5xx (push/pull/serverOutcome); worker da retry - runSync: Failed -> Error, NotConfigured respeitado (evita cast crash) - SseClient: junta linhas data: com \n (spec SSE) - ApiClient: remove imports sem uso
This commit is contained in:
1 parent
e608493c81
commit
d10853bcaa
4 files changed
+38
-2
No files matched your search
@@ -59,8 +59,11 @@ class SseClient(private val baseUrl: String, private val token: String, private
|
||||
currentCoroutineContext().ensureActive()
|
||||
val line = reader.readLine() ?: break
|
||||
when {
|
||||
line.startsWith("data: ") -> data.append(line.removePrefix("data: "))
|
||||
line.startsWith("data:") -> data.append(line.removePrefix("data:").trimStart())
|
||||
line.startsWith("data:") -> {
|
||||
// evento multilinha: junta os blocos com \n (spec SSE)
|
||||
if (data.isNotEmpty()) data.append('\n')
|
||||
data.append(line.removePrefix("data:").trimStart())
|
||||
}
|
||||
line.isEmpty() && data.isNotEmpty() -> {
|
||||
val raw = data.toString()
|
||||
data = StringBuilder()
|
||||
|
||||
@@ -22,5 +22,7 @@ sealed class SyncOutcome {
|
||||
data class Success(val pushed: Int, val pulled: Int, val conflicts: Int) : SyncOutcome()
|
||||
data class NetworkDown(val message: String) : SyncOutcome()
|
||||
data object Unauthorized : SyncOutcome()
|
||||
/** Erro transitório do servidor (5xx): pode repetir depois. */
|
||||
data class Transient(val message: String) : SyncOutcome()
|
||||
data class Failed(val message: String) : SyncOutcome()
|
||||
}
|
||||
@@ -81,6 +81,19 @@ class SyncEngine(
|
||||
status.set { it.copy(phase = SyncPhase.Offline, message = push.message) }
|
||||
return push
|
||||
}
|
||||
if (push is SyncOutcome.Transient) {
|
||||
status.set { it.copy(phase = SyncPhase.Offline, message = push.message) }
|
||||
return push
|
||||
}
|
||||
|
||||
if (push is SyncOutcome.Failed) {
|
||||
status.set { it.copy(phase = SyncPhase.Error, message = push.message) }
|
||||
return push
|
||||
}
|
||||
if (push is SyncOutcome.NotConfigured) {
|
||||
status.set { it.copy(phase = SyncPhase.NotConfigured) }
|
||||
return push
|
||||
}
|
||||
|
||||
val pull = pull(client)
|
||||
if (pull is SyncOutcome.Unauthorized) {
|
||||
@@ -91,6 +104,14 @@ class SyncEngine(
|
||||
status.set { it.copy(phase = SyncPhase.Offline, message = pull.message) }
|
||||
return pull
|
||||
}
|
||||
if (pull is SyncOutcome.Transient) {
|
||||
status.set { it.copy(phase = SyncPhase.Offline, message = pull.message) }
|
||||
return pull
|
||||
}
|
||||
if (pull is SyncOutcome.Failed) {
|
||||
status.set { it.copy(phase = SyncPhase.Error, message = pull.message) }
|
||||
return pull
|
||||
}
|
||||
|
||||
val ok = (push as SyncOutcome.Success) to (pull as SyncOutcome.Success)
|
||||
val conflicts = ok.first.conflicts + ok.second.conflicts
|
||||
@@ -151,6 +172,7 @@ class SyncEngine(
|
||||
}
|
||||
is ApiResult.Network -> return SyncOutcome.NetworkDown(resp.message)
|
||||
is ApiResult.Unauthorized -> return SyncOutcome.Unauthorized
|
||||
is ApiResult.Server -> return serverOutcome("create ${note.id}", resp)
|
||||
else -> return SyncOutcome.Failed("create ${note.id}: ${describe(resp)}")
|
||||
}
|
||||
}
|
||||
@@ -189,6 +211,7 @@ class SyncEngine(
|
||||
}
|
||||
is ApiResult.Network -> return SyncOutcome.NetworkDown(resp.message)
|
||||
is ApiResult.Unauthorized -> return SyncOutcome.Unauthorized
|
||||
is ApiResult.Server -> return serverOutcome("update ${note.id}", resp)
|
||||
else -> return SyncOutcome.Failed("update ${note.id}: ${describe(resp)}")
|
||||
}
|
||||
}
|
||||
@@ -200,6 +223,7 @@ class SyncEngine(
|
||||
is ApiResult.Ok, is ApiResult.NotFound -> Unit
|
||||
is ApiResult.Network -> return SyncOutcome.NetworkDown(resp.message)
|
||||
is ApiResult.Unauthorized -> return SyncOutcome.Unauthorized
|
||||
is ApiResult.Server -> return serverOutcome("delete ${note.id}", resp)
|
||||
else -> return SyncOutcome.Failed("delete ${note.id}: ${describe(resp)}")
|
||||
}
|
||||
}
|
||||
@@ -290,12 +314,14 @@ class SyncEngine(
|
||||
}
|
||||
is ApiResult.Network -> return SyncOutcome.NetworkDown(cats.message)
|
||||
is ApiResult.Unauthorized -> return SyncOutcome.Unauthorized
|
||||
is ApiResult.Server -> return serverOutcome("categories", cats)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
val list = client.listNotes()
|
||||
if (list is ApiResult.Network) return SyncOutcome.NetworkDown(list.message)
|
||||
if (list is ApiResult.Unauthorized) return SyncOutcome.Unauthorized
|
||||
if (list is ApiResult.Server) return serverOutcome("list", list)
|
||||
if (list !is ApiResult.Ok) return SyncOutcome.Failed("list: ${describe(list)}")
|
||||
|
||||
val serverIds = list.value.map { it.id }.toSet()
|
||||
@@ -345,6 +371,10 @@ class SyncEngine(
|
||||
|
||||
private fun NoteDetailDto.toEntity() = toNoteEntity()
|
||||
|
||||
private fun serverOutcome(prefix: String, r: ApiResult.Server): SyncOutcome =
|
||||
if (r.code >= 500) SyncOutcome.Transient("$prefix: HTTP ${r.code} ${r.message}")
|
||||
else SyncOutcome.Failed("$prefix: HTTP ${r.code} ${r.message}")
|
||||
|
||||
private fun describe(r: ApiResult<*>): String = when (r) {
|
||||
is ApiResult.Server -> "HTTP ${r.code} ${r.message}"
|
||||
is ApiResult.Network -> r.message
|
||||
|
||||
@@ -30,6 +30,7 @@ class SyncWorker(context: Context, params: WorkerParameters) :
|
||||
)
|
||||
return when (val outcome = engine.fullSync(trigger = inputData.getString(TRIGGER) ?: "auto")) {
|
||||
is com.labexposed.notas.domain.SyncOutcome.NetworkDown -> Result.retry()
|
||||
is com.labexposed.notas.domain.SyncOutcome.Transient -> Result.retry()
|
||||
is com.labexposed.notas.domain.SyncOutcome.Failed -> Result.failure()
|
||||
is com.labexposed.notas.domain.SyncOutcome.Unauthorized -> Result.success()
|
||||
else -> Result.success()
|
||||
|
||||
Reference in new issue
Block a user