Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
11 changes: 8 additions & 3 deletions core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ public interface PowerSyncDatabase : Queries {
*
* Use @param [crudThrottleMs] to specify the time between CRUD operations. Defaults to 1000ms.
* Use @param [retryDelayMs] to specify the delay between retries after failure. Defaults to 5000ms.
* Use @param [params] to specify sync parameters from the client
*
* TODO: Internal Team - Status changes are reported on [statusStream].
*/

public suspend fun connect(connector: PowerSyncBackendConnector, crudThrottleMs: Long = 1000L,
retryDelayMs: Long = 5000L)
public suspend fun connect(
connector: PowerSyncBackendConnector,
crudThrottleMs: Long = 1000L,
retryDelayMs: Long = 5000L,
params: Map<String, Any>? = null
)


/**
Expand Down Expand Up @@ -94,4 +99,4 @@ public interface PowerSyncDatabase : Queries {
* To preserve data in local-only tables, set clearLocal to false.
*/
public suspend fun disconnectAndClear(clearLocal: Boolean = true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.powersync.db.schema.Schema
import com.powersync.sync.SyncStatus
import com.powersync.sync.SyncStream
import com.powersync.utils.JsonUtil
import com.powersync.utils.Strings.quoteIdentifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
Expand Down Expand Up @@ -85,7 +84,12 @@ internal class PowerSyncDatabaseImpl(
}

@OptIn(FlowPreview::class)
override suspend fun connect(connector: PowerSyncBackendConnector, crudThrottleMs: Long, retryDelayMs: Long) {
override suspend fun connect(
connector: PowerSyncBackendConnector,
crudThrottleMs: Long,
retryDelayMs: Long,
params: Map<String, Any>?)
{
// close connection if one is open
disconnect()

Expand All @@ -95,7 +99,8 @@ internal class PowerSyncDatabaseImpl(
connector = connector,
uploadCrud = suspend { connector.uploadData(this) },
retryDelayMs = retryDelayMs,
logger = logger
logger = logger,
params = params
)

syncJob = scope.launch {
Expand Down
7 changes: 5 additions & 2 deletions core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.powersync.bucket.WriteCheckpointResponse
import co.touchlab.stately.concurrency.AtomicBoolean
import com.powersync.connectors.PowerSyncBackendConnector
import com.powersync.utils.JsonUtil
import com.powersync.utils.convertMapToJson
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.HttpTimeout
Expand Down Expand Up @@ -41,7 +42,8 @@ internal class SyncStream(
private val connector: PowerSyncBackendConnector,
private val uploadCrud: suspend () -> Unit,
private val retryDelayMs: Long = 5000L,
private val logger: Logger
private val logger: Logger,
private val params: Map<String, Any>?
) {
private var isUploadingCrud = AtomicBoolean(false)

Expand Down Expand Up @@ -245,7 +247,8 @@ internal class SyncStream(

val req = StreamingSyncRequest(
buckets = initialBuckets.map { (bucket, after) -> BucketRequest(bucket, after) },
clientId = clientId!!
clientId = clientId!!,
parameters = convertMapToJson(params)
)

streamingSyncRequest(req).collect { value ->
Expand Down
34 changes: 34 additions & 0 deletions core/src/commonMain/kotlin/com/powersync/utils/Json.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.powersync.utils

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

/**
* A global instance of a JSON serializer.
Expand All @@ -12,3 +17,32 @@ internal object JsonUtil {
}
}

internal fun convertMapToJson(map: Map<String, Any?>?): JsonObject {
if (map == null) return JsonObject(emptyMap())

val result = map.mapValues { (_, value) ->
convertToJsonElement(value)
}
return JsonObject(result)
}


internal fun convertToJsonElement(value: Any?): JsonElement {
return when (value) {
null -> JsonNull
is Int -> JsonPrimitive(value)
is Long -> JsonPrimitive(value)
is Double -> JsonPrimitive(value)
is Float -> JsonPrimitive(value)
is Boolean -> JsonPrimitive(value)
is String -> JsonPrimitive(value)
is Map<*, *> -> convertMapToJson(value as Map<String, Any?>)
is List<*> -> convertListToJsonArray(value)
is Array<*> -> convertListToJsonArray(value.toList())
else -> JsonNull
}
}

internal fun convertListToJsonArray(list: List<Any?>): JsonArray {
return JsonArray(list.map { convertToJsonElement(it) })
}
128 changes: 128 additions & 0 deletions core/src/commonTest/kotlin/com/powersync/utils/JsonTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.powersync.utils

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlin.test.*

class JsonTest {
@Test
fun testEmptyMap() {
val emptyMap = emptyMap<String, Any>()
val result = convertMapToJson(emptyMap)
assertEquals(0, result.size)
}

@Test
fun testNull() {
val result = convertMapToJson(null)
assertEquals(JsonObject(mapOf()), result)
}

@Test
fun testIntegerMap() {
val testMap = mapOf("int" to 1)
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("int" to JsonPrimitive(1))), result)
}

@Test
fun testStringMap() {
val testMap = mapOf("string" to "string")
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("string" to JsonPrimitive("string"))), result)
}

@Test
fun testLongMap() {
val testMap = mapOf("double" to 123L)
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("double" to JsonPrimitive(123L))), result)
}

@Test
fun testBooleanMap() {
val testMap = mapOf("boolean" to false)
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("boolean" to JsonPrimitive(false))), result)
}

@Test
fun testDoubleMap() {
val testMap = mapOf("double" to 0.02)
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("double" to JsonPrimitive(0.02))), result)
}

@Test
fun testArrayMap() {
val testMap = mapOf("array" to arrayOf(1, 2, 3))
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("array" to JsonArray(listOf(JsonPrimitive(1), JsonPrimitive(2), JsonPrimitive(3))))), result)
}

@Test
fun testListMap() {
val testMap = mapOf("list" to listOf("a", "b", "c"))
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("list" to JsonArray(listOf(JsonPrimitive("a"), JsonPrimitive("b"), JsonPrimitive("c"))))), result)
}

@Test
fun testNestedMap() {
val testMap = mapOf(
"nested" to mapOf(
"int" to 1,
"string" to "value"
)
)
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf(
"nested" to JsonObject(mapOf(
"int" to JsonPrimitive(1),
"string" to JsonPrimitive("value")
))
)), result)
}

@Test
fun testComplexNestedStructure() {
val testMap = mapOf(
"string" to "value",
"int" to 42,
"list" to listOf(1, "two", 3.0),
"nestedMap" to mapOf(
"array" to arrayOf(true, false),
"nestedList" to listOf(
mapOf("key" to "value"),
listOf(1, 2, 3)
)
)
)
val result = convertMapToJson(testMap)

val expected = JsonObject(mapOf(
"string" to JsonPrimitive("value"),
"int" to JsonPrimitive(42),
"list" to JsonArray(listOf(JsonPrimitive(1), JsonPrimitive("two"), JsonPrimitive(3.0))),
"nestedMap" to JsonObject(mapOf(
"array" to JsonArray(listOf(JsonPrimitive(true), JsonPrimitive(false))),
"nestedList" to JsonArray(listOf(
JsonObject(mapOf("key" to JsonPrimitive("value"))),
JsonArray(listOf(JsonPrimitive(1), JsonPrimitive(2), JsonPrimitive(3)))
))
))
))

assertEquals(expected, result)
}

@OptIn(ExperimentalSerializationApi::class)
@Test
fun testMapWithUnsupportedType() {
val testMap = mapOf("unsupported" to object {})
val result = convertMapToJson(testMap)
assertEquals(JsonObject(mapOf("unsupported" to JsonPrimitive(null))), result)
}
}
Loading