feat(sync): implement Delta-Sync API and update clients to support offline-first workflow
Added `/ping/sync` endpoint with timestamp-based Delta-Sync functionality to efficiently support offline-first clients. Extended `PingApi` and frontend clients (`PingApiClient`, `PingApiKoinClient`) with `syncPings`. Updated repository, service, and controller logic for sync handling, including new JPA query `findByCreatedAtAfter`. Adjusted test doubles and completed unit tests for backend and frontend alignment. Documented sync approach and API usage.
This commit is contained in:
+7
@@ -4,6 +4,7 @@ import at.mocode.ping.api.PingApi
|
||||
import at.mocode.ping.api.PingResponse
|
||||
import at.mocode.ping.api.EnhancedPingResponse
|
||||
import at.mocode.ping.api.HealthResponse
|
||||
import at.mocode.ping.api.PingEvent
|
||||
import at.mocode.shared.core.AppConstants
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
@@ -52,4 +53,10 @@ class PingApiClient(
|
||||
override suspend fun securePing(): PingResponse {
|
||||
return client.get("$baseUrl/api/ping/secure").body()
|
||||
}
|
||||
|
||||
override suspend fun syncPings(lastSyncTimestamp: Long): List<PingEvent> {
|
||||
return client.get("$baseUrl/api/ping/sync") {
|
||||
parameter("lastSyncTimestamp", lastSyncTimestamp)
|
||||
}.body()
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -3,6 +3,7 @@ package at.mocode.clients.pingfeature
|
||||
import at.mocode.ping.api.EnhancedPingResponse
|
||||
import at.mocode.ping.api.HealthResponse
|
||||
import at.mocode.ping.api.PingApi
|
||||
import at.mocode.ping.api.PingEvent
|
||||
import at.mocode.ping.api.PingResponse
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
@@ -34,4 +35,10 @@ class PingApiKoinClient(private val client: HttpClient) : PingApi {
|
||||
override suspend fun securePing(): PingResponse {
|
||||
return client.get("/api/ping/secure").body()
|
||||
}
|
||||
|
||||
override suspend fun syncPings(lastSyncTimestamp: Long): List<PingEvent> {
|
||||
return client.get("/api/ping/sync") {
|
||||
url.parameters.append("lastSyncTimestamp", lastSyncTimestamp.toString())
|
||||
}.body()
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -4,6 +4,7 @@ import at.mocode.ping.api.PingApi
|
||||
import at.mocode.ping.api.PingResponse
|
||||
import at.mocode.ping.api.EnhancedPingResponse
|
||||
import at.mocode.ping.api.HealthResponse
|
||||
import at.mocode.ping.api.PingEvent
|
||||
|
||||
/**
|
||||
* Test double implementation of PingApi for testing purposes.
|
||||
@@ -23,6 +24,7 @@ class TestPingApiClient : PingApi {
|
||||
var healthResponse: HealthResponse? = null
|
||||
var publicPingResponse: PingResponse? = null
|
||||
var securePingResponse: PingResponse? = null
|
||||
var syncPingsResponse: List<PingEvent> = emptyList()
|
||||
|
||||
// Call tracking
|
||||
var simplePingCalled = false
|
||||
@@ -30,6 +32,7 @@ class TestPingApiClient : PingApi {
|
||||
var healthCheckCalled = false
|
||||
var publicPingCalled = false
|
||||
var securePingCalled = false
|
||||
var syncPingsCalledWith: Long? = null
|
||||
var callCount = 0
|
||||
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
@@ -91,6 +94,21 @@ class TestPingApiClient : PingApi {
|
||||
return handleRequest(securePingResponse)
|
||||
}
|
||||
|
||||
override suspend fun syncPings(lastSyncTimestamp: Long): List<PingEvent> {
|
||||
syncPingsCalledWith = lastSyncTimestamp
|
||||
callCount++
|
||||
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return syncPingsResponse
|
||||
}
|
||||
|
||||
private suspend fun handleRequest(response: PingResponse?): PingResponse {
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
@@ -118,11 +136,13 @@ class TestPingApiClient : PingApi {
|
||||
healthResponse = null
|
||||
publicPingResponse = null
|
||||
securePingResponse = null
|
||||
syncPingsResponse = emptyList()
|
||||
simplePingCalled = false
|
||||
enhancedPingCalledWith = null
|
||||
healthCheckCalled = false
|
||||
publicPingCalled = false
|
||||
securePingCalled = false
|
||||
syncPingsCalledWith = null
|
||||
callCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user