Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/ruma-client-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Breaking changes:
- `UiaaInfo`s field `auth_error` from `Option<StandardErrorBody>` to `Option<Box<StandardErrorBody>>`
(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:

Expand Down
96 changes: 88 additions & 8 deletions crates/ruma-client-api/src/dehydrated_device/get_events.rs
Original file line number Diff line number Diff line change
@@ -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])
//!
Expand All @@ -16,7 +18,7 @@ pub mod unstable {
use ruma_events::AnyToDeviceEvent;

metadata! {
method: POST,
method: GET,
rate_limited: false,
authentication: AccessToken,
history: {
Expand All @@ -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<String>,
pub from: Option<String>,
}

/// 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<String>,

/// Messages sent directly between devices.
Expand All @@ -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 }
}
}

Expand All @@ -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<Vec<u8>> = 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::<Vec<u8>>().unwrap();

assert_eq!(String::from_utf8_lossy(res.body()), r#"{"events":[]}"#);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String>,
}

/// 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<String>,

/// Messages sent directly between devices.
pub events: Vec<Raw<AnyToDeviceEvent>>,
}

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<Raw<AnyToDeviceEvent>>) -> Self {
Self { next_batch: None, events }
}
}