ruma_client_api/membership/
join_room_by_id_or_alias.rs1pub mod v3 {
6 use ruma_common::{
11 api::{response, Metadata},
12 metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName,
13 };
14
15 use crate::membership::ThirdPartySigned;
16
17 metadata! {
18 method: POST,
19 rate_limited: true,
20 authentication: AccessToken,
21 history: {
22 1.0 => "/_matrix/client/r0/join/{room_id_or_alias}",
23 1.1 => "/_matrix/client/v3/join/{room_id_or_alias}",
24 }
25 }
26
27 #[derive(Clone, Debug)]
29 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
30 pub struct Request {
31 pub room_id_or_alias: OwnedRoomOrAliasId,
33
34 pub third_party_signed: Option<ThirdPartySigned>,
37
38 pub reason: Option<String>,
40
41 pub via: Vec<OwnedServerName>,
51 }
52
53 #[cfg_attr(feature = "client", derive(serde::Serialize))]
55 #[cfg_attr(feature = "server", derive(serde::Deserialize))]
56 struct RequestQuery {
57 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
59 via: Vec<OwnedServerName>,
60
61 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
65 server_name: Vec<OwnedServerName>,
66 }
67
68 #[cfg_attr(feature = "client", derive(serde::Serialize))]
70 #[cfg_attr(feature = "server", derive(serde::Deserialize))]
71 struct RequestBody {
72 #[serde(skip_serializing_if = "Option::is_none")]
75 third_party_signed: Option<ThirdPartySigned>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 reason: Option<String>,
80 }
81
82 #[cfg(feature = "client")]
83 impl ruma_common::api::OutgoingRequest for Request {
84 type EndpointError = crate::Error;
85 type IncomingResponse = Response;
86
87 fn try_into_http_request<T: Default + bytes::BufMut>(
88 self,
89 base_url: &str,
90 access_token: ruma_common::api::SendAccessToken<'_>,
91 considering: &'_ ruma_common::api::SupportedVersions,
92 ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
93 use http::header::{self, HeaderValue};
94
95 let server_name = if considering
98 .versions
99 .iter()
100 .rev()
101 .any(|version| version.is_superset_of(ruma_common::api::MatrixVersion::V1_12))
102 {
103 vec![]
104 } else {
105 self.via.clone()
106 };
107
108 let query_string =
109 serde_html_form::to_string(RequestQuery { server_name, via: self.via })?;
110
111 let http_request = http::Request::builder()
112 .method(Self::METHOD)
113 .uri(Self::make_endpoint_url(
114 considering,
115 base_url,
116 &[&self.room_id_or_alias],
117 &query_string,
118 )?)
119 .header(header::CONTENT_TYPE, "application/json")
120 .header(
121 header::AUTHORIZATION,
122 HeaderValue::from_str(&format!(
123 "Bearer {}",
124 access_token
125 .get_required_for_endpoint()
126 .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?
127 ))?,
128 )
129 .body(ruma_common::serde::json_to_buf(&RequestBody {
130 third_party_signed: self.third_party_signed,
131 reason: self.reason,
132 })?)?;
133
134 Ok(http_request)
135 }
136 }
137
138 #[cfg(feature = "server")]
139 impl ruma_common::api::IncomingRequest for Request {
140 type EndpointError = crate::Error;
141 type OutgoingResponse = Response;
142
143 fn try_from_http_request<B, S>(
144 request: http::Request<B>,
145 path_args: &[S],
146 ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
147 where
148 B: AsRef<[u8]>,
149 S: AsRef<str>,
150 {
151 if request.method() != Self::METHOD {
152 return Err(ruma_common::api::error::FromHttpRequestError::MethodMismatch {
153 expected: Self::METHOD,
154 received: request.method().clone(),
155 });
156 }
157
158 let (room_id_or_alias,) =
159 serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
160 _,
161 serde::de::value::Error,
162 >::new(
163 path_args.iter().map(::std::convert::AsRef::as_ref),
164 ))?;
165
166 let request_query: RequestQuery =
167 serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
168 let via = if request_query.via.is_empty() {
169 request_query.server_name
170 } else {
171 request_query.via
172 };
173
174 let body: RequestBody = serde_json::from_slice(request.body().as_ref())?;
175
176 Ok(Self {
177 room_id_or_alias,
178 reason: body.reason,
179 third_party_signed: body.third_party_signed,
180 via,
181 })
182 }
183 }
184
185 #[response(error = crate::Error)]
187 pub struct Response {
188 pub room_id: OwnedRoomId,
190 }
191
192 impl Request {
193 pub fn new(room_id_or_alias: OwnedRoomOrAliasId) -> Self {
195 Self { room_id_or_alias, via: vec![], third_party_signed: None, reason: None }
196 }
197 }
198
199 impl Response {
200 pub fn new(room_id: OwnedRoomId) -> Self {
202 Self { room_id }
203 }
204 }
205
206 #[cfg(all(test, any(feature = "client", feature = "server")))]
207 mod tests {
208 use ruma_common::{
209 api::{
210 IncomingRequest as _, MatrixVersion, OutgoingRequest, SendAccessToken,
211 SupportedVersions,
212 },
213 owned_room_id, owned_server_name,
214 };
215
216 use super::Request;
217
218 #[cfg(feature = "client")]
219 #[test]
220 fn serialize_request_via_and_server_name() {
221 let mut req = Request::new(owned_room_id!("!foo:b.ar").into());
222 req.via = vec![owned_server_name!("f.oo")];
223 let supported = SupportedVersions {
224 versions: [MatrixVersion::V1_1].into(),
225 features: Default::default(),
226 };
227
228 let req = req
229 .try_into_http_request::<Vec<u8>>(
230 "https://matrix.org",
231 SendAccessToken::IfRequired("tok"),
232 &supported,
233 )
234 .unwrap();
235 assert_eq!(req.uri().query(), Some("via=f.oo&server_name=f.oo"));
236 }
237
238 #[cfg(feature = "client")]
239 #[test]
240 fn serialize_request_only_via() {
241 let mut req = Request::new(owned_room_id!("!foo:b.ar").into());
242 req.via = vec![owned_server_name!("f.oo")];
243 let supported = SupportedVersions {
244 versions: [MatrixVersion::V1_13].into(),
245 features: Default::default(),
246 };
247
248 let req = req
249 .try_into_http_request::<Vec<u8>>(
250 "https://matrix.org",
251 SendAccessToken::IfRequired("tok"),
252 &supported,
253 )
254 .unwrap();
255 assert_eq!(req.uri().query(), Some("via=f.oo"));
256 }
257
258 #[cfg(feature = "server")]
259 #[test]
260 fn deserialize_request_wrong_method() {
261 Request::try_from_http_request(
262 http::Request::builder()
263 .method(http::Method::GET)
264 .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo")
265 .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
266 .unwrap(),
267 &["!foo:b.ar"],
268 )
269 .expect_err("Should not deserialize request with illegal method");
270 }
271
272 #[cfg(feature = "server")]
273 #[test]
274 fn deserialize_request_only_via() {
275 let req = Request::try_from_http_request(
276 http::Request::builder()
277 .method(http::Method::POST)
278 .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo")
279 .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
280 .unwrap(),
281 &["!foo:b.ar"],
282 )
283 .unwrap();
284
285 assert_eq!(req.room_id_or_alias, "!foo:b.ar");
286 assert_eq!(req.reason, Some("Let me in already!".to_owned()));
287 assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
288 }
289
290 #[cfg(feature = "server")]
291 #[test]
292 fn deserialize_request_only_server_name() {
293 let req = Request::try_from_http_request(
294 http::Request::builder()
295 .method(http::Method::POST)
296 .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?server_name=f.oo")
297 .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
298 .unwrap(),
299 &["!foo:b.ar"],
300 )
301 .unwrap();
302
303 assert_eq!(req.room_id_or_alias, "!foo:b.ar");
304 assert_eq!(req.reason, Some("Let me in already!".to_owned()));
305 assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
306 }
307
308 #[cfg(feature = "server")]
309 #[test]
310 fn deserialize_request_via_and_server_name() {
311 let req = Request::try_from_http_request(
312 http::Request::builder()
313 .method(http::Method::POST)
314 .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo&server_name=b.ar")
315 .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
316 .unwrap(),
317 &["!foo:b.ar"],
318 )
319 .unwrap();
320
321 assert_eq!(req.room_id_or_alias, "!foo:b.ar");
322 assert_eq!(req.reason, Some("Let me in already!".to_owned()));
323 assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
324 }
325 }
326}