Skip to main content

ruma_client_api/threads/
get_threads.rs

1//! `GET /_matrix/client/*/rooms/{roomId}/threads`
2//!
3//! Retrieve a list of threads in a room, with optional filters.
4
5pub mod v1 {
6    //! `/v1/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#get_matrixclientv1roomsroomidthreads
9
10    use js_int::UInt;
11    use ruma_common::{
12        OwnedRoomId,
13        api::{auth_scheme::AccessToken, request, response},
14        metadata,
15        serde::{Raw, StringEnum},
16    };
17    use ruma_events::AnyTimelineEvent;
18
19    use crate::PrivOwnedStr;
20
21    metadata! {
22        method: GET,
23        rate_limited: true,
24        authentication: AccessToken,
25        history: {
26            unstable => "/_matrix/client/unstable/org.matrix.msc3856/rooms/{room_id}/threads",
27            1.4 => "/_matrix/client/v1/rooms/{room_id}/threads",
28        }
29    }
30
31    /// Request type for the `get_thread_roots` endpoint.
32    #[request]
33    pub struct Request {
34        /// The room ID where the thread roots are located.
35        #[ruma_api(path)]
36        pub room_id: OwnedRoomId,
37
38        /// The pagination token to start returning results from.
39        ///
40        /// If `None`, results start at the most recent topological event visible to the user.
41        #[serde(skip_serializing_if = "Option::is_none")]
42        #[ruma_api(query)]
43        pub from: Option<String>,
44
45        /// Which thread roots are of interest to the caller.
46        #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
47        #[ruma_api(query)]
48        pub include: IncludeThreads,
49
50        /// The maximum number of results to return in a single `chunk`.
51        ///
52        /// Servers should apply a default value, and impose a maximum value to avoid resource
53        /// exhaustion.
54        #[serde(skip_serializing_if = "Option::is_none")]
55        #[ruma_api(query)]
56        pub limit: Option<UInt>,
57    }
58
59    /// Response type for the `get_thread_roots` endpoint.
60    #[response]
61    pub struct Response {
62        /// The thread roots, ordered by the `latest_event` in each event's aggregation bundle.
63        ///
64        /// All events returned include bundled aggregations.
65        pub chunk: Vec<Raw<AnyTimelineEvent>>,
66
67        /// An opaque string to provide to `from` to keep paginating the responses.
68        ///
69        /// If this is `None`, there are no more results to fetch and the client should stop
70        /// paginating.
71        #[serde(skip_serializing_if = "Option::is_none")]
72        pub next_batch: Option<String>,
73    }
74
75    impl Request {
76        /// Creates a new `Request` with the given room ID.
77        pub fn new(room_id: OwnedRoomId) -> Self {
78            Self { room_id, from: None, include: IncludeThreads::default(), limit: None }
79        }
80    }
81
82    impl Response {
83        /// Creates a new `Response` with the given chunk.
84        pub fn new(chunk: Vec<Raw<AnyTimelineEvent>>) -> Self {
85            Self { chunk, next_batch: None }
86        }
87    }
88
89    /// Which threads to include in the response.
90    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
91    #[derive(Clone, Default, StringEnum)]
92    #[ruma_enum(rename_all = "lowercase")]
93    #[non_exhaustive]
94    pub enum IncludeThreads {
95        /// `all`
96        ///
97        /// Include all thread roots found in the room.
98        ///
99        /// This is the default.
100        #[default]
101        All,
102
103        /// `participated`
104        ///
105        /// Only include thread roots for threads where [`current_user_participated`] is `true`.
106        ///
107        /// [`current_user_participated`]: https://spec.matrix.org/v1.19/client-server-api/#server-side-aggregation-of-mthread-relationships
108        Participated,
109
110        #[doc(hidden)]
111        _Custom(PrivOwnedStr),
112    }
113}
114
115#[cfg(all(test, feature = "server"))]
116mod tests {
117    use ruma_common::{api::IncomingRequest, room_id};
118
119    use super::v1::{IncludeThreads, Request};
120
121    #[test]
122    // Testing when the include parameter is omitted from uri
123    // It should default to IncludeThreads::All
124    fn deserialize_request_without_include() {
125        let http_req = http::Request::builder()
126            .method(http::Method::GET)
127            .uri("/_matrix/client/v1/rooms/!room:example.com/threads")
128            .body(Vec::<u8>::new())
129            .unwrap();
130
131        let req = Request::try_from_http_request(http_req, &["!room:example.com"]).unwrap();
132
133        assert_eq!(req.room_id, room_id!("!room:example.com"));
134        assert_eq!(req.from, None);
135        assert_eq!(req.limit, None);
136        assert_eq!(req.include, IncludeThreads::All);
137    }
138
139    #[test]
140    // Testing when the include parameter is explicitly provided
141    fn deserialize_request_explicit_include() {
142        let http_req = http::Request::builder()
143            .method(http::Method::GET)
144            .uri("/_matrix/client/v1/rooms/!room:example.com/threads?include=participated")
145            .body(Vec::<u8>::new())
146            .unwrap();
147
148        let req = Request::try_from_http_request(http_req, &["!room:example.com"]).unwrap();
149
150        assert_eq!(req.room_id, room_id!("!room:example.com"));
151        assert_eq!(req.include, IncludeThreads::Participated);
152    }
153}