Skip to content

Commit

Permalink
sdk: Add method to get the set of parent spaces of a room
Browse files Browse the repository at this point in the history
  • Loading branch information
progval authored Nov 22, 2023
1 parent 2aaa709 commit 5c37acb
Show file tree
Hide file tree
Showing 3 changed files with 509 additions and 3 deletions.
104 changes: 101 additions & 3 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use std::{borrow::Borrow, collections::BTreeMap, ops::Deref, time::Duration};

use eyeball::SharedObservable;
use futures_core::Stream;
use futures_util::stream::FuturesUnordered;
use matrix_sdk_base::{
deserialized_responses::{
RawAnySyncOrStrippedState, RawSyncOrStrippedState, SyncOrStrippedState, TimelineEvent,
Expand Down Expand Up @@ -53,21 +55,22 @@ use ruma::{
topic::RoomTopicEventContent,
MediaSource,
},
space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
tag::{TagInfo, TagName},
AnyRoomAccountDataEvent, AnyStateEvent, EmptyStateKey, MessageLikeEventContent,
MessageLikeEventType, RedactContent, RedactedStateEventContent, RoomAccountDataEvent,
RoomAccountDataEventContent, RoomAccountDataEventType, StateEventContent, StateEventType,
StaticEventContent, StaticStateEventContent,
StaticEventContent, StaticStateEventContent, SyncStateEvent,
},
push::{Action, PushConditionRoomCtx},
serde::Raw,
uint, EventId, Int, MatrixToUri, MatrixUri, MxcUri, OwnedEventId, OwnedServerName,
uint, EventId, Int, MatrixToUri, MatrixUri, MxcUri, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedTransactionId, OwnedUserId, TransactionId, UInt, UserId,
};
use serde::de::DeserializeOwned;
use thiserror::Error;
use tokio::sync::broadcast;
use tracing::{debug, instrument, warn};
use tracing::{debug, info, instrument, warn};

use self::futures::{SendAttachment, SendMessageLikeEvent, SendRawMessageLikeEvent};
use crate::{
Expand Down Expand Up @@ -750,6 +753,81 @@ impl Room {
Ok(self.client.store().get_state_event_static_for_key(self.room_id(), state_key).await?)
}

/// Returns the parents this room advertises as its parents.
///
/// Results are in no particular order.
pub async fn parent_spaces(&self) -> Result<impl Stream<Item = Result<ParentSpace>> + '_> {
// Implements this algorithm:
// https://spec.matrix.org/v1.8/client-server-api/#mspaceparent-relationships

// Get all m.room.parent events for this room
Ok(self
.get_state_events_static::<SpaceParentEventContent>()
.await?
.into_iter()
// Extract state key (ie. the parent's id) and sender
.flat_map(|parent_event| match parent_event.deserialize() {
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(e))) => {
Some((e.state_key.to_owned(), e.sender))
}
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => None,
Ok(SyncOrStrippedState::Stripped(e)) => Some((e.state_key.to_owned(), e.sender)),
Err(e) => {
info!(room_id = ?self.room_id(), "Could not deserialize m.room.parent: {e}");
None
}
})
// Check whether the parent recognizes this room as its child
.map(|(state_key, sender): (OwnedRoomId, OwnedUserId)| async move {
let Some(parent_room) = self.client.get_room(&state_key) else {
// We are not in the room, cannot check if the relationship is reciprocal
// TODO: try peeking into the room
return Ok(ParentSpace::Unverifiable(state_key));
};
// Get the m.room.child state of the parent with this room's id
// as state key.
if let Some(child_event) = parent_room
.get_state_event_static_for_key::<SpaceChildEventContent, _>(self.room_id())
.await?
{
match child_event.deserialize() {
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(_))) => {
// There is a valid m.room.child in the parent pointing to
// this room
return Ok(ParentSpace::Reciprocal(parent_room));
}
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => {}
Ok(SyncOrStrippedState::Stripped(_)) => {}
Err(e) => {
info!(
room_id = ?self.room_id(), parent_room_id = ?state_key,
"Could not deserialize m.room.child: {e}"
);
}
}
// Otherwise the event is either invalid or redacted. If
// redacted it would be missing the
// `via` key, thereby invalidating that end of the
// relationship: https://spec.matrix.org/v1.8/client-server-api/#mspacechild
}

// No reciprocal m.room.child found, let's check if the sender has the
// power to set it
let Some(member) = parent_room.get_member(&sender).await? else {
// Sender is not even in the parent room
return Ok(ParentSpace::Illegitimate(parent_room));
};

if member.can_send_state(StateEventType::SpaceChild) {
// Sender does have the power to set m.room.child
Ok(ParentSpace::WithPowerlevel(parent_room))
} else {
Ok(ParentSpace::Illegitimate(parent_room))
}
})
.collect::<FuturesUnordered<_>>())
}

/// Get account data in this room.
pub async fn account_data(
&self,
Expand Down Expand Up @@ -2334,6 +2412,26 @@ impl Receipts {
}
}

/// [Parent space](https://spec.matrix.org/v1.8/client-server-api/#mspaceparent-relationships)
/// listed by a room, possibly validated by checking the space's state.
#[derive(Debug)]
pub enum ParentSpace {
/// The room recognizes the given room as its parent, and the parent
/// recognizes it as its child.
Reciprocal(Room),
/// The room recognizes the given room as its parent, but the parent does
/// not recognizes it as its child. However, the author of the
/// `m.room.parent` event in the room has a sufficient power level in the
/// parent to create the child event.
WithPowerlevel(Room),
/// The room recognizes the given room as its parent, but the parent does
/// not recognizes it as its child.
Illegitimate(Room),
/// The room recognizes the given id as its parent room, but we cannot check
/// whether the parent recognizes it as its child.
Unverifiable(OwnedRoomId),
}

#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use matrix_sdk_base::SessionMeta;
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk/tests/integration/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod common;
mod joined;
mod left;
mod notification_mode;
mod spaces;
Loading

0 comments on commit 5c37acb

Please sign in to comment.