diff --git a/crates/ruma-client-api/CHANGELOG.md b/crates/ruma-client-api/CHANGELOG.md index 1310256395..d83193eb60 100644 --- a/crates/ruma-client-api/CHANGELOG.md +++ b/crates/ruma-client-api/CHANGELOG.md @@ -7,6 +7,10 @@ Breaking changes: - `UiaaInfo`s field `auth_error` from `Option` to `Option>` (this saves a couple bytes when the field is not set, and gets rid of a default-warn clippy lint that was showing up in ruma-client and matrix-sdk) +- `dehydrated_device::get_events::unstable` now matches the current state of MSC3814: it uses + `GET` with the batch token in the `from` query parameter, and the response omits `next_batch` + once there are no more events. The previous `POST` definition is available as + `dehydrated_device::get_events::unstable_v1`. Bug fixes: diff --git a/crates/ruma-client-api/src/dehydrated_device/get_events.rs b/crates/ruma-client-api/src/dehydrated_device/get_events.rs index 1117fd1508..12fbdc86e3 100644 --- a/crates/ruma-client-api/src/dehydrated_device/get_events.rs +++ b/crates/ruma-client-api/src/dehydrated_device/get_events.rs @@ -1,7 +1,9 @@ -//! `POST /_matrix/client/*/dehydrated_device/{device_id}/events` +//! `GET /_matrix/client/*/dehydrated_device/{device_id}/events` //! //! Get to-device events for a dehydrated device. +pub mod unstable_v1; + pub mod unstable { //! `msc3814` ([MSC]) //! @@ -16,7 +18,7 @@ pub mod unstable { use ruma_events::AnyToDeviceEvent; metadata! { - method: POST, + method: GET, rate_limited: false, authentication: AccessToken, history: { @@ -30,19 +32,24 @@ pub mod unstable { /// The unique ID of the device for which we would like to fetch events. #[ruma_api(path)] pub device_id: OwnedDeviceId, + /// A point in time to continue getting events from. /// /// Should be a token from the `next_batch` field of a previous `/events` - /// request. + /// response. + #[ruma_api(query)] #[serde(skip_serializing_if = "Option::is_none")] - pub next_batch: Option, + pub from: Option, } - /// Request type for the `dehydrated_device/{device_id}/events` endpoint. + /// Response type for the `dehydrated_device/{device_id}/events` endpoint. #[response] pub struct Response { - /// The batch token to supply in the `since` param of the next `/events` request. Will be - /// none if no further events can be found. + /// The batch token to supply in the `from` param of the next `/events` request. + /// + /// Omitted when there are no more events to retrieve. An empty `events` list does not + /// mean that all events have been retrieved; only the absence of this field does. + #[serde(skip_serializing_if = "Option::is_none")] pub next_batch: Option, /// Messages sent directly between devices. @@ -52,7 +59,7 @@ pub mod unstable { impl Request { /// Create a new request. pub fn new(device_id: OwnedDeviceId) -> Self { - Self { device_id, next_batch: None } + Self { device_id, from: None } } } @@ -62,4 +69,77 @@ pub mod unstable { Self { next_batch: None, events } } } + + #[cfg(all(test, feature = "client"))] + mod client_tests { + use std::borrow::Cow; + + use ruma_common::{ + api::{ + MatrixVersion, OutgoingRequestExt as _, SupportedVersions, + auth_scheme::SendAccessToken, + }, + owned_device_id, + }; + + use super::Request; + + #[test] + fn serialize_request_from_query_param() { + let supported = SupportedVersions { + versions: [MatrixVersion::V1_1].into(), + features: Default::default(), + }; + let mut request = Request::new(owned_device_id!("DEHYDRATED")); + request.from = Some("s123".into()); + + let req: http::Request> = request + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + Cow::Owned(supported), + ) + .unwrap(); + + assert_eq!(req.method(), http::Method::GET); + assert_eq!( + req.uri().path(), + "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/DEHYDRATED/events" + ); + assert_eq!(req.uri().query(), Some("from=s123")); + } + } + + #[cfg(all(test, feature = "server"))] + mod server_tests { + use ruma_common::api::{IncomingRequest as _, OutgoingResponse as _}; + + use super::{Request, Response}; + + #[test] + fn deserialize_request_from_query_param() { + let req = Request::try_from_http_request( + http::Request::builder() + .method(http::Method::GET) + .uri( + "https://matrix.org/_matrix/client/unstable/org.matrix.msc3814.v1\ + /dehydrated_device/DEHYDRATED/events?from=s123", + ) + .body(&[] as &[u8]) + .unwrap(), + &["DEHYDRATED"], + ) + .unwrap(); + + assert_eq!(req.device_id, "DEHYDRATED"); + assert_eq!(req.from.as_deref(), Some("s123")); + } + + #[test] + fn serialize_last_batch_without_next_batch() { + let res = Response::new(vec![]).try_into_http_response::>().unwrap(); + + assert_eq!(String::from_utf8_lossy(res.body()), r#"{"events":[]}"#); + } + } } diff --git a/crates/ruma-client-api/src/dehydrated_device/get_events/unstable_v1.rs b/crates/ruma-client-api/src/dehydrated_device/get_events/unstable_v1.rs new file mode 100644 index 0000000000..9cd1102093 --- /dev/null +++ b/crates/ruma-client-api/src/dehydrated_device/get_events/unstable_v1.rs @@ -0,0 +1,62 @@ +//! `msc3814` ([MSC]) +//! +//! Old endpoint definition from MSC 3814, which used `POST` with the batch token in the request +//! body. Does not correspond to the current state of the MSC. +//! +//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/blob/8f84545d5a56649ee1d56aaebc5cf71be749c3aa/proposals/3814-dehydrated-devices-with-ssss.md + +use ruma_common::{ + OwnedDeviceId, + api::{auth_scheme::AccessToken, request, response}, + metadata, + serde::Raw, +}; +use ruma_events::AnyToDeviceEvent; + +metadata! { + method: POST, + rate_limited: false, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events", + } +} + +/// Request type for the `dehydrated_device/{device_id}/events` endpoint. +#[request] +pub struct Request { + /// The unique ID of the device for which we would like to fetch events. + #[ruma_api(path)] + pub device_id: OwnedDeviceId, + /// A point in time to continue getting events from. + /// + /// Should be a token from the `next_batch` field of a previous `/events` + /// request. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_batch: Option, +} + +/// Request type for the `dehydrated_device/{device_id}/events` endpoint. +#[response] +pub struct Response { + /// The batch token to supply in the `since` param of the next `/events` request. Will be + /// none if no further events can be found. + pub next_batch: Option, + + /// Messages sent directly between devices. + pub events: Vec>, +} + +impl Request { + /// Create a new request. + pub fn new(device_id: OwnedDeviceId) -> Self { + Self { device_id, next_batch: None } + } +} + +impl Response { + /// Create a new response with the given events. + pub fn new(events: Vec>) -> Self { + Self { next_batch: None, events } + } +}