federation-api: Implement space summary API
According to MSC2946
This commit is contained in:
parent
84e1c919c9
commit
c52e51c016
@ -1,5 +1,9 @@
|
||||
# [unreleased]
|
||||
|
||||
Improvements:
|
||||
|
||||
* Add support for the space summary API in `space::get_hierarchy` according to MSC2946.
|
||||
|
||||
# 0.4.0
|
||||
|
||||
Breaking changes:
|
||||
|
@ -23,6 +23,7 @@ pub mod knock;
|
||||
pub mod membership;
|
||||
pub mod openid;
|
||||
pub mod query;
|
||||
pub mod space;
|
||||
pub mod thirdparty;
|
||||
pub mod transactions;
|
||||
|
||||
|
256
crates/ruma-federation-api/src/space.rs
Normal file
256
crates/ruma-federation-api/src/space.rs
Normal file
@ -0,0 +1,256 @@
|
||||
//! Spaces endpoints.
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_common::{directory::PublicRoomJoinRule, room::RoomType};
|
||||
use ruma_events::space::child::HierarchySpaceChildStateEvent;
|
||||
use ruma_identifiers::{MxcUri, RoomAliasId, RoomId, RoomName};
|
||||
use ruma_serde::Raw;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod get_hierarchy;
|
||||
|
||||
/// The summary of a parent space.
|
||||
///
|
||||
/// To create an instance of this type, first create a `SpaceHierarchyParentSummaryInit` and convert
|
||||
/// it via `SpaceHierarchyParentSummary::from` / `.into()`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct SpaceHierarchyParentSummary {
|
||||
/// The canonical alias of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(
|
||||
feature = "compat",
|
||||
serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
|
||||
)]
|
||||
pub canonical_alias: Option<Box<RoomAliasId>>,
|
||||
|
||||
/// The name of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<Box<RoomName>>,
|
||||
|
||||
/// The number of members joined to the room.
|
||||
pub num_joined_members: UInt,
|
||||
|
||||
/// The ID of the room.
|
||||
pub room_id: Box<RoomId>,
|
||||
|
||||
/// The topic of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub topic: Option<String>,
|
||||
|
||||
/// Whether the room may be viewed by guest users without joining.
|
||||
pub world_readable: bool,
|
||||
|
||||
/// Whether guest users may join the room and participate in it.
|
||||
///
|
||||
/// If they can, they will be subject to ordinary power level rules like any other user.
|
||||
pub guest_can_join: bool,
|
||||
|
||||
/// The URL for the room's avatar, if one is set.
|
||||
///
|
||||
/// If you activate the `compat` feature, this field being an empty string in JSON will result
|
||||
/// in `None` here during deserialization.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(
|
||||
feature = "compat",
|
||||
serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
|
||||
)]
|
||||
pub avatar_url: Option<Box<MxcUri>>,
|
||||
|
||||
/// The join rule of the room.
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
pub join_rule: PublicRoomJoinRule,
|
||||
|
||||
/// The type of room from `m.room.create`, if any.
|
||||
pub room_type: Option<RoomType>,
|
||||
|
||||
/// The stripped `m.space.child` events of the space-room.
|
||||
///
|
||||
/// If the room is not a space-room, this should be empty.
|
||||
pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
|
||||
|
||||
/// If the room is a restricted room, these are the room IDs which are specified by the join
|
||||
/// rules.
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
pub allowed_room_ids: Vec<Box<RoomId>>,
|
||||
}
|
||||
|
||||
/// Initial set of mandatory fields of `SpaceHierarchyParentSummary`.
|
||||
///
|
||||
/// This struct will not be updated even if additional fields are added to
|
||||
/// `SpaceHierarchyParentSummary` in a new (non-breaking) release of the Matrix specification.
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::exhaustive_structs)]
|
||||
pub struct SpaceHierarchyParentSummaryInit {
|
||||
/// The number of members joined to the room.
|
||||
pub num_joined_members: UInt,
|
||||
|
||||
/// The ID of the room.
|
||||
pub room_id: Box<RoomId>,
|
||||
|
||||
/// Whether the room may be viewed by guest users without joining.
|
||||
pub world_readable: bool,
|
||||
|
||||
/// Whether guest users may join the room and participate in it.
|
||||
///
|
||||
/// If they can, they will be subject to ordinary power level rules like any other user.
|
||||
pub guest_can_join: bool,
|
||||
|
||||
/// The join rule of the room.
|
||||
pub join_rule: PublicRoomJoinRule,
|
||||
|
||||
/// The stripped `m.space.child` events of the space-room.
|
||||
///
|
||||
/// If the room is not a space-room, this should be empty.
|
||||
pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
|
||||
|
||||
/// If the room is a restricted room, these are the room IDs which are specified by the join
|
||||
/// rules.
|
||||
pub allowed_room_ids: Vec<Box<RoomId>>,
|
||||
}
|
||||
|
||||
impl From<SpaceHierarchyParentSummaryInit> for SpaceHierarchyParentSummary {
|
||||
fn from(init: SpaceHierarchyParentSummaryInit) -> Self {
|
||||
let SpaceHierarchyParentSummaryInit {
|
||||
num_joined_members,
|
||||
room_id,
|
||||
world_readable,
|
||||
guest_can_join,
|
||||
join_rule,
|
||||
children_state,
|
||||
allowed_room_ids,
|
||||
} = init;
|
||||
|
||||
Self {
|
||||
canonical_alias: None,
|
||||
name: None,
|
||||
num_joined_members,
|
||||
room_id,
|
||||
topic: None,
|
||||
world_readable,
|
||||
guest_can_join,
|
||||
avatar_url: None,
|
||||
join_rule,
|
||||
room_type: None,
|
||||
children_state,
|
||||
allowed_room_ids,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The summary of a space's child.
|
||||
///
|
||||
/// To create an instance of this type, first create a `SpaceHierarchyChildSummaryInit` and convert
|
||||
/// it via `SpaceHierarchyChildSummary::from` / `.into()`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct SpaceHierarchyChildSummary {
|
||||
/// The canonical alias of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(
|
||||
feature = "compat",
|
||||
serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
|
||||
)]
|
||||
pub canonical_alias: Option<Box<RoomAliasId>>,
|
||||
|
||||
/// The name of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<Box<RoomName>>,
|
||||
|
||||
/// The number of members joined to the room.
|
||||
pub num_joined_members: UInt,
|
||||
|
||||
/// The ID of the room.
|
||||
pub room_id: Box<RoomId>,
|
||||
|
||||
/// The topic of the room, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub topic: Option<String>,
|
||||
|
||||
/// Whether the room may be viewed by guest users without joining.
|
||||
pub world_readable: bool,
|
||||
|
||||
/// Whether guest users may join the room and participate in it.
|
||||
///
|
||||
/// If they can, they will be subject to ordinary power level rules like any other user.
|
||||
pub guest_can_join: bool,
|
||||
|
||||
/// The URL for the room's avatar, if one is set.
|
||||
///
|
||||
/// If you activate the `compat` feature, this field being an empty string in JSON will result
|
||||
/// in `None` here during deserialization.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(
|
||||
feature = "compat",
|
||||
serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
|
||||
)]
|
||||
pub avatar_url: Option<Box<MxcUri>>,
|
||||
|
||||
/// The join rule of the room.
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
pub join_rule: PublicRoomJoinRule,
|
||||
|
||||
/// The type of room from `m.room.create`, if any.
|
||||
pub room_type: Option<RoomType>,
|
||||
|
||||
/// If the room is a restricted room, these are the room IDs which are specified by the join
|
||||
/// rules.
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
pub allowed_room_ids: Vec<Box<RoomId>>,
|
||||
}
|
||||
|
||||
/// Initial set of mandatory fields of `SpaceHierarchyChildSummary`.
|
||||
///
|
||||
/// This struct will not be updated even if additional fields are added to
|
||||
/// `SpaceHierarchyChildSummary` in a new (non-breaking) release of the Matrix specification.
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::exhaustive_structs)]
|
||||
pub struct SpaceHierarchyChildSummaryInit {
|
||||
/// The number of members joined to the room.
|
||||
pub num_joined_members: UInt,
|
||||
|
||||
/// The ID of the room.
|
||||
pub room_id: Box<RoomId>,
|
||||
|
||||
/// Whether the room may be viewed by guest users without joining.
|
||||
pub world_readable: bool,
|
||||
|
||||
/// Whether guest users may join the room and participate in it.
|
||||
///
|
||||
/// If they can, they will be subject to ordinary power level rules like any other user.
|
||||
pub guest_can_join: bool,
|
||||
|
||||
/// The join rule of the room.
|
||||
pub join_rule: PublicRoomJoinRule,
|
||||
|
||||
/// If the room is a restricted room, these are the room IDs which are specified by the join
|
||||
/// rules.
|
||||
pub allowed_room_ids: Vec<Box<RoomId>>,
|
||||
}
|
||||
|
||||
impl From<SpaceHierarchyChildSummaryInit> for SpaceHierarchyChildSummary {
|
||||
fn from(init: SpaceHierarchyChildSummaryInit) -> Self {
|
||||
let SpaceHierarchyChildSummaryInit {
|
||||
num_joined_members,
|
||||
room_id,
|
||||
world_readable,
|
||||
guest_can_join,
|
||||
join_rule,
|
||||
allowed_room_ids,
|
||||
} = init;
|
||||
|
||||
Self {
|
||||
canonical_alias: None,
|
||||
name: None,
|
||||
num_joined_members,
|
||||
room_id,
|
||||
topic: None,
|
||||
world_readable,
|
||||
guest_can_join,
|
||||
avatar_url: None,
|
||||
join_rule,
|
||||
room_type: None,
|
||||
allowed_room_ids,
|
||||
}
|
||||
}
|
||||
}
|
70
crates/ruma-federation-api/src/space/get_hierarchy.rs
Normal file
70
crates/ruma-federation-api/src/space/get_hierarchy.rs
Normal file
@ -0,0 +1,70 @@
|
||||
//! `GET /_matrix/federation/*/hierarchy/{roomId}`
|
||||
//!
|
||||
//! Endpoint to get the children of a given space.
|
||||
|
||||
pub mod v1 {
|
||||
//! `/v1/` ([spec])
|
||||
//!
|
||||
//! [spec]: https://spec.matrix.org/v1.2/server-server-api/#get_matrixfederationv1hierarchyroomid
|
||||
|
||||
use ruma_api::ruma_api;
|
||||
use ruma_identifiers::RoomId;
|
||||
|
||||
use crate::space::{SpaceHierarchyChildSummary, SpaceHierarchyParentSummary};
|
||||
|
||||
ruma_api! {
|
||||
metadata: {
|
||||
description: "Get the space tree in a depth-first manner to locate child rooms of a given space.",
|
||||
name: "hierarchy",
|
||||
method: GET,
|
||||
unstable_path: "/_matrix/federation/unstable/org.matrix.msc2946/hierarchy/:room_id",
|
||||
stable_path: "/_matrix/federation/v1/hierarchy/:room_id",
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
added: 1.2,
|
||||
}
|
||||
|
||||
request: {
|
||||
/// The room ID of the space to get a hierarchy for.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: &'a RoomId,
|
||||
|
||||
/// Whether or not the server should only consider suggested rooms.
|
||||
///
|
||||
/// Suggested rooms are annotated in their `m.space.child` event contents.
|
||||
#[ruma_api(query)]
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
pub suggested_only: bool,
|
||||
}
|
||||
|
||||
response: {
|
||||
/// A summary of the space’s children.
|
||||
///
|
||||
/// Rooms which the requesting server cannot peek/join will be excluded.
|
||||
pub children: Vec<SpaceHierarchyChildSummary>,
|
||||
|
||||
/// The list of room IDs the requesting server doesn’t have a viable way to peek/join.
|
||||
///
|
||||
/// Rooms which the responding server cannot provide details on will be outright
|
||||
/// excluded from the response instead.
|
||||
pub inaccessible_children: Vec<Box<RoomId>>,
|
||||
|
||||
/// A summary of the requested room.
|
||||
pub room: SpaceHierarchyParentSummary,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
/// Creates a `Request` with the given room ID.
|
||||
pub fn new(room_id: &'a RoomId) -> Self {
|
||||
Self { room_id, suggested_only: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given room summary.
|
||||
pub fn new(room_summary: SpaceHierarchyParentSummary) -> Self {
|
||||
Self { children: Vec::new(), inaccessible_children: Vec::new(), room: room_summary }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user