Skip to main content

ruma_client_api/membership/
invite_user.rs

1//! `POST /_matrix/client/*/rooms/{roomId}/invite`
2//!
3//! Invite a user to a room.
4
5pub mod v3 {
6    //! `/v3/` ([spec (MXID)][spec-mxid], [spec (3PID)][spec-3pid])
7    //!
8    //! This endpoint has two forms: one to invite a user
9    //! [by their Matrix identifier][spec-mxid], and one to invite a user
10    //! [by their third party identifier][spec-3pid].
11    //!
12    //! [spec-mxid]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3roomsroomidinvite
13    //! [spec-3pid]: https://spec.matrix.org/latest/client-server-api/#thirdparty_post_matrixclientv3roomsroomidinvite
14
15    use ruma_common::{
16        OwnedRoomId, OwnedUserId,
17        api::{auth_scheme::AccessToken, request, response},
18        metadata,
19    };
20    use serde::{Deserialize, Serialize};
21
22    use crate::membership::Invite3pid;
23
24    metadata! {
25        method: POST,
26        rate_limited: true,
27        authentication: AccessToken,
28        history: {
29            1.0 => "/_matrix/client/r0/rooms/{room_id}/invite",
30            1.1 => "/_matrix/client/v3/rooms/{room_id}/invite",
31        }
32    }
33
34    /// Request type for the `invite_user` endpoint.
35    #[request(error = crate::Error)]
36    pub struct Request {
37        /// The room where the user should be invited.
38        #[ruma_api(path)]
39        pub room_id: OwnedRoomId,
40
41        /// The user to invite.
42        #[ruma_api(body)]
43        pub recipient: InvitationRecipient,
44    }
45
46    /// Response type for the `invite_user` endpoint.
47    #[response(error = crate::Error)]
48    #[derive(Default)]
49    pub struct Response {}
50
51    impl Request {
52        /// Creates a new `Request` with the given room ID and invitation recipient.
53        pub fn new(room_id: OwnedRoomId, recipient: InvitationRecipient) -> Self {
54            Self { room_id, recipient }
55        }
56    }
57
58    impl Response {
59        /// Creates an empty `Response`.
60        pub fn new() -> Self {
61            Self {}
62        }
63    }
64
65    /// Distinguishes between invititations by Matrix or third party identifiers.
66    #[derive(Clone, Debug, Deserialize, Serialize)]
67    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
68    #[serde(untagged)]
69    pub enum InvitationRecipient {
70        /// Used to invite user by their Matrix identifier.
71        UserId(InviteUserId),
72
73        /// Used to invite user by a third party identifier.
74        ThirdPartyId(Invite3pid),
75    }
76
77    impl From<InviteUserId> for InvitationRecipient {
78        fn from(value: InviteUserId) -> Self {
79            Self::UserId(value)
80        }
81    }
82
83    impl From<Invite3pid> for InvitationRecipient {
84        fn from(value: Invite3pid) -> Self {
85            Self::ThirdPartyId(value)
86        }
87    }
88
89    /// Data to invite a user by Matrix identifier.
90    #[derive(Clone, Debug, Deserialize, Serialize)]
91    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
92    pub struct InviteUserId {
93        /// The Matrix identifier of the user to invite.
94        pub user_id: OwnedUserId,
95
96        /// The reason for inviting the user.
97        #[serde(skip_serializing_if = "Option::is_none")]
98        pub reason: Option<String>,
99    }
100
101    impl InviteUserId {
102        /// Constructs a new `InviteUserId` with the given Matrix identifier.
103        pub fn new(user_id: OwnedUserId) -> Self {
104            Self { user_id, reason: None }
105        }
106    }
107
108    #[cfg(test)]
109    mod tests {
110        use assert_matches2::assert_matches;
111        use ruma_common::thirdparty::Medium;
112        use serde_json::{from_value as from_json_value, json};
113
114        use super::{InvitationRecipient, InviteUserId};
115
116        #[test]
117        fn deserialize_invite_by_user_id() {
118            let incoming =
119                from_json_value::<InvitationRecipient>(json!({ "user_id": "@carl:example.org" }))
120                    .unwrap();
121
122            assert_matches!(
123                incoming,
124                InvitationRecipient::UserId(InviteUserId { user_id, reason: None })
125            );
126            assert_eq!(user_id, "@carl:example.org");
127        }
128
129        #[test]
130        fn deserialize_invite_by_3pid() {
131            let incoming = from_json_value::<InvitationRecipient>(json!({
132                "id_server": "example.org",
133                "id_access_token": "abcdefghijklmnop",
134                "medium": "email",
135                "address": "carl@example.org"
136            }))
137            .unwrap();
138
139            assert_matches!(incoming, InvitationRecipient::ThirdPartyId(third_party_id));
140
141            assert_eq!(third_party_id.id_server, "example.org");
142            assert_eq!(third_party_id.id_access_token, "abcdefghijklmnop");
143            assert_eq!(third_party_id.medium, Medium::Email);
144            assert_eq!(third_party_id.address, "carl@example.org");
145        }
146    }
147}