Skip to main content

ruma_client_api/room/
get_summary.rs

1//! `GET /_matrix/client/v1/summary/{roomIdOrAlias}`
2//!
3//! Returns a short description of the state of a room.
4
5pub mod v1 {
6    //! `v1` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#get_matrixclientv1room_summaryroomidoralias
9
10    use ruma_common::{
11        OwnedRoomOrAliasId, OwnedServerName,
12        api::{auth_scheme::AccessTokenOptional, request},
13        metadata,
14        room::RoomSummary,
15    };
16    use ruma_events::room::member::MembershipState;
17
18    metadata! {
19        method: GET,
20        rate_limited: false,
21        authentication: AccessTokenOptional,
22        history: {
23            unstable => "/_matrix/client/unstable/im.nheko.summary/rooms/{room_id_or_alias}/summary",
24            1.15 => "/_matrix/client/v1/room_summary/{room_id_or_alias}",
25        }
26    }
27
28    /// Request type for the `get_summary` endpoint.
29    #[request]
30    pub struct Request {
31        /// Alias or ID of the room to be summarized.
32        #[ruma_api(path)]
33        pub room_id_or_alias: OwnedRoomOrAliasId,
34
35        /// A list of servers the homeserver should attempt to use to peek at the room.
36        ///
37        /// Defaults to an empty `Vec`.
38        #[serde(default, skip_serializing_if = "Vec::is_empty")]
39        #[ruma_api(query)]
40        pub via: Vec<OwnedServerName>,
41    }
42
43    impl Request {
44        /// Creates a new `Request` with the given room or alias ID and via server names.
45        pub fn new(room_id_or_alias: OwnedRoomOrAliasId, via: Vec<OwnedServerName>) -> Self {
46            Self { room_id_or_alias, via }
47        }
48    }
49
50    /// Response type for the `get_summary` endpoint.
51    #[derive(Debug, Clone)]
52    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
53    pub struct Response {
54        /// The summary of the room.
55        pub summary: RoomSummary,
56
57        /// The current membership of this user in the room.
58        ///
59        /// This field will not be present when called unauthenticated, but is required when called
60        /// authenticated. It should be `leave` if the server doesn't know about the room, since
61        /// for all other membership states the server would know about the room already.
62        pub membership: Option<MembershipState>,
63    }
64
65    impl Response {
66        /// Creates a new [`Response`] with the given summary.
67        pub fn new(summary: RoomSummary) -> Self {
68            Self { summary, membership: None }
69        }
70    }
71
72    impl From<RoomSummary> for Response {
73        fn from(value: RoomSummary) -> Self {
74            Self::new(value)
75        }
76    }
77
78    #[doc(hidden)]
79    #[derive(ruma_common::serde::_FakeDeriveSerde)]
80    #[cfg_attr(feature = "server", derive(serde::Serialize, ruma_common::api::OutgoingBodyJson))]
81    pub struct ResponseBody {
82        #[serde(flatten)]
83        summary: RoomSummary,
84        #[serde(skip_serializing_if = "Option::is_none")]
85        membership: Option<MembershipState>,
86    }
87
88    #[cfg(feature = "client")]
89    impl<'de> serde::Deserialize<'de> for ResponseBody {
90        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91        where
92            D: serde::Deserializer<'de>,
93        {
94            use ruma_common::serde::from_raw_json_value;
95            use serde_json::value::RawValue as RawJsonValue;
96
97            #[derive(serde::Deserialize)]
98            struct ResponseBodyDeHelper {
99                membership: Option<MembershipState>,
100            }
101
102            let json = Box::<RawJsonValue>::deserialize(deserializer)?;
103            let summary = from_raw_json_value(&json)?;
104            let membership = from_raw_json_value::<ResponseBodyDeHelper, _>(&json)?.membership;
105
106            Ok(Self { membership, summary })
107        }
108    }
109
110    #[cfg(feature = "server")]
111    impl ruma_common::api::OutgoingResponse for Response {
112        type Body = ResponseBody;
113
114        fn try_into_http_response_inner(
115            self,
116        ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
117            let Self { summary, membership } = self;
118
119            http::Response::builder()
120                .header(http::header::CONTENT_TYPE, ruma_common::http_headers::APPLICATION_JSON)
121                .body(ResponseBody { summary, membership })
122                .map_err(Into::into)
123        }
124    }
125
126    #[cfg(feature = "client")]
127    impl ruma_common::api::IncomingResponse for Response {
128        type EndpointError = ruma_common::api::error::Error;
129
130        fn try_from_http_response_inner(
131            response: http::Response<&[u8]>,
132        ) -> Result<Self, ruma_common::api::error::DeserializationError> {
133            let ResponseBody { summary, membership } = serde_json::from_slice(response.body())?;
134            Ok(Self { summary, membership })
135        }
136    }
137}
138
139#[cfg(all(test, feature = "client"))]
140mod tests {
141    use ruma_common::api::IncomingResponseExt as _;
142    use ruma_events::room::member::MembershipState;
143    use serde_json::json;
144
145    use super::v1::Response;
146
147    #[test]
148    fn deserialize_response() {
149        let body = json!({
150            "room_id": "!room:localhost",
151            "num_joined_members": 5,
152            "world_readable": false,
153            "guest_can_join": false,
154            "join_rule": "restricted",
155            "allowed_room_ids": ["!otherroom:localhost"],
156            "membership": "invite",
157        })
158        .to_string();
159        let response = http::Response::new(body.as_bytes());
160        let response = Response::try_from_http_response(response).unwrap();
161
162        assert_eq!(response.summary.room_id, "!room:localhost");
163        assert_eq!(response.membership, Some(MembershipState::Invite));
164    }
165}