Skip to content

Commit d111bec

Browse files
author
DominicGBauer
committed
feat: add JsonParam sealed class to provide type safety
1 parent a2f953b commit d111bec

File tree

6 files changed

+251
-117
lines changed

6 files changed

+251
-117
lines changed

core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import com.powersync.db.Queries
55
import com.powersync.db.crud.CrudBatch
66
import com.powersync.db.crud.CrudTransaction
77
import com.powersync.sync.SyncStatus
8+
import com.powersync.utils.JsonParam
9+
import com.powersync.utils.toJsonParam
810

911
/**
1012
* A PowerSync managed database.
@@ -27,18 +29,35 @@ public interface PowerSyncDatabase : Queries {
2729
*
2830
* The connection is automatically re-opened if it fails for any reason.
2931
*
32+
* Use @param [connector] to specify the [PowerSyncBackendConnector].
3033
* Use @param [crudThrottleMs] to specify the time between CRUD operations. Defaults to 1000ms.
3134
* Use @param [retryDelayMs] to specify the delay between retries after failure. Defaults to 5000ms.
32-
* Use @param [params] to specify sync parameters from the client
35+
* Use @param [params] to specify sync parameters from the client. Can be constructed using [toJsonParam].
3336
*
37+
* Example usage:
38+
* ```
39+
* val params = mapOf(
40+
* "name" to "John",
41+
* "age" to 30,
42+
* "isStudent" to false,
43+
* "grades" to listOf(85, 90, 78)
44+
* ).mapValues { it.value.toJsonParam() }
45+
*
46+
* connect(
47+
* connector = connector,
48+
* crudThrottleMs = 2000L,
49+
* retryDelayMs = 10000L,
50+
* params = params
51+
* )
52+
* ```
3453
* TODO: Internal Team - Status changes are reported on [statusStream].
3554
*/
3655

3756
public suspend fun connect(
3857
connector: PowerSyncBackendConnector,
3958
crudThrottleMs: Long = 1000L,
4059
retryDelayMs: Long = 5000L,
41-
params: Map<String, Any>? = null
60+
params: Map<String, JsonParam?>? = null
4261
)
4362

4463

