Skip to content

Commit 45c85a1

Browse files
feat: add app_metadata to rust sync requests
1 parent c567eb3 commit 45c85a1

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

crates/core/src/sync/interface.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::sync::storage_adapter::StorageAdapter;
1212
use crate::sync::subscriptions::{StreamKey, apply_subscriptions};
1313
use alloc::borrow::Cow;
1414
use alloc::boxed::Box;
15+
use alloc::collections::btree_map::BTreeMap;
1516
use alloc::rc::Rc;
1617
use alloc::{string::String, vec::Vec};
1718
use powersync_sqlite_nostd::bindings::SQLITE_RESULT_SUBTYPE;
@@ -40,6 +41,19 @@ pub struct StartSyncStream {
4041
/// We will increase the expiry date for those streams at the time we connect and disconnect.
4142
#[serde(default)]
4243
pub active_streams: Rc<Vec<StreamKey>>,
44+
/// Application metadata to include in the request when opening a sync stream.
45+
///
46+
/// This should only contain a JSON map of strings.
47+
///
48+
/// We use `BTreeMap<String, String>` instead of `serde_json::Map<String, serde_json::Value>`
49+
/// (like `parameters` uses) because:
50+
/// 1. It enforces type safety at compile time - values must be strings, not arbitrary JSON
51+
/// 2. It requires no runtime validation to ensure values are strings
52+
/// 3. It serializes to the same JSON format (a map with string values)
53+
/// 4. `serde_json::Map<String, String>` doesn't implement `Serialize`/`Deserialize` - the
54+
/// `serde_json::Map` type only supports `serde_json::Value` as the value type, not `String`
55+
#[serde(default)]
56+
pub app_metadata: Option<BTreeMap<String, String>>,
4357
}
4458

4559
impl StartSyncStream {
@@ -55,6 +69,7 @@ impl Default for StartSyncStream {
5569
schema: Default::default(),
5670
include_defaults: Self::include_defaults_by_default(),
5771
active_streams: Default::default(),
72+
app_metadata: Default::default(),
5873
}
5974
}
6075
}
@@ -159,6 +174,8 @@ pub struct StreamingSyncRequest {
159174
pub client_id: String,
160175
pub parameters: Option<serde_json::Map<String, serde_json::Value>>,
161176
pub streams: Rc<StreamSubscriptionRequest>,
177+
#[serde(skip_serializing_if = "Option::is_none")]
178+
pub app_metadata: Option<BTreeMap<String, String>>,
162179
}
163180

164181
#[derive(Debug, Serialize, PartialEq)]

crates/core/src/sync/streaming_sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ impl StreamingSyncIteration {
886886
client_id: client_id(self.db)?,
887887
parameters: self.options.parameters.take(),
888888
streams: stream_subscriptions.request.clone(),
889+
app_metadata: self.options.app_metadata.take(),
889890
};
890891

891892
event

dart/test/sync_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,28 @@ void _syncTests<T>({
179179
});
180180
});
181181

182+
syncTest('app_metadata is passed to EstablishSyncStream request', (_) {
183+
final startInstructions = invokeControlRaw(
184+
'start',
185+
json.encode({
186+
'app_metadata': {'key1': 'value1', 'key2': 'value2'}
187+
}),
188+
);
189+
190+
expect(
191+
startInstructions,
192+
contains(
193+
containsPair(
194+
'EstablishSyncStream',
195+
containsPair(
196+
'request',
197+
containsPair('app_metadata', {'key1': 'value1', 'key2': 'value2'}),
198+
),
199+
),
200+
),
201+
);
202+
});
203+
182204
test('handles connection events', () {
183205
invokeControl('start', null);
184206
expect(invokeControl('connection', 'established'), [

0 commit comments

Comments
 (0)