Skip to content

Commit 6671a5d

Browse files
authored
Merge branch 'main' into mssql-readme
2 parents a56ac81 + 33d5b01 commit 6671a5d

File tree

9 files changed

+99
-14
lines changed

9 files changed

+99
-14
lines changed

CHANGELOG.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
# Changelog
22

3+
## 1.10.0 (unreleased)
4+
5+
- Add `appMetadata` parameter to `PowerSyncDatabase.connect()` to include application metadata in
6+
sync requests. This metadata is merged into sync requests and displayed in PowerSync service logs.
7+
8+
Note: This requires a PowerSync service version `>=1.17.0` in order for logs to display metadata.
9+
10+
```kotlin
11+
database.connect(
12+
connector = connector,
13+
appMetadata = mapOf(
14+
"appVersion" to "1.0.0",
15+
"deviceId" to "device456"
16+
)
17+
)
18+
```
19+
320
## 1.9.0
421

5-
- Updated user agent string formats to allow viewing version distributions in the new PowerSync dashboard.
22+
- Updated user agent string formats to allow viewing version distributions in the new PowerSync
23+
dashboard.
624
- Sync options: `newClientImplementation` is now the default.
725
- Make `androidx.sqlite:sqlite-bundled` an API dependency of `:core` to avoid toolchain warnings.
8-
- On Apple platforms, use a websocket protocol as a workaround to clients not supporting backpressure in HTTP response
26+
- On Apple platforms, use a websocket protocol as a workaround to clients not supporting
27+
backpressure in HTTP response
928
streams.
1029

1130
## 1.8.1
@@ -14,15 +33,19 @@
1433

1534
## 1.8.0
1635

17-
- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on
36+
- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on
1837
`com.powersync:powersync-common` where most logic is implemented.
19-
- __POTENTIALLY BREAKING CHANGE__: If you were injecting a `DatabaseDriverFactory` into Koin or Dagger, note that the
20-
`PowerSyncDatabase()` factory method now takes a more generic `PersistentConnectionFactory`.
21-
- If you're using `PowerSyncDatabase.inMemory`, you explicitly have to import `com.powersync.inMemory` now.
38+
- __POTENTIALLY BREAKING CHANGE__: If you were injecting a `DatabaseDriverFactory` into Koin or
39+
Dagger, note that the
40+
`PowerSyncDatabase()` factory method now takes a more generic `PersistentConnectionFactory`.
41+
- If you're using `PowerSyncDatabase.inMemory`, you explicitly have to import
42+
`com.powersync.inMemory` now.
2243
- Update the PowerSync core extension to version 0.4.8.
23-
- Add the `soft` flag to `disconnectAndClear()` which keeps an internal copy of synced data in the database, allowing
44+
- Add the `soft` flag to `disconnectAndClear()` which keeps an internal copy of synced data in the
45+
database, allowing
2446
faster re-sync if a compatible token is used in the next `connect()` call.
25-
- Add the `clear` parameter to `RawTable` to run a statement helping the core extension clear raw tables.
47+
- Add the `clear` parameter to `RawTable` to run a statement helping the core extension clear raw
48+
tables.
2649

2750
## 1.7.0
2851

common/src/commonIntegrationTest/kotlin/com/powersync/sync/SyncIntegrationTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,33 @@ abstract class BaseSyncIntegrationTest(
9595
}
9696
}
9797