core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.powersync.db.internal.PowerSyncTransaction
2020
import com.powersync.db.schema.Schema
2121
import com.powersync.sync.SyncStatus
2222
import com.powersync.sync.SyncStream
23+
import com.powersync.utils.JsonParam
2324
import com.powersync.utils.JsonUtil
2425
import kotlinx.coroutines.CoroutineScope
2526
import kotlinx.coroutines.FlowPreview
@@ -88,7 +89,7 @@ internal class PowerSyncDatabaseImpl(
8889
connector: PowerSyncBackendConnector,
8990
crudThrottleMs: Long,
9091
retryDelayMs: Long,
91-
params: Map<String, Any>?)
92+
params: Map<String, JsonParam?>?)
9293
{
9394
// close connection if one is open
9495
disconnect()

core/src/commonMain/kotlin/com/powersync/sync/StreamingSyncRequest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ internal data class StreamingSyncRequest(
1010
val buckets: List<BucketRequest>,
1111
@SerialName("include_checksum") val includeChecksum: Boolean = true,
1212
@SerialName("client_id") val clientId: String,
13-
val parameters: JsonObject = JsonObject(mapOf())
13+
val parameters: JsonObject? = JsonObject(mapOf())
1414
) {
1515
@SerialName("raw_data") private val rawData: Boolean = true
1616
}

core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import com.powersync.bucket.Checkpoint
88
import com.powersync.bucket.WriteCheckpointResponse
99
import co.touchlab.stately.concurrency.AtomicBoolean
1010
import com.powersync.connectors.PowerSyncBackendConnector
11+
import com.powersync.utils.JsonParam
1112
import com.powersync.utils.JsonUtil
12-
import com.powersync.utils.convertMapToJson
13+
import com.powersync.utils.toJsonObject
1314
import io.ktor.client.HttpClient
1415
import io.ktor.client.call.body
1516
import io.ktor.client.plugins.HttpTimeout
@@ -43,7 +44,7 @@ internal class SyncStream(
4344
private val uploadCrud: suspend () -> Unit,
4445
private val retryDelayMs: Long = 5000L,
4546
private val logger: Logger,
46-
private val params: Map<String, Any>?
47+
private val params: Map<String, JsonParam?>?
4748
) {
4849
private var isUploadingCrud = AtomicBoolean(false)
4950

@@ -248,7 +249,7 @@ internal class SyncStream(
248249
val req = StreamingSyncRequest(
249250
buckets = initialBuckets.map { (bucket, after) -> BucketRequest(bucket, after) },
250251
clientId = clientId!!,
251-
parameters = convertMapToJson(params)
252+
parameters = params?.toJsonObject()
252253
)
253254

254255
streamingSyncRequest(req).collect { value ->

core/src/commonMain/kotlin/com/powersync/utils/Json.kt

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,41 @@ internal object JsonUtil {
1717
}
1818
}
1919

20-
internal fun convertMapToJson(map: Map<String, Any?>?): JsonObject {
21-
if (map == null) return JsonObject(emptyMap())
20+
public sealed class JsonParam {
21+
public data class Number(val value: kotlin.Number) : JsonParam()
22+
public data class String(val value: kotlin.String) : JsonParam()
23+
public data class Boolean(val value: kotlin.Boolean) : JsonParam()
24+
public data class Map(val value: kotlin.collections.Map<kotlin.String, JsonParam>) : JsonParam()
25+
public data class Collection(val value: kotlin.collections.Collection<JsonParam>) : JsonParam()
26+
public data class JsonElement(val value: kotlinx.serialization.json.JsonElement) : JsonParam()
27+
public data object Null : JsonParam()
2228

23-
val result = map.mapValues { (_, value) ->
24-
convertToJsonElement(value)
29+
internal fun toJsonElement(): kotlinx.serialization.json.JsonElement = when (this) {
30+
is Number -> JsonPrimitive(value)
31+
is String -> JsonPrimitive(value)
32+
is Boolean -> JsonPrimitive(value)
33+
is Map -> JsonObject(value.mapValues { it.value.toJsonElement() })
34+
is Collection -> JsonArray(value.map { it.toJsonElement() })
35+
is JsonElement -> value
36+
Null -> JsonNull
2537
}
26-
return JsonObject(result)
2738
}
2839

29-
30-
internal fun convertToJsonElement(value: Any?): JsonElement {
31-
return when (value) {
32-
null -> JsonNull
33-
is Int -> JsonPrimitive(value)
34-
is Long -> JsonPrimitive(value)
35-
is Double -> JsonPrimitive(value)
36-
is Float -> JsonPrimitive(value)
37-
is Boolean -> JsonPrimitive(value)
38-
is String -> JsonPrimitive(value)
39-
is Map<*, *> -> convertMapToJson(value as Map<String, Any?>)
40-
is List<*> -> convertListToJsonArray(value)
41-
is Array<*> -> convertListToJsonArray(value.toList())
42-
else -> JsonNull
43-
}
40+
public fun Any?.toJsonParam(): JsonParam = when (this) {
41+
is Number -> JsonParam.Number(this)
42+
is String -> JsonParam.String(this)
43+
is Boolean -> JsonParam.Boolean(this)
44+
is Map<*, *> -> JsonParam.Map(this.mapKeys { it.key.toString() }
45+
.mapValues { it.value.toJsonParam() })
46+
is List<*> -> JsonParam.Collection(this.map { it.toJsonParam() })
47+
is Array<*> -> JsonParam.Collection(this.map { it.toJsonParam() })
48+
is JsonElement -> JsonParam.JsonElement(this)
49+
null -> JsonParam.Null
50+
else -> throw IllegalArgumentException("Unsupported type for JsonParam: $this")
4451
}
4552

46-
internal fun convertListToJsonArray(list: List<Any?>): JsonArray {
47-
return JsonArray(list.map { convertToJsonElement(it) })
53+
public fun Map<String, JsonParam?>.toJsonObject(): JsonObject {
54+
return JsonObject(this.mapValues { (_, value) ->
55+
value?.toJsonElement() ?: JsonNull
56+
})
4857
}

0 commit comments

Comments
 (0)