client-api: Replace server_name with via in knock_room and join_room_by_id_or_alias
According to MSC4156 Co-authored-by: Kévin Commaille <76261501+zecakeh@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									8f288cc1f2
								
							
						
					
					
						commit
						0c00b90b22
					
				@ -10,6 +10,8 @@ Breaking changes:
 | 
				
			|||||||
- The `content_disposition` fields of `media::get_content::v3::Response` and
 | 
					- The `content_disposition` fields of `media::get_content::v3::Response` and
 | 
				
			||||||
  `media::get_content_as_filename::v3::Response` use now the strongly typed
 | 
					  `media::get_content_as_filename::v3::Response` use now the strongly typed
 | 
				
			||||||
  `ContentDisposition` instead of strings.
 | 
					  `ContentDisposition` instead of strings.
 | 
				
			||||||
 | 
					- Replace `server_name` on `knock::knock_room::v3::Request` and 
 | 
				
			||||||
 | 
					  `membership::join_room_by_id_or_alias::v3::Request` with `via` as per MSC4156.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Improvements:
 | 
					Improvements:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ pub mod v3 {
 | 
				
			|||||||
    //! [spec]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3knockroomidoralias
 | 
					    //! [spec]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3knockroomidoralias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use ruma_common::{
 | 
					    use ruma_common::{
 | 
				
			||||||
        api::{request, response, Metadata},
 | 
					        api::{response, Metadata},
 | 
				
			||||||
        metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName,
 | 
					        metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,22 +23,137 @@ pub mod v3 {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Request type for the `knock_room` endpoint.
 | 
					    /// Request type for the `knock_room` endpoint.
 | 
				
			||||||
    #[request(error = crate::Error)]
 | 
					    #[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					    #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
 | 
				
			||||||
    pub struct Request {
 | 
					    pub struct Request {
 | 
				
			||||||
        /// The room the user should knock on.
 | 
					        /// The room the user should knock on.
 | 
				
			||||||
        #[ruma_api(path)]
 | 
					 | 
				
			||||||
        pub room_id_or_alias: OwnedRoomOrAliasId,
 | 
					        pub room_id_or_alias: OwnedRoomOrAliasId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// The reason for joining a room.
 | 
					        /// The reason for joining a room.
 | 
				
			||||||
        #[serde(skip_serializing_if = "Option::is_none")]
 | 
					 | 
				
			||||||
        pub reason: Option<String>,
 | 
					        pub reason: Option<String>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// The servers to attempt to knock on the room through.
 | 
					        /// The servers to attempt to knock on the room through.
 | 
				
			||||||
        ///
 | 
					        ///
 | 
				
			||||||
        /// One of the servers must be participating in the room.
 | 
					        /// One of the servers must be participating in the room.
 | 
				
			||||||
        #[ruma_api(query)]
 | 
					        ///
 | 
				
			||||||
 | 
					        /// When serializing, this field is mapped to both `server_name` and `via`
 | 
				
			||||||
 | 
					        /// with identical values.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// When deserializing, the value is read from `via` if it's not missing or
 | 
				
			||||||
 | 
					        /// empty and `server_name` otherwise.
 | 
				
			||||||
 | 
					        pub via: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Data in the request's query string.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "client", derive(serde::Serialize))]
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
 | 
				
			||||||
 | 
					    struct RequestQuery {
 | 
				
			||||||
 | 
					        /// The servers to attempt to knock on the room through.
 | 
				
			||||||
        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
					        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
				
			||||||
        pub server_name: Vec<OwnedServerName>,
 | 
					        via: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// The servers to attempt to knock on the room through.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// Deprecated in Matrix >1.11 in favour of `via`.
 | 
				
			||||||
 | 
					        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
				
			||||||
 | 
					        server_name: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Data in the request's body.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "client", derive(serde::Serialize))]
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
 | 
				
			||||||
 | 
					    struct RequestBody {
 | 
				
			||||||
 | 
					        /// The reason for joining a room.
 | 
				
			||||||
 | 
					        #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
 | 
					        reason: Option<String>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "client")]
 | 
				
			||||||
 | 
					    impl ruma_common::api::OutgoingRequest for Request {
 | 
				
			||||||
 | 
					        type EndpointError = crate::Error;
 | 
				
			||||||
 | 
					        type IncomingResponse = Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const METADATA: Metadata = METADATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn try_into_http_request<T: Default + bytes::BufMut>(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            base_url: &str,
 | 
				
			||||||
 | 
					            access_token: ruma_common::api::SendAccessToken<'_>,
 | 
				
			||||||
 | 
					            considering_versions: &'_ [ruma_common::api::MatrixVersion],
 | 
				
			||||||
 | 
					        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
 | 
				
			||||||
 | 
					            use http::header::{self, HeaderValue};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let query_string = serde_html_form::to_string(RequestQuery {
 | 
				
			||||||
 | 
					                server_name: self.via.clone(),
 | 
				
			||||||
 | 
					                via: self.via,
 | 
				
			||||||
 | 
					            })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let http_request = http::Request::builder()
 | 
				
			||||||
 | 
					                .method(METADATA.method)
 | 
				
			||||||
 | 
					                .uri(METADATA.make_endpoint_url(
 | 
				
			||||||
 | 
					                    considering_versions,
 | 
				
			||||||
 | 
					                    base_url,
 | 
				
			||||||
 | 
					                    &[&self.room_id_or_alias],
 | 
				
			||||||
 | 
					                    &query_string,
 | 
				
			||||||
 | 
					                )?)
 | 
				
			||||||
 | 
					                .header(header::CONTENT_TYPE, "application/json")
 | 
				
			||||||
 | 
					                .header(
 | 
				
			||||||
 | 
					                    header::AUTHORIZATION,
 | 
				
			||||||
 | 
					                    HeaderValue::from_str(&format!(
 | 
				
			||||||
 | 
					                        "Bearer {}",
 | 
				
			||||||
 | 
					                        access_token
 | 
				
			||||||
 | 
					                            .get_required_for_endpoint()
 | 
				
			||||||
 | 
					                            .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?
 | 
				
			||||||
 | 
					                    ))?,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .body(ruma_common::serde::json_to_buf(&RequestBody { reason: self.reason })?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(http_request)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					    impl ruma_common::api::IncomingRequest for Request {
 | 
				
			||||||
 | 
					        type EndpointError = crate::Error;
 | 
				
			||||||
 | 
					        type OutgoingResponse = Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const METADATA: Metadata = METADATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn try_from_http_request<B, S>(
 | 
				
			||||||
 | 
					            request: http::Request<B>,
 | 
				
			||||||
 | 
					            path_args: &[S],
 | 
				
			||||||
 | 
					        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
 | 
				
			||||||
 | 
					        where
 | 
				
			||||||
 | 
					            B: AsRef<[u8]>,
 | 
				
			||||||
 | 
					            S: AsRef<str>,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if request.method() != METADATA.method {
 | 
				
			||||||
 | 
					                return Err(ruma_common::api::error::FromHttpRequestError::MethodMismatch {
 | 
				
			||||||
 | 
					                    expected: METADATA.method,
 | 
				
			||||||
 | 
					                    received: request.method().clone(),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let (room_id_or_alias,) =
 | 
				
			||||||
 | 
					                serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
 | 
				
			||||||
 | 
					                    _,
 | 
				
			||||||
 | 
					                    serde::de::value::Error,
 | 
				
			||||||
 | 
					                >::new(
 | 
				
			||||||
 | 
					                    path_args.iter().map(::std::convert::AsRef::as_ref),
 | 
				
			||||||
 | 
					                ))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let request_query: RequestQuery =
 | 
				
			||||||
 | 
					                serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
 | 
				
			||||||
 | 
					            let via = if request_query.via.is_empty() {
 | 
				
			||||||
 | 
					                request_query.server_name
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                request_query.via
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let body: RequestBody = serde_json::from_slice(request.body().as_ref())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(Self { room_id_or_alias, reason: body.reason, via })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Response type for the `knock_room` endpoint.
 | 
					    /// Response type for the `knock_room` endpoint.
 | 
				
			||||||
@ -51,7 +166,7 @@ pub mod v3 {
 | 
				
			|||||||
    impl Request {
 | 
					    impl Request {
 | 
				
			||||||
        /// Creates a new `Request` with the given room ID or alias.
 | 
					        /// Creates a new `Request` with the given room ID or alias.
 | 
				
			||||||
        pub fn new(room_id_or_alias: OwnedRoomOrAliasId) -> Self {
 | 
					        pub fn new(room_id_or_alias: OwnedRoomOrAliasId) -> Self {
 | 
				
			||||||
            Self { room_id_or_alias, reason: None, server_name: vec![] }
 | 
					            Self { room_id_or_alias, reason: None, via: vec![] }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -61,4 +176,97 @@ pub mod v3 {
 | 
				
			|||||||
            Self { room_id }
 | 
					            Self { room_id }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(all(test, any(feature = "client", feature = "server")))]
 | 
				
			||||||
 | 
					    mod tests {
 | 
				
			||||||
 | 
					        use ruma_common::{
 | 
				
			||||||
 | 
					            api::{IncomingRequest as _, MatrixVersion, OutgoingRequest, SendAccessToken},
 | 
				
			||||||
 | 
					            owned_room_id, owned_server_name,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        use super::Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "client")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn serialize_request() {
 | 
				
			||||||
 | 
					            let mut req = Request::new(owned_room_id!("!foo:b.ar").into());
 | 
				
			||||||
 | 
					            req.via = vec![owned_server_name!("f.oo")];
 | 
				
			||||||
 | 
					            let req = req
 | 
				
			||||||
 | 
					                .try_into_http_request::<Vec<u8>>(
 | 
				
			||||||
 | 
					                    "https://matrix.org",
 | 
				
			||||||
 | 
					                    SendAccessToken::IfRequired("tok"),
 | 
				
			||||||
 | 
					                    &[MatrixVersion::V1_1],
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .unwrap();
 | 
				
			||||||
 | 
					            assert_eq!(req.uri().query(), Some("via=f.oo&server_name=f.oo"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_wrong_method() {
 | 
				
			||||||
 | 
					            Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::GET)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/knock/!foo:b.ar?via=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .expect_err("Should not deserialize request with illegal method");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_only_via() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/knock/!foo:b.ar?via=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_only_server_name() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/knock/!foo:b.ar?server_name=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_via_and_server_name() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/knock/!foo:b.ar?via=f.oo&server_name=b.ar")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ pub mod v3 {
 | 
				
			|||||||
    //! [spec]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3joinroomidoralias
 | 
					    //! [spec]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3joinroomidoralias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use ruma_common::{
 | 
					    use ruma_common::{
 | 
				
			||||||
        api::{request, response, Metadata},
 | 
					        api::{response, Metadata},
 | 
				
			||||||
        metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName,
 | 
					        metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,27 +25,154 @@ pub mod v3 {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Request type for the `join_room_by_id_or_alias` endpoint.
 | 
					    /// Request type for the `join_room_by_id_or_alias` endpoint.
 | 
				
			||||||
    #[request(error = crate::Error)]
 | 
					    #[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					    #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
 | 
				
			||||||
    pub struct Request {
 | 
					    pub struct Request {
 | 
				
			||||||
        /// The room where the user should be invited.
 | 
					        /// The room where the user should be invited.
 | 
				
			||||||
        #[ruma_api(path)]
 | 
					 | 
				
			||||||
        pub room_id_or_alias: OwnedRoomOrAliasId,
 | 
					        pub room_id_or_alias: OwnedRoomOrAliasId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// The signature of a `m.third_party_invite` token to prove that this user owns a third
 | 
				
			||||||
 | 
					        /// party identity which has been invited to the room.
 | 
				
			||||||
 | 
					        pub third_party_signed: Option<ThirdPartySigned>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Optional reason for joining the room.
 | 
				
			||||||
 | 
					        pub reason: Option<String>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// The servers to attempt to join the room through.
 | 
					        /// The servers to attempt to join the room through.
 | 
				
			||||||
        ///
 | 
					        ///
 | 
				
			||||||
        /// One of the servers must be participating in the room.
 | 
					        /// One of the servers must be participating in the room.
 | 
				
			||||||
        #[ruma_api(query)]
 | 
					        ///
 | 
				
			||||||
        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
					        /// When serializing, this field is mapped to both `server_name` and `via`
 | 
				
			||||||
        pub server_name: Vec<OwnedServerName>,
 | 
					        /// with identical values.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// When deserializing, the value is read from `via` if it's not missing or
 | 
				
			||||||
 | 
					        /// empty and `server_name` otherwise.
 | 
				
			||||||
 | 
					        pub via: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Data in the request's query string.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "client", derive(serde::Serialize))]
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
 | 
				
			||||||
 | 
					    struct RequestQuery {
 | 
				
			||||||
 | 
					        /// The servers to attempt to join the room through.
 | 
				
			||||||
 | 
					        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
				
			||||||
 | 
					        via: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// The servers to attempt to join the room through.
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// Deprecated in Matrix >1.11 in favour of `via`.
 | 
				
			||||||
 | 
					        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
 | 
				
			||||||
 | 
					        server_name: Vec<OwnedServerName>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Data in the request's body.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "client", derive(serde::Serialize))]
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
 | 
				
			||||||
 | 
					    struct RequestBody {
 | 
				
			||||||
        /// The signature of a `m.third_party_invite` token to prove that this user owns a third
 | 
					        /// The signature of a `m.third_party_invite` token to prove that this user owns a third
 | 
				
			||||||
        /// party identity which has been invited to the room.
 | 
					        /// party identity which has been invited to the room.
 | 
				
			||||||
        #[serde(skip_serializing_if = "Option::is_none")]
 | 
					        #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
        pub third_party_signed: Option<ThirdPartySigned>,
 | 
					        third_party_signed: Option<ThirdPartySigned>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Optional reason for joining the room.
 | 
					        /// Optional reason for joining the room.
 | 
				
			||||||
        #[serde(skip_serializing_if = "Option::is_none")]
 | 
					        #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
        pub reason: Option<String>,
 | 
					        reason: Option<String>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "client")]
 | 
				
			||||||
 | 
					    impl ruma_common::api::OutgoingRequest for Request {
 | 
				
			||||||
 | 
					        type EndpointError = crate::Error;
 | 
				
			||||||
 | 
					        type IncomingResponse = Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const METADATA: Metadata = METADATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn try_into_http_request<T: Default + bytes::BufMut>(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            base_url: &str,
 | 
				
			||||||
 | 
					            access_token: ruma_common::api::SendAccessToken<'_>,
 | 
				
			||||||
 | 
					            considering_versions: &'_ [ruma_common::api::MatrixVersion],
 | 
				
			||||||
 | 
					        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
 | 
				
			||||||
 | 
					            use http::header::{self, HeaderValue};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let query_string = serde_html_form::to_string(RequestQuery {
 | 
				
			||||||
 | 
					                server_name: self.via.clone(),
 | 
				
			||||||
 | 
					                via: self.via,
 | 
				
			||||||
 | 
					            })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let http_request = http::Request::builder()
 | 
				
			||||||
 | 
					                .method(METADATA.method)
 | 
				
			||||||
 | 
					                .uri(METADATA.make_endpoint_url(
 | 
				
			||||||
 | 
					                    considering_versions,
 | 
				
			||||||
 | 
					                    base_url,
 | 
				
			||||||
 | 
					                    &[&self.room_id_or_alias],
 | 
				
			||||||
 | 
					                    &query_string,
 | 
				
			||||||
 | 
					                )?)
 | 
				
			||||||
 | 
					                .header(header::CONTENT_TYPE, "application/json")
 | 
				
			||||||
 | 
					                .header(
 | 
				
			||||||
 | 
					                    header::AUTHORIZATION,
 | 
				
			||||||
 | 
					                    HeaderValue::from_str(&format!(
 | 
				
			||||||
 | 
					                        "Bearer {}",
 | 
				
			||||||
 | 
					                        access_token
 | 
				
			||||||
 | 
					                            .get_required_for_endpoint()
 | 
				
			||||||
 | 
					                            .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?
 | 
				
			||||||
 | 
					                    ))?,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .body(ruma_common::serde::json_to_buf(&RequestBody {
 | 
				
			||||||
 | 
					                    third_party_signed: self.third_party_signed,
 | 
				
			||||||
 | 
					                    reason: self.reason,
 | 
				
			||||||
 | 
					                })?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(http_request)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					    impl ruma_common::api::IncomingRequest for Request {
 | 
				
			||||||
 | 
					        type EndpointError = crate::Error;
 | 
				
			||||||
 | 
					        type OutgoingResponse = Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const METADATA: Metadata = METADATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn try_from_http_request<B, S>(
 | 
				
			||||||
 | 
					            request: http::Request<B>,
 | 
				
			||||||
 | 
					            path_args: &[S],
 | 
				
			||||||
 | 
					        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
 | 
				
			||||||
 | 
					        where
 | 
				
			||||||
 | 
					            B: AsRef<[u8]>,
 | 
				
			||||||
 | 
					            S: AsRef<str>,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if request.method() != METADATA.method {
 | 
				
			||||||
 | 
					                return Err(ruma_common::api::error::FromHttpRequestError::MethodMismatch {
 | 
				
			||||||
 | 
					                    expected: METADATA.method,
 | 
				
			||||||
 | 
					                    received: request.method().clone(),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let (room_id_or_alias,) =
 | 
				
			||||||
 | 
					                serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
 | 
				
			||||||
 | 
					                    _,
 | 
				
			||||||
 | 
					                    serde::de::value::Error,
 | 
				
			||||||
 | 
					                >::new(
 | 
				
			||||||
 | 
					                    path_args.iter().map(::std::convert::AsRef::as_ref),
 | 
				
			||||||
 | 
					                ))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let request_query: RequestQuery =
 | 
				
			||||||
 | 
					                serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
 | 
				
			||||||
 | 
					            let via = if request_query.via.is_empty() {
 | 
				
			||||||
 | 
					                request_query.server_name
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                request_query.via
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let body: RequestBody = serde_json::from_slice(request.body().as_ref())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(Self {
 | 
				
			||||||
 | 
					                room_id_or_alias,
 | 
				
			||||||
 | 
					                reason: body.reason,
 | 
				
			||||||
 | 
					                third_party_signed: body.third_party_signed,
 | 
				
			||||||
 | 
					                via,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Response type for the `join_room_by_id_or_alias` endpoint.
 | 
					    /// Response type for the `join_room_by_id_or_alias` endpoint.
 | 
				
			||||||
@ -58,7 +185,7 @@ pub mod v3 {
 | 
				
			|||||||
    impl Request {
 | 
					    impl Request {
 | 
				
			||||||
        /// Creates a new `Request` with the given room ID or alias ID.
 | 
					        /// Creates a new `Request` with the given room ID or alias ID.
 | 
				
			||||||
        pub fn new(room_id_or_alias: OwnedRoomOrAliasId) -> Self {
 | 
					        pub fn new(room_id_or_alias: OwnedRoomOrAliasId) -> Self {
 | 
				
			||||||
            Self { room_id_or_alias, server_name: vec![], third_party_signed: None, reason: None }
 | 
					            Self { room_id_or_alias, via: vec![], third_party_signed: None, reason: None }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -68,4 +195,97 @@ pub mod v3 {
 | 
				
			|||||||
            Self { room_id }
 | 
					            Self { room_id }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(all(test, any(feature = "client", feature = "server")))]
 | 
				
			||||||
 | 
					    mod tests {
 | 
				
			||||||
 | 
					        use ruma_common::{
 | 
				
			||||||
 | 
					            api::{IncomingRequest as _, MatrixVersion, OutgoingRequest, SendAccessToken},
 | 
				
			||||||
 | 
					            owned_room_id, owned_server_name,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        use super::Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "client")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn serialize_request() {
 | 
				
			||||||
 | 
					            let mut req = Request::new(owned_room_id!("!foo:b.ar").into());
 | 
				
			||||||
 | 
					            req.via = vec![owned_server_name!("f.oo")];
 | 
				
			||||||
 | 
					            let req = req
 | 
				
			||||||
 | 
					                .try_into_http_request::<Vec<u8>>(
 | 
				
			||||||
 | 
					                    "https://matrix.org",
 | 
				
			||||||
 | 
					                    SendAccessToken::IfRequired("tok"),
 | 
				
			||||||
 | 
					                    &[MatrixVersion::V1_1],
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .unwrap();
 | 
				
			||||||
 | 
					            assert_eq!(req.uri().query(), Some("via=f.oo&server_name=f.oo"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_wrong_method() {
 | 
				
			||||||
 | 
					            Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::GET)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .expect_err("Should not deserialize request with illegal method");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_only_via() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_only_server_name() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?server_name=f.oo")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "server")]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn deserialize_request_via_and_server_name() {
 | 
				
			||||||
 | 
					            let req = Request::try_from_http_request(
 | 
				
			||||||
 | 
					                http::Request::builder()
 | 
				
			||||||
 | 
					                    .method(http::Method::POST)
 | 
				
			||||||
 | 
					                    .uri("https://matrix.org/_matrix/client/v3/join/!foo:b.ar?via=f.oo&server_name=b.ar")
 | 
				
			||||||
 | 
					                    .body(b"{ \"reason\": \"Let me in already!\" }" as &[u8])
 | 
				
			||||||
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                &["!foo:b.ar"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(req.room_id_or_alias, "!foo:b.ar");
 | 
				
			||||||
 | 
					            assert_eq!(req.reason, Some("Let me in already!".to_owned()));
 | 
				
			||||||
 | 
					            assert_eq!(req.via, vec![owned_server_name!("f.oo")]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user