Skip to content

HTTP Client Config #229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
23 changes: 17 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 1.4.0 (unreleased)

* Added the ability to log PowerSync service HTTP request information via specifying a
`SyncClientConfiguration` in the `SyncOptions.clientConfiguration` parameter used in
`PowerSyncDatabase.connect()` calls.

## 1.3.1

* Update SQLite to 3.50.3.
Expand All @@ -12,7 +18,8 @@
## 1.3.0

* Support tables created outside of PowerSync with the `RawTable` API.
For more information, see [the documentation](https://docs.powersync.com/usage/use-case-examples/raw-tables).
For more information,
see [the documentation](https://docs.powersync.com/usage/use-case-examples/raw-tables).
* Fix `runWrapped` catching cancellation exceptions.
* Fix errors in `PowerSyncBackendConnector.fetchCredentials()` crashing Android apps.

Expand All @@ -23,7 +30,8 @@

## 1.2.1

* [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as connection errors. The connector now reports errors for codes `>=400`.
* [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as
connection errors. The connector now reports errors for codes `>=400`.
* Update PowerSync core extension to `0.4.1`, fixing an issue with the new Rust client.
* Rust sync client: Fix writes made while offline not being uploaded reliably.
* Add watchOS support.
Expand All @@ -32,7 +40,7 @@

* Add a new sync client implementation written in Rust instead of Kotlin. While this client is still
experimental, we intend to make it the default in the future. The main benefit of this client is
faster sync performance, but upcoming features will also require this client. We encourage
faster sync performance, but upcoming features will also require this client. We encourage
interested users to try it out by opting in to `ExperimentalPowerSyncAPI` and passing options when
connecting:
```Kotlin
Expand Down Expand Up @@ -62,10 +70,13 @@

## 1.1.0

* Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous values on updates.
* Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for updates.
* Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous
values on updates.
* Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for
updates.
The configured metadata is available through `CrudEntry.metadata`.
* Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change any values.
* Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change
any values.

## 1.0.1

Expand Down
13 changes: 11 additions & 2 deletions PowerSyncKotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(project(":core"))
implementation(libs.ktor.client.logging)
}
}
}
Expand All @@ -58,8 +59,16 @@ listOf("Debug", "Release").forEach { buildType ->
val originalFramework = tasks.getByName("assemblePowerSyncKotlin${buildType}XCFramework")
dependsOn(originalFramework)

val source = project.layout.buildDirectory.map { it.dir("XCFrameworks/${buildType.lowercase()}") }.get().asFile
val archiveFile = project.layout.buildDirectory.map { it.file("FrameworkArchives/PowersyncKotlin$buildType.zip") }.get().asFile
val source =
project.layout.buildDirectory
.map { it.dir("XCFrameworks/${buildType.lowercase()}") }
.get()
.asFile
val archiveFile =
project.layout.buildDirectory
.map { it.file("FrameworkArchives/PowersyncKotlin$buildType.zip") }
.get()
.asFile

archiveFile.parentFile.mkdirs()
archiveFile.delete()
Expand Down
60 changes: 59 additions & 1 deletion PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

package com.powersync

import com.powersync.sync.SyncClientConfiguration
import com.powersync.sync.SyncOptions
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.logging.Logger as KtorLogger

/**
* Helper class designed to bridge SKIEE methods and allow them to throw
Expand All @@ -17,16 +21,70 @@ import com.powersync.sync.SyncOptions
public fun throwPowerSyncException(exception: PowerSyncException): Unit = throw exception

/**
* Creates a [ConnectionMethod] based on simple booleans, because creating the actual instance with
* A small wrapper around the Ktor LogLevel enum to allow
* specifying the log level from Swift without exposing the Ktor plugin types.
*/
public enum class SwiftSyncRequestLogLevel {
ALL,
HEADERS,
BODY,
INFO,
NONE,
}

/**
* Mapper function to Ktor LogLevel
*/
internal fun SwiftSyncRequestLogLevel.toKtorLogLevel(): LogLevel =
when (this) {
SwiftSyncRequestLogLevel.ALL -> LogLevel.ALL
SwiftSyncRequestLogLevel.HEADERS -> LogLevel.HEADERS
SwiftSyncRequestLogLevel.BODY -> LogLevel.BODY
SwiftSyncRequestLogLevel.INFO -> LogLevel.INFO
SwiftSyncRequestLogLevel.NONE -> LogLevel.NONE
}

/**
* Configuration which is used to configure the Ktor logging plugin
*/
public data class SwiftRequestLoggerConfig(
public val logLevel: SwiftSyncRequestLogLevel,
public val log: (message: String) -> Unit,
)

/**
* Creates a Ktor [SyncClientConfiguration.ExtendedConfig] that extends the default Ktor client.
* Specifying a [SwiftRequestLoggerConfig] will install the Ktor logging plugin with the specified configuration.
*/
public fun createExtendedSyncClientConfiguration(loggingConfig: SwiftRequestLoggerConfig? = null): SyncClientConfiguration =
SyncClientConfiguration.ExtendedConfig {
if (loggingConfig != null) {
install(Logging) {
// Pass everything to the provided logger. The logger controls the active level
level = loggingConfig.logLevel.toKtorLogLevel()
logger =
object : KtorLogger {
override fun log(message: String) {
loggingConfig.log(message)
}
}
}
}
}

/**
* Creates a [SyncOptions] based on simple parameters, because creating the actual instance with
* the default constructor is not possible from Swift due to an optional argument with an internal
* default value.
*/
@OptIn(ExperimentalPowerSyncAPI::class)
public fun createSyncOptions(
newClient: Boolean,
userAgent: String,
loggingConfig: SwiftRequestLoggerConfig? = null,
): SyncOptions =
SyncOptions(
newClientImplementation = newClient,
userAgent = userAgent,
clientConfiguration = createExtendedSyncClientConfiguration(loggingConfig),
)
9 changes: 5 additions & 4 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import org.gradle.internal.os.OperatingSystem
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
import org.jetbrains.kotlin.konan.target.Family
import java.nio.file.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.writeText
import org.jetbrains.kotlin.konan.target.Family

plugins {
alias(libs.plugins.kotlinMultiplatform)
Expand Down Expand Up @@ -140,12 +140,13 @@ val generateVersionConstant by tasks.registering {
dir.mkdir()
val rootPath = dir.toPath()

val source = """
val source =
"""
package $packageName

internal const val LIBRARY_VERSION: String = "$currentVersion"

""".trimIndent()
""".trimIndent()

val packageRoot = packageName.split('.').fold(rootPath, Path::resolve)
packageRoot.createDirectories()
Expand Down Expand Up @@ -204,7 +205,6 @@ kotlin {
dependencies {
implementation(libs.uuid)
implementation(libs.kotlin.stdlib)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.contentnegotiation)
implementation(libs.ktor.serialization.json)
implementation(libs.kotlinx.io)
Expand All @@ -213,6 +213,7 @@ kotlin {
implementation(libs.stately.concurrency)
implementation(libs.configuration.annotations)
api(projects.persistence)
api(libs.ktor.client.core)
api(libs.kermit)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.powersync.sync

import com.powersync.ExperimentalPowerSyncAPI
import com.powersync.testutils.ActiveDatabaseTest

/**
* Small utility to run tests both with the legacy Kotlin sync implementation and the new
Expand All @@ -11,7 +12,9 @@ abstract class AbstractSyncTest(
protected val useBson: Boolean = false,
) {
@OptIn(ExperimentalPowerSyncAPI::class)
val options: SyncOptions get() {
return SyncOptions(useNewSyncImplementation)
}
internal fun ActiveDatabaseTest.getOptions(): SyncOptions =
SyncOptions(
useNewSyncImplementation,
clientConfiguration = SyncClientConfiguration.ExistingClient(createSyncClient()),
)
}
Loading
Loading