98+
@Test
99+
fun useAppMetadata() =
100+
databaseTest {
101+
database.connect(
102+
connector,
103+
options = getOptions(),
104+
appMetadata =
105+
mapOf(
106+
"app_version" to "1.0.0",
107+
),
108+
)
109+
turbineScope(timeout = 10.0.seconds) {
110+
val turbine = database.currentStatus.asFlow().testIn(this)
111+
turbine.waitFor { it.connected }
112+
turbine.cancel()
113+
}
114+
115+
requestedSyncStreams shouldHaveSingleElement {
116+
val meta = it.jsonObject["app_metadata"]!!.jsonObject
117+
meta.keys shouldHaveSingleElement "app_version"
118+
meta.values
119+
.first()
120+
.jsonPrimitive.content shouldBe "1.0.0"
121+
true
122+
}
123+
}
124+
98125
@Test
99126
@OptIn(DelicateCoroutinesApi::class)
100127
fun closesResponseStreamOnDatabaseClose() =

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public interface PowerSyncDatabase : Queries {
8080
* Use @param [crudThrottleMs] to specify the time between CRUD operations. Defaults to 1000ms.
8181
* Use @param [retryDelayMs] to specify the delay between retries after failure. Defaults to 5000ms.
8282
* Use @param [params] to specify sync parameters from the client.
83+
* Use @param [appMetadata] to specify application metadata that will be displayed in PowerSync service logs.
8384
*
8485
* Example usage:
8586
* ```
@@ -91,11 +92,17 @@ public interface PowerSyncDatabase : Queries {
9192
* )
9293
* )
9394
*
95+
* val appMetadata = mapOf(
96+
* "appVersion" to "1.0.0",
97+
* "deviceId" to "device456"
98+
* )
99+
*
94100
* connect(
95101
* connector = connector,
96102
* crudThrottleMs = 2000L,
97103
* retryDelayMs = 10000L,
98-
* params = params
104+
* params = params,
105+
* appMetadata = appMetadata
99106
* )
100107
* ```
101108
*/
@@ -106,6 +113,7 @@ public interface PowerSyncDatabase : Queries {
106113
retryDelayMs: Long = 5000L,
107114
params: Map<String, JsonParam?> = emptyMap(),
108115
options: SyncOptions = SyncOptions(),
116+
appMetadata: Map<String, String> = emptyMap(),
109117
)
110118

111119
/**
@@ -272,7 +280,8 @@ public interface PowerSyncDatabase : Queries {
272280
val logger = generateLogger(logger)
273281
// Since this returns a fresh in-memory database every time, use a fresh group to avoid warnings about the
274282
// same database being opened multiple times.
275-
val collection = ActiveDatabaseGroup.GroupsCollection().referenceDatabase(logger, "test")
283+
val collection =
284+
ActiveDatabaseGroup.GroupsCollection().referenceDatabase(logger, "test")
276285

277286
return openedWithGroup(
278287
SingleConnectionPool(factory.openInMemoryConnection()),

common/src/commonMain/kotlin/com/powersync/bucket/BucketStorage.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ internal sealed interface PowerSyncControlArguments {
7171
val includeDefaults: Boolean,
7272
@SerialName("active_streams")
7373
val activeStreams: List<StreamKey>,
74+
@SerialName("app_metadata")
75+
val appMetadata: Map<String, String>,
7476
) : PowerSyncControlArguments {
7577
override val sqlArguments: Pair<String, Any?>
7678
get() = "start" to JsonUtil.json.encodeToString(this)
@@ -109,7 +111,8 @@ internal sealed interface PowerSyncControlArguments {
109111
class UpdateSubscriptions(
110112
activeStreams: List<StreamKey>,
111113
) : PowerSyncControlArguments {
112-
override val sqlArguments: Pair<String, Any?> = "update_subscriptions" to JsonUtil.json.encodeToString(activeStreams)
114+
override val sqlArguments: Pair<String, Any?> =
115+
"update_subscriptions" to JsonUtil.json.encodeToString(activeStreams)
113116
}
114117
}
115118

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ internal class PowerSyncDatabaseImpl(
142142
retryDelayMs: Long,
143143
params: Map<String, JsonParam?>,
144144
options: SyncOptions,
145+
appMetadata: Map<String, String>,
145146
) {
146147
waitReady()
147148
mutex.withLock {
@@ -159,6 +160,7 @@ internal class PowerSyncDatabaseImpl(
159160
options = options,
160161
schema = schema,
161162
activeSubscriptions = streams.currentlyReferencedStreams,
163+
appMetadata = appMetadata,
162164
)
163165
}
164166
}

common/src/commonMain/kotlin/com/powersync/sync/StreamingSync.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import kotlinx.io.readByteArray
5959
import kotlinx.io.readIntLe
6060
import kotlinx.serialization.json.JsonElement
6161
import kotlinx.serialization.json.JsonObject
62+
import kotlinx.serialization.json.JsonPrimitive
6263
import kotlinx.serialization.json.encodeToJsonElement
6364
import kotlin.time.Clock
6465

@@ -74,6 +75,7 @@ internal class StreamingSyncClient(
7475
private val options: SyncOptions,
7576
private val schema: Schema,
7677
private val activeSubscriptions: StateFlow<List<SubscriptionGroup>>,
78+
private val appMetadata: Map<String, String> = emptyMap(),
7779
) {
7880
private var isUploadingCrud = AtomicReference<PendingCrudUpload?>(null)
7981
private var completedCrudUploads = Channel<Unit>(onBufferOverflow = BufferOverflow.DROP_OLDEST)
@@ -92,6 +94,7 @@ internal class StreamingSyncClient(
9294
configureSyncHttpClient(options.userAgent)
9395
config.block(this)
9496
}
97+
9598
is SyncClientConfiguration.ExistingClient -> config.client
9699
}
97100

@@ -127,7 +130,13 @@ internal class StreamingSyncClient(
127130
status.update { copy(downloadError = e) }
128131
} finally {
129132
if (!result.hideDisconnectStateAndReconnectImmediately) {
130-
status.update { copy(connected = false, connecting = true, downloading = false) }
133+
status.update {
134+
copy(
135+
connected = false,
136+
connecting = true,
137+
downloading = false,
138+
)
139+
}
131140
delay(retryDelayMs)
132141
}
133142
}
@@ -297,7 +306,8 @@ internal class StreamingSyncClient(
297306
} else {
298307
// Use RSocket as a fallback to ensure we have backpressure on platforms that don't support it natively.
299308
flow {
300-
val credentials = requireNotNull(connector.getCredentialsCached()) { "Not logged in" }
309+
val credentials =
310+
requireNotNull(connector.getCredentialsCached()) { "Not logged in" }
301311

302312
emitAll(
303313
httpClient.rSocketSyncStream(
@@ -367,6 +377,7 @@ internal class StreamingSyncClient(
367377
schema = schema.toSerializable(),
368378
includeDefaults = options.includeDefaultStreams,
369379
activeStreams = subscriptions.map { it.key },
380+
appMetadata = appMetadata,
370381
),
371382
)
372383

@@ -375,7 +386,9 @@ internal class StreamingSyncClient(
375386
activeSubscriptions.collect {
376387
if (subscriptions !== it) {
377388
subscriptions = it
378-
controlInvocations.send(PowerSyncControlArguments.UpdateSubscriptions(activeSubscriptions.value.map { it.key }))
389+
controlInvocations.send(
390+
PowerSyncControlArguments.UpdateSubscriptions(activeSubscriptions.value.map { it.key }),
391+
)
379392
}
380393
}
381394
}
@@ -536,6 +549,7 @@ internal class StreamingSyncClient(
536549
},
537550
clientId = clientId!!,
538551
parameters = params,
552+
appMetadata = appMetadata,
539553
)
540554

541555
lateinit var receiveLines: Job

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ internal data class StreamingSyncRequest(
1212
@SerialName("include_checksum") val includeChecksum: Boolean = true,
1313
@SerialName("client_id") val clientId: String,
1414
val parameters: JsonObject = JsonObject(mapOf()),
15+
@SerialName("app_metadata") val appMetadata: Map<String, String> = emptyMap(),
1516
) {
1617
@SerialName("raw_data")
1718
private val rawData: Boolean = true

demos/supabase-todolist/shared/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,8 @@ buildkonfig {
123123
stringConfigField("POWERSYNC_URL")
124124
stringConfigField("SUPABASE_URL")
125125
stringConfigField("SUPABASE_ANON_KEY")
126+
127+
// App version from Gradle project version
128+
buildConfigField(STRING, "APP_VERSION", "\"${project.version}\"")
126129
}
127130
}

demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/Auth.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ internal class AuthViewModel(
6969
}
7070
},
7171
),
72+
appMetadata = mapOf(
73+
"appVersion" to Config.APP_VERSION
74+
),
7275
)
7376
}
7477

0 commit comments

Comments
 (0)