client-api: Add future endpoints
This commit is contained in:
parent
e5a370f7e5
commit
862be071d2
@ -20,6 +20,10 @@ Improvements:
|
||||
- Stabilize support for animated thumbnails, according to Matrix 1.11
|
||||
- Add support for terms of service at registration, according to MSC1692 /
|
||||
Matrix 1.11
|
||||
- Add unstable support for [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)
|
||||
to send `Future` events and update `Future` events with `future_tokens`.
|
||||
(`Future` events are scheduled messages that can be controlled
|
||||
with `future_tokens` to send on demand or restart the timeout)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
|
@ -50,6 +50,7 @@ unstable-msc3843 = []
|
||||
unstable-msc3983 = []
|
||||
unstable-msc4108 = []
|
||||
unstable-msc4121 = []
|
||||
unstable-msc4140 = []
|
||||
|
||||
[dependencies]
|
||||
as_variant = { workspace = true }
|
||||
|
38
crates/ruma-client-api/src/future.rs
Normal file
38
crates/ruma-client-api/src/future.rs
Normal file
@ -0,0 +1,38 @@
|
||||
//! Endpoints for sending and receiving futures
|
||||
|
||||
pub mod send_future_message_event;
|
||||
pub mod send_future_state_event;
|
||||
pub mod update_future;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use web_time::Duration;
|
||||
|
||||
/// The query parameters for a future request.
|
||||
/// It can contain the possible timeout and the future_group_id combinations.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(untagged)]
|
||||
pub enum FutureParameters {
|
||||
/// Only sending the timeout creates a timeout future with a new (server generated)
|
||||
/// group id. The optional group id is used to create a secondary timeout.
|
||||
/// In a future group with two timeouts only one of them will ever be sent.
|
||||
Timeout {
|
||||
/// The timeout duration for this Future.
|
||||
#[serde(with = "ruma_common::serde::duration::ms")]
|
||||
#[serde(rename = "future_timeout")]
|
||||
timeout: Duration,
|
||||
/// The associated group for this Future.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(rename = "future_group_id")]
|
||||
group_id: Option<String>,
|
||||
},
|
||||
|
||||
/// Adds an additional action to a future without a timeout but requires a future group_id.
|
||||
/// A possible matrix event that this future group can resolve to. It can be sent by using the
|
||||
/// send_token as an alternative to the timeout future of an already existing group.
|
||||
Action {
|
||||
/// The associated group for this Future.
|
||||
#[serde(rename = "future_group_id")]
|
||||
group_id: String,
|
||||
},
|
||||
}
|
182
crates/ruma-client-api/src/future/send_future_message_event.rs
Normal file
182
crates/ruma-client-api/src/future/send_future_message_event.rs
Normal file
@ -0,0 +1,182 @@
|
||||
//! `PUT /_matrix/client/*/rooms/{roomId}/send_future/{eventType}/{txnId}`
|
||||
//!
|
||||
//! Send a future (a scheduled message) to a room. [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)
|
||||
|
||||
pub mod unstable {
|
||||
//! `msc4140` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140
|
||||
|
||||
use ruma_common::{
|
||||
api::{request, response, Metadata},
|
||||
metadata,
|
||||
serde::Raw,
|
||||
OwnedRoomId, OwnedTransactionId,
|
||||
};
|
||||
use ruma_events::{AnyMessageLikeEventContent, MessageLikeEventContent, MessageLikeEventType};
|
||||
use serde_json::value::to_raw_value as to_raw_json_value;
|
||||
|
||||
use crate::future::FutureParameters;
|
||||
|
||||
const METADATA: Metadata = metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/org.matrix.msc4140/rooms/:room_id/send_future/:event_type/:txn_id",
|
||||
}
|
||||
};
|
||||
/// Request type for the [`send_future_message_event`](crate::future::send_future_message_event)
|
||||
/// endpoint.
|
||||
#[request(error = crate::Error)]
|
||||
pub struct Request {
|
||||
/// The room to send the event to.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// The type of event to send.
|
||||
#[ruma_api(path)]
|
||||
pub event_type: MessageLikeEventType,
|
||||
|
||||
/// The transaction ID for this event.
|
||||
///
|
||||
/// Clients should generate a unique ID across requests within the
|
||||
/// same session. A session is identified by an access token, and
|
||||
/// persists when the [access token is refreshed].
|
||||
///
|
||||
/// It will be used by the server to ensure idempotency of requests.
|
||||
///
|
||||
/// [access token is refreshed]: https://spec.matrix.org/latest/client-server-api/#refreshing-access-tokens
|
||||
#[ruma_api(path)]
|
||||
pub txn_id: OwnedTransactionId,
|
||||
|
||||
/// Additional parameters to describe sending a future.
|
||||
///
|
||||
/// Only three combinations for `future_timeout` and `future_group_id` are possible.
|
||||
/// The enum [`FutureParameters`] enforces this.
|
||||
#[ruma_api(query_all)]
|
||||
pub future_parameters: FutureParameters,
|
||||
|
||||
/// The event content to send.
|
||||
#[ruma_api(body)]
|
||||
pub body: Raw<AnyMessageLikeEventContent>,
|
||||
}
|
||||
|
||||
/// Response type for the
|
||||
/// [`send_future_message_event`](crate::future::send_future_message_event) endpoint.
|
||||
#[response(error = crate::Error)]
|
||||
pub struct Response {
|
||||
/// A token to send/insert the future into the DAG.
|
||||
pub send_token: String,
|
||||
/// A token to cancel this future. It will never be send if this is called.
|
||||
pub cancel_token: String,
|
||||
/// The `future_group_id` generated for this future. Used to connect multiple futures
|
||||
/// only one of the connected futures will be sent and inserted into the DAG.
|
||||
pub future_group_id: String,
|
||||
/// A token used to refresh the timer of the future. This allows
|
||||
/// to implement heartbeat like capabilities. An event is only sent once
|
||||
/// a refresh in the timeout interval is missed.
|
||||
///
|
||||
/// If the future does not have a timeout this will be `None`.
|
||||
pub refresh_token: Option<String>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given room id, transaction id future_parameters and
|
||||
/// event content.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Since `Request` stores the request body in serialized form, this function can fail if
|
||||
/// `T`s [`::serde::Serialize`] implementation can fail.
|
||||
pub fn new<T>(
|
||||
room_id: OwnedRoomId,
|
||||
txn_id: OwnedTransactionId,
|
||||
future_parameters: FutureParameters,
|
||||
content: &T,
|
||||
) -> serde_json::Result<Self>
|
||||
where
|
||||
T: MessageLikeEventContent,
|
||||
{
|
||||
Ok(Self {
|
||||
room_id,
|
||||
txn_id,
|
||||
event_type: content.event_type(),
|
||||
future_parameters,
|
||||
body: Raw::from_json(to_raw_json_value(content)?),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new `Request` with the given room id, transaction id, event type,
|
||||
/// future parameters and raw event content.
|
||||
pub fn new_raw(
|
||||
room_id: OwnedRoomId,
|
||||
txn_id: OwnedTransactionId,
|
||||
event_type: MessageLikeEventType,
|
||||
future_parameters: FutureParameters,
|
||||
body: Raw<AnyMessageLikeEventContent>,
|
||||
) -> Self {
|
||||
Self { room_id, event_type, txn_id, future_parameters, body }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the tokens required to control the future using the
|
||||
/// [`crate::future::update_future::unstable::Request`] request.
|
||||
pub fn new(
|
||||
send_token: String,
|
||||
cancel_token: String,
|
||||
future_group_id: String,
|
||||
refresh_token: Option<String>,
|
||||
) -> Self {
|
||||
Self { send_token, cancel_token, future_group_id, refresh_token }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "client"))]
|
||||
mod tests {
|
||||
use ruma_common::{
|
||||
api::{MatrixVersion, OutgoingRequest, SendAccessToken},
|
||||
owned_room_id,
|
||||
};
|
||||
use ruma_events::room::message::RoomMessageEventContent;
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
use web_time::Duration;
|
||||
|
||||
use super::Request;
|
||||
use crate::future::send_future_message_event::unstable::FutureParameters;
|
||||
|
||||
#[test]
|
||||
fn serialize_message_future_request() {
|
||||
let room_id = owned_room_id!("!roomid:example.org");
|
||||
|
||||
let req = Request::new(
|
||||
room_id,
|
||||
"1234".into(),
|
||||
FutureParameters::Timeout {
|
||||
timeout: Duration::from_millis(103),
|
||||
group_id: Some("testId".to_owned()),
|
||||
},
|
||||
&RoomMessageEventContent::text_plain("test"),
|
||||
)
|
||||
.unwrap();
|
||||
let request: http::Request<Vec<u8>> = req
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_1],
|
||||
)
|
||||
.unwrap();
|
||||
let (parts, body) = request.into_parts();
|
||||
assert_eq!(
|
||||
"https://homeserver.tld/_matrix/client/unstable/org.matrix.msc4140/rooms/!roomid:example.org/send_future/m.room.message/1234?future_timeout=103&future_group_id=testId",
|
||||
parts.uri.to_string()
|
||||
);
|
||||
assert_eq!("PUT", parts.method.to_string());
|
||||
assert_eq!(
|
||||
json!({"msgtype":"m.text","body":"test"}),
|
||||
serde_json::from_str::<JsonValue>(std::str::from_utf8(&body).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
175
crates/ruma-client-api/src/future/send_future_state_event.rs
Normal file
175
crates/ruma-client-api/src/future/send_future_state_event.rs
Normal file
@ -0,0 +1,175 @@
|
||||
//! `PUT /_matrix/client/*/rooms/{roomId}/state_future/{eventType}/{txnId}`
|
||||
//!
|
||||
//! Send a future state (a scheduled state event) to a room. [MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)
|
||||
|
||||
pub mod unstable {
|
||||
//! `msc4140` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140
|
||||
|
||||
use ruma_common::{
|
||||
api::{request, response, Metadata},
|
||||
metadata,
|
||||
serde::Raw,
|
||||
OwnedRoomId,
|
||||
};
|
||||
use ruma_events::{AnyStateEventContent, StateEventContent, StateEventType};
|
||||
use serde_json::value::to_raw_value as to_raw_json_value;
|
||||
|
||||
use crate::future::FutureParameters;
|
||||
|
||||
const METADATA: Metadata = metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/org.matrix.msc4140/rooms/:room_id/state_future/:event_type/:state_key",
|
||||
}
|
||||
};
|
||||
|
||||
/// Request type for the [`send_future_state_event`](crate::future::send_future_state_event)
|
||||
/// endpoint.
|
||||
#[request(error = crate::Error)]
|
||||
pub struct Request {
|
||||
/// The room to send the event to.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// The type of event to send.
|
||||
#[ruma_api(path)]
|
||||
pub event_type: StateEventType,
|
||||
|
||||
/// The state_key for the state to send.
|
||||
#[ruma_api(path)]
|
||||
pub state_key: String,
|
||||
|
||||
/// Additional parameters to describe sending a future.
|
||||
///
|
||||
/// Only three combinations for `future_timeout` and `future_group_id` are possible.
|
||||
/// The enum [`FutureParameters`] enforces this.
|
||||
#[ruma_api(query_all)]
|
||||
pub future_parameters: FutureParameters,
|
||||
|
||||
/// The event content to send.
|
||||
#[ruma_api(body)]
|
||||
pub body: Raw<AnyStateEventContent>,
|
||||
}
|
||||
|
||||
/// Response type for the [`send_future_state_event`](crate::future::send_future_state_event)
|
||||
/// endpoint.
|
||||
#[response(error = crate::Error)]
|
||||
pub struct Response {
|
||||
/// A token to send/insert the future into the DAG.
|
||||
pub send_token: String,
|
||||
/// A token to cancel this future. It will never be send if this is called.
|
||||
pub cancel_token: String,
|
||||
/// The `future_group_id` generated for this future. Used to connect multiple futures
|
||||
/// only one of the connected futures will be sent and inserted into the DAG.
|
||||
pub future_group_id: String,
|
||||
/// A token used to refresh the timer of the future. This allows
|
||||
/// to implement heardbeat like capabilities. An event is only send once
|
||||
/// a refresh in the timeout interval is missed.
|
||||
///
|
||||
/// If the future does not have a timeout this will be `None`.
|
||||
pub refresh_token: Option<String>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given room id, state_key future_parameters and
|
||||
/// event content.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Since `Request` stores the request body in serialized form, this function can fail if
|
||||
/// `T`s [`::serde::Serialize`] implementation can fail.
|
||||
pub fn new<T>(
|
||||
room_id: OwnedRoomId,
|
||||
state_key: String,
|
||||
future_parameters: FutureParameters,
|
||||
content: &T,
|
||||
) -> serde_json::Result<Self>
|
||||
where
|
||||
T: StateEventContent,
|
||||
{
|
||||
Ok(Self {
|
||||
room_id,
|
||||
state_key,
|
||||
event_type: content.event_type(),
|
||||
future_parameters,
|
||||
body: Raw::from_json(to_raw_json_value(content)?),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new `Request` with the given room id, event type, state key,
|
||||
/// future parameters and raw event content.
|
||||
pub fn new_raw(
|
||||
room_id: OwnedRoomId,
|
||||
state_key: String,
|
||||
event_type: StateEventType,
|
||||
future_parameters: FutureParameters,
|
||||
body: Raw<AnyStateEventContent>,
|
||||
) -> Self {
|
||||
Self { room_id, event_type, state_key, body, future_parameters }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the tokens required to control the future using the
|
||||
/// [`crate::future::update_future::unstable::Request`] request.
|
||||
pub fn new(
|
||||
send_token: String,
|
||||
cancel_token: String,
|
||||
future_group_id: String,
|
||||
refresh_token: Option<String>,
|
||||
) -> Self {
|
||||
Self { send_token, cancel_token, future_group_id, refresh_token }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "client"))]
|
||||
mod tests {
|
||||
use ruma_common::{
|
||||
api::{MatrixVersion, OutgoingRequest, SendAccessToken},
|
||||
owned_room_id,
|
||||
};
|
||||
use ruma_events::room::topic::RoomTopicEventContent;
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
use web_time::Duration;
|
||||
|
||||
use super::Request;
|
||||
use crate::future::FutureParameters;
|
||||
|
||||
#[test]
|
||||
fn serialize_state_future_request() {
|
||||
let room_id = owned_room_id!("!roomid:example.org");
|
||||
|
||||
let req = Request::new(
|
||||
room_id,
|
||||
"@userAsStateKey:example.org".to_owned(),
|
||||
FutureParameters::Timeout {
|
||||
timeout: Duration::from_millis(1_234_321),
|
||||
group_id: Some("abs1abs1abs1abs1".to_owned()),
|
||||
},
|
||||
&RoomTopicEventContent::new("my_topic".to_owned()),
|
||||
)
|
||||
.unwrap();
|
||||
let request: http::Request<Vec<u8>> = req
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_1],
|
||||
)
|
||||
.unwrap();
|
||||
let (parts, body) = request.into_parts();
|
||||
assert_eq!(
|
||||
"https://homeserver.tld/_matrix/client/unstable/org.matrix.msc4140/rooms/!roomid:example.org/state_future/m.room.topic/@userAsStateKey:example.org?future_timeout=1234321&future_group_id=abs1abs1abs1abs1",
|
||||
parts.uri.to_string()
|
||||
);
|
||||
assert_eq!("PUT", parts.method.to_string());
|
||||
assert_eq!(
|
||||
json!({"topic": "my_topic"}),
|
||||
serde_json::from_str::<JsonValue>(std::str::from_utf8(&body).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
49
crates/ruma-client-api/src/future/update_future.rs
Normal file
49
crates/ruma-client-api/src/future/update_future.rs
Normal file
@ -0,0 +1,49 @@
|
||||
//! `POST /_matrix/client/*/futures/{token}`
|
||||
//!
|
||||
//! Send a future token to update/cancel/send the associated future event.
|
||||
|
||||
pub mod unstable {
|
||||
//! `msc3814` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140
|
||||
|
||||
use ruma_common::{
|
||||
api::{request, response, Metadata},
|
||||
metadata,
|
||||
};
|
||||
|
||||
const METADATA: Metadata = metadata! {
|
||||
method: POST,
|
||||
rate_limited: true,
|
||||
authentication: None,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/org.matrix.msc4140/future/:token",
|
||||
}
|
||||
};
|
||||
|
||||
/// Request type for the [`update_future`](crate::future::update_future) endpoint.
|
||||
#[request(error = crate::Error)]
|
||||
pub struct Request {
|
||||
/// The token.
|
||||
#[ruma_api(path)]
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` to update a future. This is an unauthenticated request and only
|
||||
/// requires the future token.
|
||||
pub fn new(token: String) -> serde_json::Result<Self> {
|
||||
Ok(Self { token })
|
||||
}
|
||||
}
|
||||
|
||||
/// Response type for the [`update_future`](crate::future::update_future) endpoint.
|
||||
#[response(error = crate::Error)]
|
||||
pub struct Response {}
|
||||
impl Response {
|
||||
/// Creates a new response for the [`update_future`](crate::future::update_future) endpoint.
|
||||
pub fn new() -> Self {
|
||||
Response {}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@ pub mod directory;
|
||||
pub mod discovery;
|
||||
pub mod error;
|
||||
pub mod filter;
|
||||
#[cfg(feature = "unstable-msc4140")]
|
||||
pub mod future;
|
||||
pub mod http_headers;
|
||||
pub mod keys;
|
||||
pub mod knock;
|
||||
|
@ -227,6 +227,7 @@ unstable-msc4075 = ["ruma-events?/unstable-msc4075"]
|
||||
unstable-msc4108 = ["ruma-client-api?/unstable-msc4108"]
|
||||
unstable-msc4121 = ["ruma-client-api?/unstable-msc4121"]
|
||||
unstable-msc4125 = ["ruma-federation-api?/unstable-msc4125"]
|
||||
unstable-msc4140 = ["ruma-client-api?/unstable-msc4140"]
|
||||
unstable-pdu = ["ruma-events?/unstable-pdu"]
|
||||
unstable-unspecified = [
|
||||
"ruma-common/unstable-unspecified",
|
||||
@ -280,6 +281,7 @@ __ci = [
|
||||
"unstable-msc4108",
|
||||
"unstable-msc4121",
|
||||
"unstable-msc4125",
|
||||
"unstable-msc4140"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
Loading…
x
Reference in New Issue
Block a user