From 83e7afab5d8a43fb8a863bb764540f9a38a68af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Wed, 31 May 2023 13:37:59 +0200 Subject: [PATCH] sdk: Allow to get stripped state events from the store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- .../src/deserialized_responses.rs | 15 +++- crates/matrix-sdk-base/src/rooms/members.rs | 9 ++- .../src/store/integration_tests.rs | 26 +++---- .../matrix-sdk-base/src/store/memory_store.rs | 74 ++++++++++--------- crates/matrix-sdk-base/src/store/traits.rs | 37 +++++----- .../src/state_store/mod.rs | 43 +++++++++-- .../src/state_store/migrations.rs | 7 +- crates/matrix-sdk-sled/src/state_store/mod.rs | 74 +++++++++++-------- crates/matrix-sdk-sqlite/src/state_store.rs | 66 +++++++++-------- crates/matrix-sdk/CHANGELOG.md | 2 + crates/matrix-sdk/src/room/common.rs | 43 ++++++----- .../tests/integration/room/common.rs | 5 +- .../src/tests/redaction.rs | 51 +++++++++---- 13 files changed, 279 insertions(+), 173 deletions(-) diff --git a/crates/matrix-sdk-base/src/deserialized_responses.rs b/crates/matrix-sdk-base/src/deserialized_responses.rs index f335eef1d..de51f94f1 100644 --- a/crates/matrix-sdk-base/src/deserialized_responses.rs +++ b/crates/matrix-sdk-base/src/deserialized_responses.rs @@ -19,7 +19,10 @@ use std::{collections::BTreeMap, fmt}; pub use matrix_sdk_common::deserialized_responses::*; use ruma::{ events::{ - room::member::{MembershipState, RoomMemberEvent, RoomMemberEventContent}, + room::{ + member::{MembershipState, RoomMemberEvent, RoomMemberEventContent}, + power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, + }, AnyStrippedStateEvent, AnySyncStateEvent, EventContentFromType, PossiblyRedactedStateEventContent, RedactContent, RedactedStateEventContent, StateEventContent, StaticStateEventContent, StrippedStateEvent, SyncStateEvent, @@ -265,3 +268,13 @@ impl MemberEvent { self.state_key() } } + +impl SyncOrStrippedState { + /// The power levels of the event. + pub fn power_levels(&self) -> RoomPowerLevels { + match self { + Self::Sync(e) => e.power_levels(), + Self::Stripped(e) => e.power_levels(), + } + } +} diff --git a/crates/matrix-sdk-base/src/rooms/members.rs b/crates/matrix-sdk-base/src/rooms/members.rs index 853417031..ab133f1c4 100644 --- a/crates/matrix-sdk-base/src/rooms/members.rs +++ b/crates/matrix-sdk-base/src/rooms/members.rs @@ -19,14 +19,17 @@ use ruma::{ presence::PresenceEvent, room::{ member::MembershipState, - power_levels::{PowerLevelAction, RoomPowerLevels, SyncRoomPowerLevelsEvent}, + power_levels::{PowerLevelAction, RoomPowerLevels, RoomPowerLevelsEventContent}, }, MessageLikeEventType, StateEventType, }, MxcUri, UserId, }; -use crate::{deserialized_responses::MemberEvent, MinimalRoomMemberEvent}; +use crate::{ + deserialized_responses::{MemberEvent, SyncOrStrippedState}, + MinimalRoomMemberEvent, +}; /// A member of a room. #[derive(Clone, Debug)] @@ -38,7 +41,7 @@ pub struct RoomMember { pub(crate) profile: Arc>, #[allow(dead_code)] pub(crate) presence: Arc>, - pub(crate) power_levels: Arc>, + pub(crate) power_levels: Arc>>, pub(crate) max_power_level: i64, pub(crate) is_room_creator: bool, pub(crate) display_name_ambiguous: bool, diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index b54ebbb2f..fbd40b25f 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -17,12 +17,12 @@ use ruma::{ SyncRoomMemberEvent, }, power_levels::RoomPowerLevelsEventContent, - topic::{OriginalRoomTopicEvent, RedactedRoomTopicEvent, RoomTopicEventContent}, + topic::RoomTopicEventContent, MediaSource, }, AnyEphemeralRoomEventContent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncStateEvent, - GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, + GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, SyncStateEvent, }, mxc_uri, room_id, serde::Raw, @@ -266,8 +266,12 @@ impl StateStoreIntegrationTests for DynStateStore { self.get_state_event_static::(room_id) .await? .expect("room topic found before redaction") - .deserialize_as::() + .deserialize() .expect("can deserialize room topic before redaction") + .as_sync() + .expect("room topic is a sync state event") + .as_original() + .expect("room topic is not redacted yet") .content .topic, "😀" @@ -283,23 +287,15 @@ impl StateStoreIntegrationTests for DynStateStore { changes.add_redaction(room_id, &redacted_event_id, redaction_evt); self.save_changes(&changes).await?; - match self - .get_state_event_static::(room_id) - .await? - .expect("room topic found before redaction") - .deserialize_as::() - { - Err(_) => {} // as expected - Ok(_) => panic!("Topic has not been redacted"), - } - - let _ = self + let redacted_event = self .get_state_event_static::(room_id) .await? .expect("room topic found after redaction") - .deserialize_as::() + .deserialize() .expect("can deserialize room topic after redaction"); + assert_matches!(redacted_event.as_sync(), Some(SyncStateEvent::Redacted(_))); + Ok(()) } diff --git a/crates/matrix-sdk-base/src/store/memory_store.rs b/crates/matrix-sdk-base/src/store/memory_store.rs index 02d99e41b..7b1e5cd1c 100644 --- a/crates/matrix-sdk-base/src/store/memory_store.rs +++ b/crates/matrix-sdk-base/src/store/memory_store.rs @@ -37,8 +37,9 @@ use tracing::{debug, warn}; use super::{Result, RoomInfo, StateChanges, StateStore, StoreError}; use crate::{ - deserialized_responses::RawMemberEvent, media::MediaRequest, MinimalRoomMemberEvent, - RoomMemberships, StateStoreDataKey, StateStoreDataValue, + deserialized_responses::{RawAnySyncOrStrippedState, RawMemberEvent}, + media::MediaRequest, + MinimalRoomMemberEvent, RoomMemberships, StateStoreDataKey, StateStoreDataValue, }; /// In-Memory, non-persistent implementation of the `StateStore` @@ -376,25 +377,48 @@ impl MemoryStore { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>> { - Ok(self + ) -> Result> { + if let Some(e) = self + .stripped_room_state + .get(room_id) + .as_ref() + .and_then(|events| events.get(&event_type)) + .and_then(|m| m.get(state_key).map(|m| m.clone())) + { + Ok(Some(RawAnySyncOrStrippedState::Stripped(e))) + } else if let Some(e) = self .room_state .get(room_id) - .and_then(|e| e.get(&event_type).and_then(|s| s.get(state_key).map(|e| e.clone())))) + .as_ref() + .and_then(|events| events.get(&event_type)) + .and_then(|m| m.get(state_key).map(|m| m.clone())) + { + Ok(Some(RawAnySyncOrStrippedState::Sync(e))) + } else { + Ok(None) + } } async fn get_state_events( &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>> { - Ok(self - .room_state - .get(room_id) - .and_then(|e| { - e.get(&event_type).map(|s| s.iter().map(|e| e.clone()).collect::>()) + ) -> Result> { + if let Some(v) = self.stripped_room_state.get(room_id).as_ref().and_then(|events| { + events.get(&event_type).map(|s| { + s.iter().map(|e| RawAnySyncOrStrippedState::Stripped(e.clone())).collect::>() }) - .unwrap_or_default()) + }) { + Ok(v) + } else if let Some(v) = self.room_state.get(room_id).as_ref().and_then(|events| { + events.get(&event_type).map(|s| { + s.iter().map(|e| RawAnySyncOrStrippedState::Sync(e.clone())).collect::>() + }) + }) { + Ok(v) + } else { + Ok(Vec::new()) + } } async fn get_profile( @@ -410,25 +434,9 @@ impl MemoryStore { room_id: &RoomId, state_key: &UserId, ) -> Result> { - if let Some(e) = self - .stripped_room_state - .get(room_id) - .as_ref() - .and_then(|events| events.get(&StateEventType::RoomMember)) - .and_then(|m| m.get(state_key.as_str()).map(|m| m.clone().cast())) - { - Ok(Some(RawMemberEvent::Stripped(e))) - } else if let Some(e) = self - .room_state - .get(room_id) - .as_ref() - .and_then(|events| events.get(&StateEventType::RoomMember)) - .and_then(|m| m.get(state_key.as_str()).map(|m| m.clone().cast())) - { - Ok(Some(RawMemberEvent::Sync(e))) - } else { - Ok(None) - } + self.get_state_event(room_id, StateEventType::RoomMember, state_key.as_str()) + .await + .map(|opt| opt.map(|raw| raw.cast())) } /// Get the user IDs for the given room with the given memberships and @@ -588,7 +596,7 @@ impl StateStore for MemoryStore { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>> { + ) -> Result> { self.get_state_event(room_id, event_type, state_key).await } @@ -596,7 +604,7 @@ impl StateStore for MemoryStore { &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>> { + ) -> Result> { self.get_state_events(room_id, event_type).await } diff --git a/crates/matrix-sdk-base/src/store/traits.rs b/crates/matrix-sdk-base/src/store/traits.rs index 668b1fc88..a11e74bb6 100644 --- a/crates/matrix-sdk-base/src/store/traits.rs +++ b/crates/matrix-sdk-base/src/store/traits.rs @@ -20,11 +20,10 @@ use ruma::{ events::{ presence::PresenceEvent, receipt::{Receipt, ReceiptThread, ReceiptType}, - AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent, EmptyStateKey, - GlobalAccountDataEvent, GlobalAccountDataEventContent, GlobalAccountDataEventType, - RedactContent, RedactedStateEventContent, RoomAccountDataEvent, - RoomAccountDataEventContent, RoomAccountDataEventType, StateEventType, StaticEventContent, - StaticStateEventContent, SyncStateEvent, + AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, EmptyStateKey, GlobalAccountDataEvent, + GlobalAccountDataEventContent, GlobalAccountDataEventType, RedactContent, + RedactedStateEventContent, RoomAccountDataEvent, RoomAccountDataEventContent, + RoomAccountDataEventType, StateEventType, StaticEventContent, StaticStateEventContent, }, serde::Raw, EventId, MxcUri, OwnedEventId, OwnedUserId, RoomId, UserId, @@ -32,8 +31,9 @@ use ruma::{ use super::{StateChanges, StoreError}; use crate::{ - deserialized_responses::RawMemberEvent, media::MediaRequest, MinimalRoomMemberEvent, RoomInfo, - RoomMemberships, + deserialized_responses::{RawAnySyncOrStrippedState, RawMemberEvent, RawSyncOrStrippedState}, + media::MediaRequest, + MinimalRoomMemberEvent, RoomInfo, RoomMemberships, }; /// An abstract state store trait that can be used to implement different stores @@ -102,7 +102,7 @@ pub trait StateStore: AsyncTraitDeps { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>, Self::Error>; + ) -> Result, Self::Error>; /// Get a list of state events for a given room and `StateEventType`. /// @@ -115,7 +115,7 @@ pub trait StateStore: AsyncTraitDeps { &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>, Self::Error>; + ) -> Result, Self::Error>; /// Get the current profile for the given user in the given room. /// @@ -370,7 +370,7 @@ impl StateStore for EraseStateStoreError { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>, Self::Error> { + ) -> Result, Self::Error> { self.0.get_state_event(room_id, event_type, state_key).await.map_err(Into::into) } @@ -378,7 +378,7 @@ impl StateStore for EraseStateStoreError { &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>, Self::Error> { + ) -> Result, Self::Error> { self.0.get_state_events(room_id, event_type).await.map_err(Into::into) } @@ -530,12 +530,12 @@ pub trait StateStoreExt: StateStore { async fn get_state_event_static( &self, room_id: &RoomId, - ) -> Result>>, Self::Error> + ) -> Result>, Self::Error> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::Redacted: RedactedStateEventContent, { - Ok(self.get_state_event(room_id, C::TYPE.into(), "").await?.map(Raw::cast)) + Ok(self.get_state_event(room_id, C::TYPE.into(), "").await?.map(|raw| raw.cast())) } /// Get a specific state event of statically-known type. @@ -547,14 +547,17 @@ pub trait StateStoreExt: StateStore { &self, room_id: &RoomId, state_key: &K, - ) -> Result>>, Self::Error> + ) -> Result>, Self::Error> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::StateKey: Borrow, C::Redacted: RedactedStateEventContent, K: AsRef + ?Sized + Sync, { - Ok(self.get_state_event(room_id, C::TYPE.into(), state_key.as_ref()).await?.map(Raw::cast)) + Ok(self + .get_state_event(room_id, C::TYPE.into(), state_key.as_ref()) + .await? + .map(|raw| raw.cast())) } /// Get a list of state events of a statically-known type for a given room. @@ -565,7 +568,7 @@ pub trait StateStoreExt: StateStore { async fn get_state_events_static( &self, room_id: &RoomId, - ) -> Result>>, Self::Error> + ) -> Result>, Self::Error> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::Redacted: RedactedStateEventContent, @@ -575,7 +578,7 @@ pub trait StateStoreExt: StateStore { .get_state_events(room_id, C::TYPE.into()) .await? .into_iter() - .map(Raw::cast) + .map(|raw| raw.cast()) .collect()) } diff --git a/crates/matrix-sdk-indexeddb/src/state_store/mod.rs b/crates/matrix-sdk-indexeddb/src/state_store/mod.rs index b32488676..b17453359 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store/mod.rs @@ -22,7 +22,7 @@ use async_trait::async_trait; use gloo_utils::format::JsValueSerdeExt; use indexed_db_futures::prelude::*; use matrix_sdk_base::{ - deserialized_responses::RawMemberEvent, + deserialized_responses::{RawAnySyncOrStrippedState, RawMemberEvent}, media::{MediaRequest, UniqueKey}, store::{StateChanges, StateStore, StoreError}, MinimalStateEvent, RoomInfo, RoomMemberships, StateStoreDataKey, StateStoreDataValue, @@ -820,21 +820,52 @@ impl_state_store! { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>> { - self.inner + ) -> Result> { + if let Some(e) = self + .inner + .transaction_on_one_with_mode(keys::STRIPPED_ROOM_STATE, IdbTransactionMode::Readonly)? + .object_store(keys::STRIPPED_ROOM_STATE)? + .get(&self.encode_key(keys::STRIPPED_ROOM_STATE, (room_id, &event_type, state_key)))? + .await? + .map(|f| self.deserialize_event(&f)) + .transpose()? + { + Ok(Some(RawAnySyncOrStrippedState::Stripped(e))) + } else if let Some(e) = self + .inner .transaction_on_one_with_mode(keys::ROOM_STATE, IdbTransactionMode::Readonly)? .object_store(keys::ROOM_STATE)? .get(&self.encode_key(keys::ROOM_STATE, (room_id, event_type, state_key)))? .await? .map(|f| self.deserialize_event(&f)) - .transpose() + .transpose()? + { + Ok(Some(RawAnySyncOrStrippedState::Sync(e))) + } else { + Ok(None) + } } async fn get_state_events( &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>> { + ) -> Result> { + let stripped_range = self.encode_to_range(keys::STRIPPED_ROOM_STATE, (room_id, &event_type))?; + let stripped_events = self + .inner + .transaction_on_one_with_mode(keys::STRIPPED_ROOM_STATE, IdbTransactionMode::Readonly)? + .object_store(keys::STRIPPED_ROOM_STATE)? + .get_all_with_key(&stripped_range)? + .await? + .iter() + .filter_map(|f| self.deserialize_event(&f).ok().map(RawAnySyncOrStrippedState::Stripped)) + .collect::>(); + + if !stripped_events.is_empty() { + return Ok(stripped_events) + } + let range = self.encode_to_range(keys::ROOM_STATE, (room_id, event_type))?; Ok(self .inner @@ -843,7 +874,7 @@ impl_state_store! { .get_all_with_key(&range)? .await? .iter() - .filter_map(|f| self.deserialize_event(&f).ok()) + .filter_map(|f| self.deserialize_event(&f).ok().map(RawAnySyncOrStrippedState::Sync)) .collect::>()) } diff --git a/crates/matrix-sdk-sled/src/state_store/migrations.rs b/crates/matrix-sdk-sled/src/state_store/migrations.rs index e9a99e485..938f7e91e 100644 --- a/crates/matrix-sdk-sled/src/state_store/migrations.rs +++ b/crates/matrix-sdk-sled/src/state_store/migrations.rs @@ -402,7 +402,10 @@ mod test { use matrix_sdk_test::{async_test, test_json}; use ruma::{ events::{ - room::member::{StrippedRoomMemberEvent, SyncRoomMemberEvent}, + room::{ + member::{StrippedRoomMemberEvent, SyncRoomMemberEvent}, + topic::RoomTopicEventContent, + }, AnySyncStateEvent, StateEventType, }, room_id, @@ -553,7 +556,7 @@ mod test { .unwrap(); let event = store.get_state_event(room_id, StateEventType::RoomTopic, "").await.unwrap().unwrap(); - event.deserialize().unwrap(); + event.cast::().deserialize().unwrap(); } #[async_test] diff --git a/crates/matrix-sdk-sled/src/state_store/mod.rs b/crates/matrix-sdk-sled/src/state_store/mod.rs index 8cb72ee5c..4509246f3 100644 --- a/crates/matrix-sdk-sled/src/state_store/mod.rs +++ b/crates/matrix-sdk-sled/src/state_store/mod.rs @@ -23,7 +23,7 @@ use async_trait::async_trait; use futures_core::stream::Stream; use futures_util::stream::{self, TryStreamExt}; use matrix_sdk_base::{ - deserialized_responses::RawMemberEvent, + deserialized_responses::{RawAnySyncOrStrippedState, RawMemberEvent}, media::{MediaRequest, UniqueKey}, store::{Result as StoreResult, StateChanges, StateStore, StoreError}, MinimalStateEvent, RoomInfo, RoomMemberships, StateStoreDataKey, StateStoreDataValue, @@ -787,11 +787,26 @@ impl SledStateStore { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>> { + ) -> Result> { let db = self.clone(); let key = self.encode_key(keys::ROOM_STATE, (room_id, event_type.to_string(), state_key)); + let stripped_key = self + .encode_key(keys::STRIPPED_ROOM_STATE, (room_id, event_type.to_string(), state_key)); spawn_blocking(move || { - db.room_state.get(key)?.map(|e| db.deserialize_value(&e)).transpose() + if let Some(e) = db + .stripped_room_state + .get(stripped_key)? + .map(|v| db.deserialize_value(&v)) + .transpose()? + { + Ok(Some(RawAnySyncOrStrippedState::Stripped(e))) + } else if let Some(e) = + db.room_state.get(key)?.map(|v| db.deserialize_value(&v)).transpose()? + { + Ok(Some(RawAnySyncOrStrippedState::Sync(e))) + } else { + Ok(None) + } }) .await? } @@ -800,13 +815,31 @@ impl SledStateStore { &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>> { + ) -> Result> { let db = self.clone(); let key = self.encode_key(keys::ROOM_STATE, (room_id, event_type.to_string())); + let stripped_key = + self.encode_key(keys::STRIPPED_ROOM_STATE, (room_id, event_type.to_string())); spawn_blocking(move || { + let stripped_events = db + .stripped_room_state + .scan_prefix(stripped_key) + .flat_map(|e| { + e.map(|(_, e)| { + db.deserialize_value(&e).map(RawAnySyncOrStrippedState::Stripped) + }) + }) + .collect::, _>>()?; + + if !stripped_events.is_empty() { + return Ok(stripped_events); + } + db.room_state .scan_prefix(key) - .flat_map(|e| e.map(|(_, e)| db.deserialize_value(&e))) + .flat_map(|e| { + e.map(|(_, e)| db.deserialize_value(&e).map(RawAnySyncOrStrippedState::Sync)) + }) .collect::>() }) .await? @@ -828,30 +861,9 @@ impl SledStateStore { room_id: &RoomId, state_key: &UserId, ) -> Result> { - let db = self.clone(); - let key = - self.encode_key(keys::ROOM_STATE, (room_id, StateEventType::RoomMember, state_key)); - let stripped_key = self.encode_key( - keys::STRIPPED_ROOM_STATE, - (room_id, StateEventType::RoomMember, state_key), - ); - spawn_blocking(move || { - if let Some(e) = db - .stripped_room_state - .get(stripped_key)? - .map(|v| db.deserialize_value(&v)) - .transpose()? - { - Ok(Some(RawMemberEvent::Stripped(e))) - } else if let Some(e) = - db.room_state.get(key)?.map(|v| db.deserialize_value(&v)).transpose()? - { - Ok(Some(RawMemberEvent::Sync(e))) - } else { - Ok(None) - } - }) - .await? + self.get_state_event(room_id, StateEventType::RoomMember, state_key.as_str()) + .await + .map(|opt| opt.map(|raw| raw.cast())) } /// Get the user IDs for the given room with the given memberships and @@ -1230,7 +1242,7 @@ impl StateStore for SledStateStore { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> StoreResult>> { + ) -> StoreResult> { self.get_state_event(room_id, event_type, state_key).await.map_err(Into::into) } @@ -1238,7 +1250,7 @@ impl StateStore for SledStateStore { &self, room_id: &RoomId, event_type: StateEventType, - ) -> StoreResult>> { + ) -> StoreResult> { self.get_state_events(room_id, event_type).await.map_err(Into::into) } diff --git a/crates/matrix-sdk-sqlite/src/state_store.rs b/crates/matrix-sdk-sqlite/src/state_store.rs index aa7cc5f16..485efcde6 100644 --- a/crates/matrix-sdk-sqlite/src/state_store.rs +++ b/crates/matrix-sdk-sqlite/src/state_store.rs @@ -9,7 +9,7 @@ use std::{ use async_trait::async_trait; use deadpool_sqlite::{Object as SqliteConn, Pool as SqlitePool, Runtime}; use matrix_sdk_base::{ - deserialized_responses::RawMemberEvent, + deserialized_responses::{RawAnySyncOrStrippedState, RawMemberEvent}, media::{MediaRequest, UniqueKey}, RoomInfo, RoomMemberships, RoomState, StateChanges, StateStore, StateStoreDataKey, StateStoreDataValue, @@ -547,24 +547,6 @@ trait SqliteObjectStateStoreExt: SqliteObjectExt { .await?) } - async fn get_state_event( - &self, - room_id: Key, - event_type: Key, - state_key: Key, - stripped: bool, - ) -> Result>> { - Ok(self - .query_row( - "SELECT data FROM state_event - WHERE room_id = ? AND event_type = ? AND state_key = ? AND stripped = ?", - (room_id, event_type, state_key, stripped), - |row| row.get(0), - ) - .await - .optional()?) - } - async fn get_maybe_stripped_state_event( &self, room_id: Key, @@ -582,12 +564,20 @@ trait SqliteObjectStateStoreExt: SqliteObjectExt { .optional()?) } - async fn get_state_events(&self, room_id: Key, event_type: Key) -> Result>> { + async fn get_maybe_stripped_state_events( + &self, + room_id: Key, + event_type: Key, + ) -> Result)>> { Ok(self .prepare( - "SELECT data FROM state_event - WHERE room_id = ? AND event_type = ? AND stripped = FALSE", - |mut stmt| stmt.query((room_id, event_type))?.mapped(|row| row.get(0)).collect(), + "SELECT stripped, data FROM state_event + WHERE room_id = ? AND event_type = ?", + |mut stmt| { + stmt.query((room_id, event_type))? + .mapped(|row| Ok((row.get(0)?, row.get(1)?))) + .collect() + }, ) .await?) } @@ -1080,15 +1070,23 @@ impl StateStore for SqliteStateStore { room_id: &RoomId, event_type: StateEventType, state_key: &str, - ) -> Result>> { + ) -> Result> { let room_id = self.encode_key(keys::STATE_EVENT, room_id); let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string()); let state_key = self.encode_key(keys::STATE_EVENT, state_key); self.acquire() .await? - .get_state_event(room_id, event_type, state_key, false) + .get_maybe_stripped_state_event(room_id, event_type, state_key) .await? - .map(|data| self.deserialize_json(&data)) + .map(|(stripped, data)| { + let ev = if stripped { + RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?) + } else { + RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?) + }; + + Ok(ev) + }) .transpose() } @@ -1096,15 +1094,23 @@ impl StateStore for SqliteStateStore { &self, room_id: &RoomId, event_type: StateEventType, - ) -> Result>> { + ) -> Result> { let room_id = self.encode_key(keys::STATE_EVENT, room_id); let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string()); self.acquire() .await? - .get_state_events(room_id, event_type) + .get_maybe_stripped_state_events(room_id, event_type) .await? - .iter() - .map(|data| self.deserialize_json(data)) + .into_iter() + .map(|(stripped, data)| { + let ev = if stripped { + RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?) + } else { + RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?) + }; + + Ok(ev) + }) .collect() } diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 40aa3d0f5..4d807ff4a 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -10,6 +10,8 @@ - `Common::active_members(_no_sync)` and `Common::joined_members(_no_sync)` are deprecated. - `matrix-sdk-sqlite` is the new default store implementation outside of WASM, behind the `sqlite` feature. - The `sled` feature was removed. It is still possible to use `matrix-sdk-sled` as a custom store. +- The `Common` methods to retrieve state events can now return a sync or stripped event, so it can be used + for invited rooms too. # 0.6.2 diff --git a/crates/matrix-sdk/src/room/common.rs b/crates/matrix-sdk/src/room/common.rs index 228df3d1b..65456dce0 100644 --- a/crates/matrix-sdk/src/room/common.rs +++ b/crates/matrix-sdk/src/room/common.rs @@ -1,7 +1,10 @@ use std::{borrow::Borrow, collections::BTreeMap, fmt, ops::Deref, sync::Arc}; use matrix_sdk_base::{ - deserialized_responses::{MembersResponse, TimelineEvent}, + deserialized_responses::{ + MembersResponse, RawAnySyncOrStrippedState, RawSyncOrStrippedState, SyncOrStrippedState, + TimelineEvent, + }, store::StateStoreExt, RoomMemberships, StateChanges, }; @@ -35,10 +38,9 @@ use ruma::{ MediaSource, }, tag::{TagInfo, TagName}, - AnyRoomAccountDataEvent, AnyStateEvent, AnySyncStateEvent, EmptyStateKey, RedactContent, + AnyRoomAccountDataEvent, AnyStateEvent, EmptyStateKey, RedactContent, RedactedStateEventContent, RoomAccountDataEvent, RoomAccountDataEventContent, RoomAccountDataEventType, StateEventType, StaticEventContent, StaticStateEventContent, - SyncStateEvent, }, push::{Action, PushConditionRoomCtx}, serde::Raw, @@ -539,7 +541,7 @@ impl Common { pub async fn get_state_events( &self, event_type: StateEventType, - ) -> Result>> { + ) -> Result> { self.client.store().get_state_events(self.room_id(), event_type).await.map_err(Into::into) } @@ -551,15 +553,15 @@ impl Common { /// # async { /// # let room: matrix_sdk::room::Common = todo!(); /// use matrix_sdk::ruma::{ - /// events::room::member::SyncRoomMemberEvent, serde::Raw, + /// events::room::member::RoomMemberEventContent, serde::Raw, /// }; /// - /// let room_members: Vec> = - /// room.get_state_events_static().await?; + /// let room_members = + /// room.get_state_events_static::().await?; /// # anyhow::Ok(()) /// # }; /// ``` - pub async fn get_state_events_static(&self) -> Result>>> + pub async fn get_state_events_static(&self) -> Result>> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::Redacted: RedactedStateEventContent, @@ -572,7 +574,7 @@ impl Common { &self, event_type: StateEventType, state_key: &str, - ) -> Result>> { + ) -> Result> { self.client .store() .get_state_event(self.room_id(), event_type, state_key) @@ -588,17 +590,17 @@ impl Common { /// ```no_run /// # async { /// # let room: matrix_sdk::room::Common = todo!(); - /// use matrix_sdk::ruma::events::room::power_levels::SyncRoomPowerLevelsEvent; + /// use matrix_sdk::ruma::events::room::power_levels::RoomPowerLevelsEventContent; /// - /// let power_levels: SyncRoomPowerLevelsEvent = room - /// .get_state_event_static() + /// let power_levels = room + /// .get_state_event_static::() /// .await? /// .expect("every room has a power_levels event") /// .deserialize()?; /// # anyhow::Ok(()) /// # }; /// ``` - pub async fn get_state_event_static(&self) -> Result>>> + pub async fn get_state_event_static(&self) -> Result>> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::Redacted: RedactedStateEventContent, @@ -614,11 +616,13 @@ impl Common { /// # async { /// # let room: matrix_sdk::room::Common = todo!(); /// use matrix_sdk::ruma::{ - /// events::room::member::SyncRoomMemberEvent, serde::Raw, user_id, + /// events::room::member::RoomMemberEventContent, serde::Raw, user_id, /// }; /// - /// let member_event: Option> = room - /// .get_state_event_static_for_key(user_id!("@alice:example.org")) + /// let member_event = room + /// .get_state_event_static_for_key::(user_id!( + /// "@alice:example.org" + /// )) /// .await?; /// # anyhow::Ok(()) /// # }; @@ -626,7 +630,7 @@ impl Common { pub async fn get_state_event_static_for_key( &self, state_key: &K, - ) -> Result>>> + ) -> Result>> where C: StaticEventContent + StaticStateEventContent + RedactContent, C::StateKey: Borrow, @@ -839,7 +843,10 @@ impl Common { .get_state_event_static::() .await? .and_then(|ev| ev.deserialize().ok()); - let acl = acl_ev.as_ref().and_then(|ev| ev.as_original()).map(|ev| &ev.content); + let acl = acl_ev.as_ref().and_then(|ev| match ev { + SyncOrStrippedState::Sync(ev) => ev.as_original().map(|ev| &ev.content), + SyncOrStrippedState::Stripped(ev) => Some(&ev.content), + }); // Filter out server names that: // - Are blocked due to server ACLs diff --git a/crates/matrix-sdk/tests/integration/room/common.rs b/crates/matrix-sdk/tests/integration/room/common.rs index 450aaaeb9..2703322dc 100644 --- a/crates/matrix-sdk/tests/integration/room/common.rs +++ b/crates/matrix-sdk/tests/integration/room/common.rs @@ -171,7 +171,10 @@ async fn test_state_event_getting() { .deserialize() .unwrap(); - assert_matches::assert_matches!(encryption_event, AnySyncStateEvent::RoomEncryption(_)); + assert_matches::assert_matches!( + encryption_event.as_sync(), + Some(AnySyncStateEvent::RoomEncryption(_)) + ); } #[async_test] diff --git a/testing/matrix-sdk-integration-testing/src/tests/redaction.rs b/testing/matrix-sdk-integration-testing/src/tests/redaction.rs index 95ca7af17..7c93a4b04 100644 --- a/testing/matrix-sdk-integration-testing/src/tests/redaction.rs +++ b/testing/matrix-sdk-integration-testing/src/tests/redaction.rs @@ -4,10 +4,7 @@ use matrix_sdk::{ config::SyncSettings, ruma::{ api::client::room::create_room::v3::Request as CreateRoomRequest, - events::{ - room::name::{RoomNameEventContent, SyncRoomNameEvent}, - StateEventType, - }, + events::{room::name::RoomNameEventContent, StateEventType}, }, Client, }; @@ -64,13 +61,14 @@ async fn test_redacting_name() -> Result<()> { let raw_event = room.get_state_event(StateEventType::RoomName, "").await?.expect("Room Name not found"); - let room_name_event: SyncRoomNameEvent = raw_event.deserialize_as()?; + let room_name_event = raw_event.cast::().deserialize()?; + let sync_room_name_event = room_name_event.as_sync().expect("event is sync event"); assert!( - room_name_event.as_original().expect("event exists").content.name.is_some(), + sync_room_name_event.as_original().expect("event exists").content.name.is_some(), "Event not found" ); - room.redact(room_name_event.event_id(), None, None).await?; + room.redact(sync_room_name_event.event_id(), None, None).await?; // sync up. for _ in 0..=10 { // we call sync up to ten times to give the server time to flush other @@ -84,10 +82,17 @@ async fn test_redacting_name() -> Result<()> { let raw_event = room.get_state_event(StateEventType::RoomName, "").await?.expect("Room Name not found"); - let room_name_event: SyncRoomNameEvent = raw_event.deserialize_as()?; + let room_name_event = raw_event.cast::().deserialize()?; // Name content has been redacted assert!( - room_name_event.as_original().expect("event exists").content.name.is_none(), + room_name_event + .as_sync() + .expect("event is sync event") + .as_original() + .expect("event exists") + .content + .name + .is_none(), "Event hasn't been redacted" ); @@ -132,14 +137,18 @@ async fn test_redacting_name_static() -> Result<()> { // check state event. - let room_name_event: SyncRoomNameEvent = - room.get_state_event_static().await?.expect("Room Name not found").deserialize()?; + let room_name_event = room + .get_state_event_static::() + .await? + .expect("Room Name not found") + .deserialize()?; + let sync_room_name_event = room_name_event.as_sync().expect("event is sync event"); assert!( - room_name_event.as_original().expect("event exists").content.name.is_some(), + sync_room_name_event.as_original().expect("event exists").content.name.is_some(), "Event not found" ); - room.redact(room_name_event.event_id(), None, None).await?; + room.redact(sync_room_name_event.event_id(), None, None).await?; // we sync up. for _ in 0..=10 { // we call sync up to ten times to give the server time to flush other @@ -151,11 +160,21 @@ async fn test_redacting_name_static() -> Result<()> { } } - let room_name_event: SyncRoomNameEvent = - room.get_state_event_static().await?.expect("Room Name not found").deserialize()?; + let room_name_event = room + .get_state_event_static::() + .await? + .expect("Room Name not found") + .deserialize()?; // Name content has been redacted assert!( - room_name_event.as_original().expect("event exists").content.name.is_none(), + room_name_event + .as_sync() + .expect("event is sync event") + .as_original() + .expect("event exists") + .content + .name + .is_none(), "Event hasn't been redacted" );