From c60eaddec2696283e52d65f1ce32cbfcf555bc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabr=C3=ADcio=20Bracht?= Date: Thu, 15 Oct 2026 13:57:59 -0700 Subject: [PATCH] update mqtt5 to 0.48.1 and mqdb-agent to 0.8.37, resume sessions and fix native projection --- .github/workflows/ci.yml | 2 +- Cargo.lock | 219 ++++-------------------- crates/mqp2p/Cargo.toml | 6 +- crates/stitch-harness/Cargo.toml | 6 +- crates/stitch-wasm/CHANGELOG.md | 6 + crates/stitch-wasm/Cargo.toml | 2 +- crates/stitch/CHANGELOG.md | 13 ++ crates/stitch/Cargo.toml | 14 +- crates/stitch/src/backend/native.rs | 4 +- crates/stitch/src/mqtt_client/native.rs | 14 +- crates/stitch/src/mqtt_client/wasm.rs | 22 +-- crates/stitch/tests/broker_harness.rs | 28 +++ crates/stitch/tests/persistence.rs | 36 ++++ 13 files changed, 156 insertions(+), 216 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d72f84e..2b3ddfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: targets: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 - run: cargo clippy --workspace --all-targets -- -D warnings - - run: cargo test -p stitch-sync --lib --test memory_store --test offline_queue --test persistence + - run: cargo test -p stitch-sync -p stitch-harness - run: cargo check -p stitch-sync --target wasm32-unknown-unknown - run: cargo clippy -p stitch-wasm --target wasm32-unknown-unknown --all-targets -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index bd100a5..6b3566a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -615,7 +615,7 @@ checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" dependencies = [ "curve25519-dalek", "ed25519", - "sha2 0.11.0", + "sha2", "subtle", ] @@ -830,11 +830,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1363,9 +1361,9 @@ dependencies = [ [[package]] name = "mqdb-agent" -version = "0.8.10" +version = "0.8.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4067cf38b8c59d79024687bd2e18eee9f22e35a6eefd7415ef4f0df3026eb69b" +checksum = "3d55479519f523418762245c8480c3f9c90dce5c5fe8ab08d2bf24356f43617f" dependencies = [ "arc-swap", "comfy-table", @@ -1374,7 +1372,7 @@ dependencies = [ "glob", "lru", "mqdb-core", - "mqtt5 0.31.4", + "mqtt5", "regex", "ring", "serde", @@ -1389,9 +1387,9 @@ dependencies = [ [[package]] name = "mqdb-core" -version = "0.7.3" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6878281d8ac6039cebb9a6e97078b64799571d10bed6f994649cf9f461027" +checksum = "7f1868ce47980e19cab42326692a40a8e517e7fe4021c91a2e510c77798439a2" dependencies = [ "arc-swap", "bebytes", @@ -1412,9 +1410,9 @@ dependencies = [ [[package]] name = "mqdb-wasm" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cb94ee438c45a11fc28dbeb5686eec110d237cb12af02d654bab3019a3f26d" +checksum = "311d162dce884fd5df7fc436935bde1c44bea7ba46139921a66a67b8a752ab05" dependencies = [ "console_error_panic_hook", "futures-channel", @@ -1436,7 +1434,7 @@ version = "0.3.0" dependencies = [ "clap", "flume", - "mqtt5 0.38.2", + "mqtt5", "quinn", "rand 0.10.2", "rcgen", @@ -1455,53 +1453,9 @@ dependencies = [ [[package]] name = "mqtt5" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cdc6fd7da80a9f1f7d09d111384f2dffb90c0e35a45fd2bcc7c637ea3146e4" -dependencies = [ - "argon2", - "base64", - "bebytes", - "bytes", - "flume", - "futures", - "futures-util", - "getrandom 0.3.4", - "hex", - "http", - "http-body-util", - "humantime-serde", - "hyper", - "hyper-rustls", - "hyper-util", - "mqtt5-protocol 0.12.0", - "parking_lot", - "quinn", - "rand 0.9.5", - "regex", - "ring", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "sha2 0.10.9", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-tungstenite 0.28.0", - "toml 0.9.12+spec-1.1.0", - "tracing", - "url", - "webpki-roots", - "zeroize", -] - -[[package]] -name = "mqtt5" -version = "0.38.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e969d1327b7ce3ce6a1f19fbcef90f0ddb1dad35c228381d9929c26c84b86aca" +checksum = "6451bf85e391b7938bf8834977251b1fcdf4202b741228fda28c9dfdef3839fe" dependencies = [ "argon2", "base64", @@ -1518,22 +1472,23 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "mqtt5-protocol 0.14.3", + "mqtt5-protocol", "parking_lot", "quinn", "rand 0.10.2", "regex", "ring", "rustls", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", - "sha2 0.11.0", + "sha2", "thiserror", "tokio", "tokio-rustls", - "tokio-tungstenite 0.29.0", - "toml 1.1.4+spec-1.1.0", + "tokio-tungstenite", + "toml", "tracing", "url", "webpki-roots", @@ -1542,26 +1497,9 @@ dependencies = [ [[package]] name = "mqtt5-protocol" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2673a581b938357355f8c7854ea056e6c0179ea943b75f8247971e8204091a" -dependencies = [ - "bebytes", - "bytes", - "hashbrown 0.16.1", - "portable-atomic", - "portable-atomic-util", - "serde", - "thiserror", - "tracing", - "web-time", -] - -[[package]] -name = "mqtt5-protocol" -version = "0.14.3" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ea23fbb2ab11fb239d77da902e0f71ebac91de86618b8ea332264f6f697f17" +checksum = "7127c4e0f1719483c3606ce51e0b27d036c4a22b985bef7ad375d89c34d5d145" dependencies = [ "bebytes", "bytes", @@ -1576,9 +1514,9 @@ dependencies = [ [[package]] name = "mqtt5-wasm" -version = "1.4.4" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d34a8551ad9c916fabdf324e108601a605780eea7bb563b9bc527786e0873cf" +checksum = "4a38a367859511af2cbf08b81a1df62a97ecd4c4ad5959a9c20b102316e23556" dependencies = [ "bytes", "console_error_panic_hook", @@ -1588,8 +1526,8 @@ dependencies = [ "getrandom 0.4.3", "gloo-timers", "js-sys", - "mqtt5-protocol 0.14.3", - "sha2 0.11.0", + "mqtt5-protocol", + "sha2", "tracing", "wasm-bindgen", "wasm-bindgen-futures", @@ -2041,9 +1979,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.43" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "once_cell", "ring", @@ -2065,15 +2003,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.15.1" @@ -2113,9 +2042,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -2272,17 +2201,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - [[package]] name = "sha2" version = "0.11.0" @@ -2384,7 +2302,7 @@ version = "0.1.0" dependencies = [ "mqdb-agent", "mqdb-core", - "mqtt5 0.38.2", + "mqtt5", "tempfile", "tokio", ] @@ -2415,8 +2333,8 @@ dependencies = [ "mqdb-agent", "mqdb-core", "mqdb-wasm", - "mqtt5 0.38.2", - "mqtt5-protocol 0.14.3", + "mqtt5", + "mqtt5-protocol", "mqtt5-wasm", "serde", "serde_json", @@ -2638,22 +2556,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tungstenite 0.28.0", -] - [[package]] name = "tokio-tungstenite" version = "0.29.0" @@ -2667,22 +2569,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.29.0", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", + "tungstenite", ] [[package]] @@ -2694,19 +2581,10 @@ dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime 1.1.1+spec-1.1.0", + "toml_datetime", "toml_parser", "toml_writer", - "winnow 1.0.4", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", + "winnow", ] [[package]] @@ -2724,7 +2602,7 @@ version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.4", + "winnow", ] [[package]] @@ -2806,25 +2684,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.5", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.29.0" @@ -2891,12 +2750,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -3223,12 +3076,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - [[package]] name = "winnow" version = "1.0.4" diff --git a/crates/mqp2p/Cargo.toml b/crates/mqp2p/Cargo.toml index c350822..a4c2504 100644 --- a/crates/mqp2p/Cargo.toml +++ b/crates/mqp2p/Cargo.toml @@ -2,14 +2,14 @@ name = "mqp2p" version = "0.3.0" edition = "2024" -rust-version = "1.88" +rust-version = "1.89" license = "Apache-2.0" description = "Peer-to-peer transport (QUIC + NAT traversal) over an MQTT signaling channel." repository = "https://github.com/LabOverWire/stitch-rs" publish = false [dependencies] -mqtt5 = "0.38" +mqtt5 = "0.48.1" quinn = { version = "0.11", default-features = false, features = ["rustls-ring", "runtime-tokio"] } rustls = { version = "0.23", default-features = false, features = ["ring", "std"] } rustls-pki-types = { version = "1.14", features = ["std"] } @@ -28,4 +28,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } clap = { version = "4", features = ["derive"] } tempfile = "3" rand = "0.10.0" -mqtt5 = "0.38" +mqtt5 = "0.48.1" diff --git a/crates/stitch-harness/Cargo.toml b/crates/stitch-harness/Cargo.toml index fa022ad..09aaf11 100644 --- a/crates/stitch-harness/Cargo.toml +++ b/crates/stitch-harness/Cargo.toml @@ -8,8 +8,8 @@ description = "Test harness that spawns a configured in-process mqdb agent (brok publish = false [dependencies] -mqdb-agent = { version = "0.8.10", default-features = false } -mqdb-core = "0.7.3" -mqtt5 = "0.38" +mqdb-agent = { version = "0.8.37", default-features = false } +mqdb-core = "0.7.12" +mqtt5 = "0.48.1" tempfile = "3" tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "net"] } diff --git a/crates/stitch-wasm/CHANGELOG.md b/crates/stitch-wasm/CHANGELOG.md index 9444f50..2f069ee 100644 --- a/crates/stitch-wasm/CHANGELOG.md +++ b/crates/stitch-wasm/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to the `stitch-wasm` crate are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Built on stitch-sync with mqtt5-wasm 2.1.7. A store reloaded with the same client id now resumes its broker session instead of failing to connect; see the stitch-sync changelog. Minimum Rust version is 1.89. + ## [0.5.0] - 2026-08-29 ### Changed diff --git a/crates/stitch-wasm/Cargo.toml b/crates/stitch-wasm/Cargo.toml index 81e9220..685b36c 100644 --- a/crates/stitch-wasm/Cargo.toml +++ b/crates/stitch-wasm/Cargo.toml @@ -2,7 +2,7 @@ name = "stitch-wasm" version = "0.5.0" edition = "2024" -rust-version = "1.88" +rust-version = "1.89" license = "Apache-2.0" description = "Browser (wasm-bindgen) bindings for the stitch reactive state-sync store." authors = ["LabOverWire"] diff --git a/crates/stitch/CHANGELOG.md b/crates/stitch/CHANGELOG.md index 8141643..679ea69 100644 --- a/crates/stitch/CHANGELOG.md +++ b/crates/stitch/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to the `stitch-sync` crate are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- mqtt5 updated to 0.48.1 (native), mqtt5-protocol to 0.16.1 and mqtt5-wasm to 2.1.7 (wasm), mqdb-agent to 0.8.37 and mqdb-core to 0.7.12, so native builds carry a single mqtt5. mqdb-wasm is 0.3.6. +- A broker `DISCONNECT` with `NotAuthorized`, `BadUsernameOrPassword` or `BadAuthenticationMethod` now reports `ConnectionStatus::Error` with the auth-failure flag, as a failed re-authentication already did. mqtt5 0.39 started reporting broker disconnects with their reason code. +- Brokers on mqdb-agent 0.8.33 or later send a response only to a topic the requester may publish to. An ACL must grant write (readwrite) on the response prefix, `$DB/clients//#` by default; with read only, every request times out. + +### Fixed + +- A `Store` restarted with the same client id connects again. stitch keeps its MQTT session across restarts (`clean_start: false`, 3600 s session expiry), and since mqtt5 0.41 a new client that finds a stored session refuses the connection unless it asks to resume one. Both the native and the wasm client now ask to resume whenever `clean_start` is off. +- `Store::list` with a `projection` now returns only the projected fields on native. The projection was passed to mqdb's `list` as its relationship includes, so no projection was applied. + ## [0.5.0] - 2026-08-26 ### Fixed diff --git a/crates/stitch/Cargo.toml b/crates/stitch/Cargo.toml index 9d18190..1f78cca 100644 --- a/crates/stitch/Cargo.toml +++ b/crates/stitch/Cargo.toml @@ -31,19 +31,19 @@ tracing-subscriber = { version = "0.3.23", features = ["fmt", "env-filter"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] flume = "0.12.0" futures = "0.3.32" -mqdb-agent = { version = "0.8.10", default-features = false } -mqdb-core = "0.7.3" -mqtt5 = "0.38" +mqdb-agent = { version = "0.8.37", default-features = false } +mqdb-core = "0.7.12" +mqtt5 = "0.48.1" tokio = { version = "1.52.3", default-features = false, features = ["rt-multi-thread", "macros", "sync", "time", "io-util", "fs"] } [target.'cfg(target_arch = "wasm32")'.dependencies] futures = "0.3.32" gloo-timers = { version = "0.4.0", features = ["futures"] } js-sys = "0.3" -mqdb-core = { version = "0.7.3", default-features = false } -mqdb-wasm = "0.3.3" -mqtt5-protocol = "0.14.3" -mqtt5-wasm = "1.4.4" +mqdb-core = { version = "0.7.12", default-features = false } +mqdb-wasm = "0.3.6" +mqtt5-protocol = "0.16.1" +mqtt5-wasm = "2.1.7" tokio = { version = "1.52.3", default-features = false, features = ["sync"] } uuid = { version = "1.23.1", features = ["v4", "v7", "js"] } wasm-bindgen = "0.2" diff --git a/crates/stitch/src/backend/native.rs b/crates/stitch/src/backend/native.rs index 29ebbb9..85c09b0 100644 --- a/crates/stitch/src/backend/native.rs +++ b/crates/stitch/src/backend/native.rs @@ -122,8 +122,8 @@ impl Db for NativeDb { filters, sort, pagination, - projection, - None, + Vec::new(), + (!projection.is_empty()).then_some(projection), ) .await .map_err(|e| Error::mqdb(format!("list:{entity}"), e)) diff --git a/crates/stitch/src/mqtt_client/native.rs b/crates/stitch/src/mqtt_client/native.rs index 1438b37..d906afb 100644 --- a/crates/stitch/src/mqtt_client/native.rs +++ b/crates/stitch/src/mqtt_client/native.rs @@ -6,7 +6,8 @@ use crate::error::{Error, Result}; use crate::types::ConnectionStatus; use mqtt5::client::{ConnectionEvent, DisconnectReason, JwtAuthHandler}; use mqtt5::types::{ - ConnectOptions, Message, PublishOptions, PublishProperties, SubscribeOptions, WillMessage, + ConnectOptions, Message, PublishOptions, PublishProperties, ReasonCode, SubscribeOptions, + WillMessage, }; use mqtt5::{MqttClient, QoS}; use std::time::Duration; @@ -30,6 +31,7 @@ impl MqttClientApi for NativeMqttClient { async fn connect(&self, url: &str, args: ConnectArgs) -> Result { let mut options = ConnectOptions::new(&self.client_id) .with_clean_start(args.clean_start) + .with_resume_existing_session(!args.clean_start) .with_keep_alive(Duration::from_secs(args.keep_alive_secs)) .with_session_expiry_interval(args.session_expiry_secs); @@ -140,11 +142,17 @@ impl MqttClientApi for NativeMqttClient { ConnectionEvent::Connecting => (ConnectionStatus::Connecting, false), ConnectionEvent::Connected { .. } => (ConnectionStatus::Connected, false), ConnectionEvent::Disconnected { - reason: DisconnectReason::AuthFailure, + reason: + DisconnectReason::AuthFailure + | DisconnectReason::ServerDisconnect( + ReasonCode::NotAuthorized + | ReasonCode::BadUsernameOrPassword + | ReasonCode::BadAuthenticationMethod, + ), } => (ConnectionStatus::Error, true), - ConnectionEvent::Disconnected { .. } => (ConnectionStatus::Disconnected, false), ConnectionEvent::Reconnecting { .. } => (ConnectionStatus::Connecting, false), ConnectionEvent::ReconnectFailed { .. } => (ConnectionStatus::Error, false), + _ => (ConnectionStatus::Disconnected, false), }; handler(status, auth_failure); }) diff --git a/crates/stitch/src/mqtt_client/wasm.rs b/crates/stitch/src/mqtt_client/wasm.rs index e02dbc2..2523539 100644 --- a/crates/stitch/src/mqtt_client/wasm.rs +++ b/crates/stitch/src/mqtt_client/wasm.rs @@ -34,20 +34,21 @@ fn js_err(ctx: &str, e: &JsValue) -> Error { impl MqttClientApi for WasmMqttClientAdapter { async fn connect(&self, url: &str, args: ConnectArgs) -> Result { let mut opts = WasmConnectOptions::new(); - opts.set_cleanStart(args.clean_start); - opts.set_keepAlive(u16::try_from(args.keep_alive_secs).unwrap_or(u16::MAX)); - opts.set_sessionExpiryInterval(Some(args.session_expiry_secs)); + opts.set_clean_start(args.clean_start); + opts.set_resume_existing_session(!args.clean_start); + opts.set_keep_alive(u16::try_from(args.keep_alive_secs).unwrap_or(u16::MAX)); + opts.set_session_expiry_interval(Some(args.session_expiry_secs)); if let Some(will) = &args.will { let mut message = WasmWillMessage::new(will.topic.clone(), will.payload.clone()); message.set_qos(will.qos); message.set_retain(will.retain); - message.set_willDelayInterval(will.will_delay_interval_secs); - message.set_contentType(will.content_type.clone()); + message.set_will_delay_interval(will.will_delay_interval_secs); + message.set_content_type(will.content_type.clone()); opts.set_will(message); } if let Some(ticket) = &args.jwt_ticket { - opts.set_authenticationMethod(Some("JWT".into())); - opts.set_authenticationData(ticket.as_bytes()); + opts.set_authentication_method(Some("JWT".into())); + opts.set_authentication_data(ticket.as_bytes()); let on_challenge = Closure::::new(|| {}); self.client.on_auth_challenge( on_challenge @@ -89,14 +90,15 @@ impl MqttClientApi for WasmMqttClientAdapter { async fn publish(&self, topic: String, payload: Vec, args: PublishArgs) -> Result<()> { let mut opts = WasmPublishOptions::new(); opts.set_qos(QoS::AtLeastOnce as u8); - opts.set_responseTopic(Some(args.response_topic)); - opts.set_correlationData(&args.correlation_data); + opts.set_response_topic(Some(args.response_topic)); + opts.set_correlation_data(&args.correlation_data); for (key, value) in args.user_properties { - opts.addUserProperty(key, value); + opts.add_user_property(key, value); } self.client .publish_with_options(&topic, &payload, &opts) .await + .map(|_| ()) .map_err(|e| js_err("publish", &e)) } diff --git a/crates/stitch/tests/broker_harness.rs b/crates/stitch/tests/broker_harness.rs index abe33cd..4a67ec5 100644 --- a/crates/stitch/tests/broker_harness.rs +++ b/crates/stitch/tests/broker_harness.rs @@ -83,6 +83,34 @@ async fn authenticated_store_crud_round_trips_through_broker() { broker.shutdown().await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn restarted_store_with_the_same_client_id_resumes_its_session() { + init_tracing(); + let broker = BrokerHarness::new() + .scope("project", "projectId") + .user("app", "secret") + .allow("app", "$DB/#", Access::ReadWrite) + .start() + .await + .expect("start broker"); + + for run in 0..2 { + let store = Store::with_client_id( + fixture_config(), + authed_options(broker.tcp_url(), "app", "secret"), + "stable-client".into(), + ); + store.initialize().await.expect("initialize"); + assert!( + reaches_connected(&store).await, + "run {run}: a store reusing its client id must reconnect to its stored session" + ); + store.shutdown().await.expect("shutdown store"); + } + + broker.shutdown().await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn wrong_password_never_reaches_connected() { init_tracing(); diff --git a/crates/stitch/tests/persistence.rs b/crates/stitch/tests/persistence.rs index d8c44d2..4b1cb23 100644 --- a/crates/stitch/tests/persistence.rs +++ b/crates/stitch/tests/persistence.rs @@ -523,3 +523,39 @@ async fn update_accepts_null_optional_field_and_keeps_record() { "list_by_scope must still return the record after null update", ); } + +#[tokio::test] +async fn list_with_projection_returns_only_projected_fields() { + let dir = TempDir::new().unwrap(); + let layer = open_layer(&dir).await; + layer + .create( + "task", + make_record(&[ + ("id", json!("t1")), + ("title", json!("hidden")), + ("projectId", json!("p1")), + ]), + Origin::Local, + ) + .await + .unwrap(); + + let rows = layer + .list_with_projection( + "task", + Vec::new(), + Vec::new(), + None, + vec!["id".to_string(), "projectId".to_string()], + ) + .await + .unwrap(); + assert_eq!(rows.len(), 1); + assert_eq!(rows[0].get("projectId").and_then(Value::as_str), Some("p1")); + assert!( + rows[0].get("title").is_none(), + "a projection must drop unlisted fields: {:?}", + rows[0] + ); +}