Skip to content

Commit 36cd237

Browse files
rygineinsipx
andauthored
Add consent & user preference streaming to WASM bindings (#1647)
* Add consent streaming * consent & preference streaming * improve browser integration test --------- Co-authored-by: Andrew Plaza <[email protected]>
1 parent 8d0ae0c commit 36cd237

File tree

15 files changed

+329
-61
lines changed

15 files changed

+329
-61
lines changed

.cargo/nextest.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[profile.default]
22
default-filter = "not test(test_stream_all_messages_does_not_lose_messages)"
3-
retries = 3
3+
retries = 1

Cargo.lock

Lines changed: 46 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings_wasm/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ js-sys.workspace = true
1414
prost.workspace = true
1515
serde.workspace = true
1616
serde-wasm-bindgen = "0.6.5"
17+
serde_bytes = "0.11"
1718
tracing.workspace = true
1819
tracing-subscriber = { workspace = true, features = ["env-filter", "json"] }
1920
tracing-web = "0.1"
21+
tsify-next = { version = "0.5", default-features = false, features = ["js"] }
2022
wasm-bindgen.workspace = true
2123
wasm-bindgen-futures.workspace = true
2224
xmtp_api.workspace = true
@@ -27,7 +29,6 @@ xmtp_cryptography.workspace = true
2729
xmtp_id.workspace = true
2830
xmtp_mls = { workspace = true, features = ["test-utils", "http-api"] }
2931
xmtp_proto = { workspace = true, features = ["proto_full"] }
30-
3132
[dev-dependencies]
3233
wasm-bindgen-test.workspace = true
3334
xmtp_common = { workspace = true, features = ["test-utils"] }

bindings_wasm/src/consent_state.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use serde::{Deserialize, Serialize};
12
use wasm_bindgen::{prelude::wasm_bindgen, JsError};
23
use xmtp_mls::storage::consent_record::{
34
ConsentState as XmtpConsentState, ConsentType as XmtpConsentType, StoredConsentRecord,
@@ -6,7 +7,7 @@ use xmtp_mls::storage::consent_record::{
67
use crate::{client::Client, conversation::Conversation};
78

89
#[wasm_bindgen]
9-
#[derive(Clone, serde::Serialize)]
10+
#[derive(Clone, Serialize, Deserialize)]
1011
pub enum ConsentState {
1112
Unknown,
1213
Allowed,
@@ -34,11 +35,12 @@ impl From<ConsentState> for XmtpConsentState {
3435
}
3536

3637
#[wasm_bindgen]
37-
#[derive(Clone)]
38+
#[derive(Copy, Clone, Serialize, Deserialize)]
39+
#[repr(u16)]
3840
pub enum ConsentEntityType {
39-
GroupId,
40-
InboxId,
41-
Address,
41+
GroupId = 0,
42+
InboxId = 1,
43+
Address = 2,
4244
}
4345

4446
impl From<ConsentEntityType> for XmtpConsentType {
@@ -51,9 +53,19 @@ impl From<ConsentEntityType> for XmtpConsentType {
5153
}
5254
}
5355

56+
fn entity_to_u16<S>(consent_entity_type: &ConsentEntityType, s: S) -> Result<S::Ok, S::Error>
57+
where
58+
S: serde::Serializer,
59+
{
60+
let num: u16 = (*consent_entity_type) as u16;
61+
s.serialize_u16(num)
62+
}
63+
5464
#[wasm_bindgen(getter_with_clone)]
65+
#[derive(Clone, Serialize, Deserialize)]
5566
pub struct Consent {
5667
#[wasm_bindgen(js_name = entityType)]
68+
#[serde(rename = "entityType", serialize_with = "entity_to_u16")]
5769
pub entity_type: ConsentEntityType,
5870
pub state: ConsentState,
5971
pub entity: String,
@@ -81,6 +93,20 @@ impl From<Consent> for StoredConsentRecord {
8193
}
8294
}
8395

96+
impl From<StoredConsentRecord> for Consent {
97+
fn from(value: StoredConsentRecord) -> Self {
98+
Self {
99+
entity: value.entity,
100+
entity_type: match value.entity_type {
101+
XmtpConsentType::Address => ConsentEntityType::Address,
102+
XmtpConsentType::ConversationId => ConsentEntityType::GroupId,
103+
XmtpConsentType::InboxId => ConsentEntityType::InboxId,
104+
},
105+
state: value.state.into(),
106+
}
107+
}
108+
}
109+
84110
#[wasm_bindgen]
85111
impl Client {
86112
#[wasm_bindgen(js_name = setConsentStates)]

bindings_wasm/src/conversations.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22
use std::sync::Arc;
33
use wasm_bindgen::prelude::wasm_bindgen;
4+
use wasm_bindgen::UnwrapThrowExt;
45
use wasm_bindgen::{JsError, JsValue};
56
use xmtp_mls::groups::{
67
DMMetadataOptions, GroupMetadataOptions, HmacKey as XmtpHmacKey, PreconfiguredPolicies,
@@ -10,10 +11,11 @@ use xmtp_mls::storage::group::ConversationType as XmtpConversationType;
1011
use xmtp_mls::storage::group::GroupMembershipState as XmtpGroupMembershipState;
1112
use xmtp_mls::storage::group::GroupQueryArgs;
1213

13-
use crate::consent_state::ConsentState;
14+
use crate::consent_state::{Consent, ConsentState};
1415
use crate::messages::Message;
1516
use crate::permissions::{GroupPermissionsOptions, PermissionPolicySet};
1617
use crate::streams::{StreamCallback, StreamCloser};
18+
use crate::user_preferences::UserPreference;
1719
use crate::{client::RustXmtpClient, conversation::Conversation};
1820

1921
use xmtp_mls::groups::group_mutable_metadata::MessageDisappearingSettings as XmtpMessageDisappearingSettings;
@@ -660,4 +662,34 @@ impl Conversations {
660662
);
661663
Ok(StreamCloser::new(stream_closer))
662664
}
665+
666+
#[wasm_bindgen(js_name = "streamConsent")]
667+
pub fn stream_consent(&self, callback: StreamCallback) -> Result<StreamCloser, JsError> {
668+
let stream_closer =
669+
RustXmtpClient::stream_consent_with_callback(self.inner_client.clone(), move |message| {
670+
match message {
671+
Ok(m) => {
672+
let array = m.into_iter().map(Consent::from).collect::<Vec<Consent>>();
673+
let value = serde_wasm_bindgen::to_value(&array).unwrap_throw();
674+
callback.on_consent_update(value)
675+
}
676+
Err(e) => callback.on_error(JsError::from(e)),
677+
}
678+
});
679+
Ok(StreamCloser::new(stream_closer))
680+
}
681+
682+
#[wasm_bindgen(js_name = "streamPreferences")]
683+
pub fn stream_preferences(&self, callback: StreamCallback) -> Result<StreamCloser, JsError> {
684+
let stream_closer =
685+
RustXmtpClient::stream_preferences_with_callback(self.inner_client.clone(), move |message| {
686+
match message {
687+
Ok(m) => {
688+
callback.on_user_preference_update(m.into_iter().map(UserPreference::from).collect())
689+
}
690+
Err(e) => callback.on_error(JsError::from(e)),
691+
}
692+
});
693+
Ok(StreamCloser::new(stream_closer))
694+
}
663695
}

bindings_wasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod messages;
1010
pub mod permissions;
1111
pub mod signatures;
1212
pub mod streams;
13+
mod user_preferences;
1314

1415
fn error(e: impl std::error::Error) -> JsError {
1516
JsError::new(&format!("{}", e))

bindings_wasm/src/streams.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::conversation::Conversation;
22
use crate::messages::Message;
3+
use crate::user_preferences::UserPreference;
34
use std::{cell::RefCell, rc::Rc};
45
use wasm_bindgen::prelude::*;
56
use wasm_bindgen::JsError;
@@ -18,6 +19,12 @@ extern "C" {
1819
#[wasm_bindgen(structural, method)]
1920
pub fn on_message(this: &StreamCallback, item: Message);
2021

22+
#[wasm_bindgen(structural, method)]
23+
pub fn on_consent_update(this: &StreamCallback, item: JsValue);
24+
25+
#[wasm_bindgen(structural, method)]
26+
pub fn on_user_preference_update(this: &StreamCallback, item: Vec<UserPreference>);
27+
2128
#[wasm_bindgen(structural, method)]
2229
pub fn on_conversation(this: &StreamCallback, item: Conversation);
2330

bindings_wasm/src/user_preferences.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use crate::consent_state::Consent;
2+
use serde::{Deserialize, Serialize};
3+
use tsify_next::Tsify;
4+
use wasm_bindgen::prelude::wasm_bindgen;
5+
use xmtp_mls::groups::device_sync::preference_sync::UserPreferenceUpdate;
6+
7+
#[derive(Tsify, Serialize, Deserialize)]
8+
#[tsify(into_wasm_abi, from_wasm_abi)]
9+
#[serde(tag = "type")]
10+
pub enum UserPreference {
11+
Consent {
12+
consent: Consent,
13+
},
14+
HmacKeyUpdate {
15+
// serde bytes converts to Uint8Array
16+
#[serde(with = "serde_bytes")]
17+
key: Vec<u8>,
18+
},
19+
}
20+
21+
impl From<UserPreferenceUpdate> for UserPreference {
22+
fn from(v: UserPreferenceUpdate) -> UserPreference {
23+
match v {
24+
UserPreferenceUpdate::ConsentUpdate(c) => UserPreference::Consent {
25+
consent: Consent::from(c),
26+
},
27+
UserPreferenceUpdate::HmacKeyUpdate { key } => UserPreference::HmacKeyUpdate { key },
28+
}
29+
}
30+
}

dev/test/browser-sdk

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,63 @@
11
#!/bin/bash
22
set -eou pipefail
33

4-
TMP=$(mktemp -d 2>/dev/null || mktemp -d -t 'test-browser-tmp')
4+
# USAGE
5+
# ./dev/test/browser-sdk will clone browser sdk to a tmp directory and execute the tests
6+
# ./dev/test/browser-sdk MY_DIR will clone to MY_DIR and execute the tests
7+
# ./dev/test/browser-sdk MY_DIR my_branch will clone to MY_DIR checkout MY_BRANCH and excute tests
8+
9+
function tmp() {
10+
mktemp -d 2>/dev/null || mktemp -d -t 'test-browser-tmp'
11+
}
12+
13+
TMP=$(tmp)
14+
DIR="${1:-$TMP}"
15+
BRANCH="${2:-main}"
516
CARGO="nix develop -i.#wasmBuild --command cargo"
617
WORKSPACE_MANIFEST="$($CARGO locate-project --workspace --message-format=plain)"
718
WORKSPACE_PATH="$(dirname $WORKSPACE_MANIFEST)"
819
PLAYWRIGHT_VERSION=$(nix develop .#js --command bash -c 'echo $PLAYWRIGHT_VERSION')
920
YARN="nix develop -i ${WORKSPACE_PATH}#js --command yarn"
1021
22+
trap ctrl_c INT
23+
function ctrl_c() {
24+
if [ -d $TMP ]; then
25+
rm -rf $TMP
26+
fi
27+
}
28+
1129
function run() {
12-
git clone [email protected]:xmtp/xmtp-js.git $TMP/xmtp-js
30+
cd $WORKSPACE_PATH
31+
nix build .#bindings_wasm --no-substitute --refresh
32+
33+
if [ ! -d $DIR/xmtp-js ]; then
34+
git clone [email protected]:xmtp/xmtp-js.git $DIR/xmtp-js
35+
fi
36+
37+
cd $DIR/xmtp-js
38+
git checkout $BRANCH
39+
40+
rm -rf node_modules/@xmtp/wasm-bindings
1341
14-
cd $TMP/xmtp-js
1542
$YARN install
43+
rm -rf $DIR/xmtp-js/node_modules/@xmtp/wasm-bindings/dist
44+
cp --no-preserve=mode,ownership -r $WORKSPACE_PATH/result/dist $DIR/xmtp-js/node_modules/@xmtp/wasm-bindings/
45+
1646
$YARN build
47+
# set playwright to same version as nix (otherwise will not work)
1748
$YARN set resolution "playwright@npm:^1.49.1" npm:$PLAYWRIGHT_VERSION
1849
$YARN set resolution "playwright-core@npm:^1.49.1" npm:$PLAYWRIGHT_VERSION
1950
2051
cd $WORKSPACE_PATH
21-
22-
nix build .#bindings_wasm
23-
24-
rm -rf $TMP/xmtp-js/node_modules/@xmtp/wasm-bindings/dist
25-
cp -r result/dist $TMP/xmtp-js/node_modules/@xmtp/wasm-bindings
26-
27-
$YARN --cwd $TMP/xmtp-js/sdks/browser-sdk test
52+
$YARN --cwd $DIR/xmtp-js/sdks/browser-sdk test test/Conversations.test.ts
2853
2954
return 0
3055
}
3156
3257
run
3358
RUN_STATUS=$?
3459
35-
rm -rf $TMP
60+
ctrl_c
3661
3762
# Check if run was successful
3863
if [ $RUN_STATUS -eq 0 ]; then

0 commit comments

Comments
 (0)