From 55652a87c5890673baf2b9676d88a729b4904c52 Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 25 Jul 2024 15:27:39 +0200 Subject: [PATCH] WidgetDriver: Add authenticated future update action --- Cargo.lock | 20 +++-- Cargo.toml | 4 +- bindings/matrix-sdk-ffi/src/widget.rs | 10 +++ crates/matrix-sdk/src/widget/capabilities.rs | 35 +++++++- .../src/widget/machine/driver_req.rs | 40 ++++++++- .../src/widget/machine/from_widget.rs | 68 ++++++--------- .../matrix-sdk/src/widget/machine/incoming.rs | 7 +- crates/matrix-sdk/src/widget/machine/mod.rs | 34 +++++++- .../src/widget/machine/tests/send_event.rs | 4 +- crates/matrix-sdk/src/widget/matrix.rs | 14 ++- crates/matrix-sdk/src/widget/mod.rs | 10 ++- crates/matrix-sdk/tests/integration/widget.rs | 85 +++++++++++++------ 12 files changed, 237 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 732eb842928..f262ce7b2c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5060,7 +5060,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "assign", "js_int", @@ -5077,7 +5077,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "as_variant", "assign", @@ -5100,7 +5100,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "as_variant", "base64 0.22.1", @@ -5132,7 +5132,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "as_variant", "indexmap 2.2.6", @@ -5157,9 +5157,11 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ + "http 1.1.0", "js_int", + "mime", "ruma-common", "ruma-events", "serde", @@ -5169,7 +5171,7 @@ dependencies = [ [[package]] name = "ruma-html" version = "0.2.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "as_variant", "html5ever", @@ -5181,7 +5183,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "js_int", "thiserror", @@ -5190,7 +5192,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "once_cell", "proc-macro-crate", @@ -5205,7 +5207,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://github.com/matrix-org/ruma?rev=f25b3220d0c3ece7720020ed180af4955a855402#f25b3220d0c3ece7720020ed180af4955a855402" +source = "git+https://github.com/toger5/ruma?rev=195cc6f57464b541d6ec0e8368c0993b3f62a760#195cc6f57464b541d6ec0e8368c0993b3f62a760" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index ffbaa20c7a2..515156f39cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ once_cell = "1.16.0" pin-project-lite = "0.2.9" rand = "0.8.5" reqwest = { version = "0.12.4", default-features = false } -ruma = { git = "https://github.com/matrix-org/ruma", rev = "f25b3220d0c3ece7720020ed180af4955a855402", features = [ +ruma = { git = "https://github.com/toger5/ruma", rev = "195cc6f57464b541d6ec0e8368c0993b3f62a760", features = [ "client-api-c", "compat-upload-signatures", "compat-user-id", @@ -59,7 +59,7 @@ ruma = { git = "https://github.com/matrix-org/ruma", rev = "f25b3220d0c3ece77200 "unstable-msc4075", "unstable-msc4140", ] } -ruma-common = { git = "https://github.com/matrix-org/ruma", rev = "f25b3220d0c3ece7720020ed180af4955a855402" } +ruma-common = { git = "https://github.com/toger5/ruma", rev = "195cc6f57464b541d6ec0e8368c0993b3f62a760" } serde = "1.0.151" serde_html_form = "0.2.0" serde_json = "1.0.91" diff --git a/bindings/matrix-sdk-ffi/src/widget.rs b/bindings/matrix-sdk-ffi/src/widget.rs index 73d9e7080ab..370caba0cff 100644 --- a/bindings/matrix-sdk-ffi/src/widget.rs +++ b/bindings/matrix-sdk-ffi/src/widget.rs @@ -318,6 +318,8 @@ pub fn get_element_call_required_permissions( }, ], requires_client: true, + update_future_event: true, + send_future_event: true, } } @@ -379,6 +381,10 @@ pub struct WidgetCapabilities { /// This means clients should not offer to open the widget in a separate /// browser/tab/webview that is not connected to the postmessage widget-api. pub requires_client: bool, + /// This allows the widget to ask the client to update future events. + pub update_future_event: bool, + /// This allows the widget to send events with a delay (Futures). + pub send_future_event: bool, } impl From for matrix_sdk::widget::Capabilities { @@ -387,6 +393,8 @@ impl From for matrix_sdk::widget::Capabilities { read: value.read.into_iter().map(Into::into).collect(), send: value.send.into_iter().map(Into::into).collect(), requires_client: value.requires_client, + update_future_event: value.update_future_event, + send_future_event: value.send_future_event, } } } @@ -397,6 +405,8 @@ impl From for WidgetCapabilities { read: value.read.into_iter().map(Into::into).collect(), send: value.send.into_iter().map(Into::into).collect(), requires_client: value.requires_client, + update_future_event: value.update_future_event, + send_future_event: value.send_future_event, } } } diff --git a/crates/matrix-sdk/src/widget/capabilities.rs b/crates/matrix-sdk/src/widget/capabilities.rs index c6b9ba4720d..d9497f77ca4 100644 --- a/crates/matrix-sdk/src/widget/capabilities.rs +++ b/crates/matrix-sdk/src/widget/capabilities.rs @@ -51,6 +51,10 @@ pub struct Capabilities { /// This means clients should not offer to open the widget in a separate /// browser/tab/webview that is not connected to the postmessage widget-api. pub requires_client: bool, + /// This allows the widget to ask the client to update future events. + pub update_future_event: bool, + /// This allows the widget to send events with a delay (Futures). + pub send_future_event: bool, } impl Capabilities { @@ -73,6 +77,8 @@ const READ_EVENT: &str = "org.matrix.msc2762.receive.event"; const SEND_STATE: &str = "org.matrix.msc2762.send.state_event"; const READ_STATE: &str = "org.matrix.msc2762.receive.state_event"; const REQUIRES_CLIENT: &str = "io.element.requires_client"; +pub const SEND_FUTURE_EVENT: &str = "org.matrix.msc4157.send.delayed_event"; +pub const UPDATE_FUTURE_EVENT: &str = "org.matrix.msc4157.update.delayed_event"; impl Serialize for Capabilities { fn serialize(&self, serializer: S) -> Result @@ -117,12 +123,22 @@ impl Serialize for Capabilities { } } - let seq_len = self.requires_client as usize + self.read.len() + self.send.len(); + let seq_len = self.requires_client as usize + + self.update_future_event as usize + + self.send_future_event as usize + + self.read.len() + + self.send.len(); let mut seq = serializer.serialize_seq(Some(seq_len))?; if self.requires_client { seq.serialize_element(REQUIRES_CLIENT)?; } + if self.update_future_event { + seq.serialize_element(UPDATE_FUTURE_EVENT)?; + } + if self.send_future_event { + seq.serialize_element(SEND_FUTURE_EVENT)?; + } for filter in &self.read { let name = match filter { EventFilter::MessageLike(_) => READ_EVENT, @@ -149,6 +165,8 @@ impl<'de> Deserialize<'de> for Capabilities { { enum Permission { RequiresClient, + UpdateFutureEvent, + SendFutureEvent, Read(EventFilter), Send(EventFilter), Unknown, @@ -163,6 +181,12 @@ impl<'de> Deserialize<'de> for Capabilities { if s == REQUIRES_CLIENT { return Ok(Self::RequiresClient); } + if s == UPDATE_FUTURE_EVENT { + return Ok(Self::UpdateFutureEvent); + } + if s == SEND_FUTURE_EVENT { + return Ok(Self::SendFutureEvent); + } match s.split_once(':') { Some((READ_EVENT, filter_s)) => Ok(Permission::Read(EventFilter::MessageLike( @@ -211,6 +235,8 @@ impl<'de> Deserialize<'de> for Capabilities { Permission::Send(filter) => capabilities.send.push(filter), // ignore unknown capabilities Permission::Unknown => {} + Permission::UpdateFutureEvent => capabilities.update_future_event = true, + Permission::SendFutureEvent => capabilities.send_future_event = true, } } @@ -233,7 +259,8 @@ mod tests { "org.matrix.msc2762.receive.state_event:m.room.member", "org.matrix.msc2762.receive.state_event:org.matrix.msc3401.call.member", "org.matrix.msc2762.send.event:org.matrix.rageshake_request", - "org.matrix.msc2762.send.state_event:org.matrix.msc3401.call.member#@user:matrix.server" + "org.matrix.msc2762.send.state_event:org.matrix.msc3401.call.member#@user:matrix.server", + "org.matrix.msc4140.send.delayed_event" ]"#; let parsed = serde_json::from_str::(capabilities_str).unwrap(); @@ -257,6 +284,8 @@ mod tests { )), ], requires_client: true, + update_future_event: true, + send_future_event: false, }; assert_eq!(parsed, expected); @@ -285,6 +314,8 @@ mod tests { )), ], requires_client: true, + update_future_event: false, + send_future_event: false, }; let capabilities_str = serde_json::to_string(&capabilities).unwrap(); diff --git a/crates/matrix-sdk/src/widget/machine/driver_req.rs b/crates/matrix-sdk/src/widget/machine/driver_req.rs index 58b7dd64061..2641f9f5097 100644 --- a/crates/matrix-sdk/src/widget/machine/driver_req.rs +++ b/crates/matrix-sdk/src/widget/machine/driver_req.rs @@ -17,11 +17,15 @@ use std::marker::PhantomData; use ruma::{ - api::client::{account::request_openid_token, future::FutureParameters}, + api::client::{ + account::request_openid_token, + future::{update_future, FutureParameters}, + }, events::{AnyTimelineEvent, MessageLikeEventType, StateEventType, TimelineEventType}, serde::Raw, }; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; + use serde_json::value::RawValue as RawJsonValue; use tracing::error; @@ -51,6 +55,9 @@ pub(crate) enum MatrixDriverRequestData { /// Send matrix event that corresponds to the given description. SendMatrixEvent(SendEventRequest), + + /// Data for sending a UpdateFuture client server api request. + UpdateFuture(UpdateFutureRequest), } /// A handle to a pending `toWidget` request. @@ -245,3 +252,32 @@ impl FromMatrixDriverResponse for SendEventResponse { } } } + +/// Ask the client to send a UpdateFuture request with the given `future_id` and `action`. +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct UpdateFutureRequest { + pub(crate) action: update_future::unstable::UpdateAction, + pub(crate) future_id: String, +} + +impl From for MatrixDriverRequestData { + fn from(value: UpdateFutureRequest) -> Self { + MatrixDriverRequestData::UpdateFuture(value) + } +} + +impl MatrixDriverRequest for UpdateFutureRequest { + type Response = update_future::unstable::Response; +} + +impl FromMatrixDriverResponse for update_future::unstable::Response { + fn from_response(ev: MatrixDriverResponse) -> Option { + match ev { + MatrixDriverResponse::MatrixFutureUpdate(response) => Some(response), + _ => { + error!("bug in MatrixDriver, received wrong event response"); + None + } + } + } +} diff --git a/crates/matrix-sdk/src/widget/machine/from_widget.rs b/crates/matrix-sdk/src/widget/machine/from_widget.rs index 78425711071..1608981df2c 100644 --- a/crates/matrix-sdk/src/widget/machine/from_widget.rs +++ b/crates/matrix-sdk/src/widget/machine/from_widget.rs @@ -15,7 +15,7 @@ use std::fmt; use ruma::{ - api::client::future, + api::client::future::{send_future_message_event, send_future_state_event, update_future}, events::{AnyTimelineEvent, MessageLikeEventType, StateEventType}, serde::Raw, OwnedEventId, OwnedRoomId, @@ -35,6 +35,8 @@ pub(super) enum FromWidgetRequest { #[serde(rename = "org.matrix.msc2876.read_events")] ReadEvent(ReadEventRequest), SendEvent(SendEventRequest), + #[serde(rename = "org.matrix.msc4157.update_delayed_events")] + FutureUpdate(UpdateFutureRequest), } #[derive(Serialize)] @@ -140,61 +142,39 @@ pub(crate) struct SendEventResponse { /// The event id of the send event. It's optional because if it's a future /// event, it does not get the event_id at this point. pub(crate) event_id: Option, - /// A token to send/insert the future event into the DAG. - pub(crate) send_token: Option, - /// A token to cancel this future event. It will never be sent if this is - /// called. - pub(crate) cancel_token: Option, - /// The `future_group_id` generated for this future event. Used to connect - /// multiple future events. Only one of the connected future events will be - /// sent and inserted into the DAG. - pub(crate) future_group_id: Option, - /// A token used to refresh the timer of the future event. This allows - /// to implement heartbeat-like capabilities. An event is only sent once - /// a refresh in the timeout interval is missed. - /// - /// If the future event does not have a timeout this will be `None`. - pub(crate) refresh_token: Option, + /// The `future_id` generated for this future event. Used to interact with the + /// Future Event. + #[serde(rename = "delay_id")] + pub(crate) future_id: Option, } impl SendEventResponse { pub(crate) fn from_event_id(event_id: OwnedEventId) -> Self { - SendEventResponse { - room_id: None, - event_id: Some(event_id), - send_token: None, - cancel_token: None, - future_group_id: None, - refresh_token: None, - } + SendEventResponse { room_id: None, event_id: Some(event_id), future_id: None } } pub(crate) fn set_room_id(&mut self, room_id: OwnedRoomId) { self.room_id = Some(room_id); } } -impl From for SendEventResponse { - fn from(val: future::send_future_message_event::unstable::Response) -> Self { - SendEventResponse { - room_id: None, - event_id: None, - send_token: Some(val.send_token), - cancel_token: Some(val.cancel_token), - future_group_id: Some(val.future_group_id), - refresh_token: val.refresh_token, - } +impl From for SendEventResponse { + fn from(val: send_future_message_event::unstable::Response) -> Self { + SendEventResponse { room_id: None, event_id: None, future_id: Some(val.future_id) } } } -impl From for SendEventResponse { - fn from(val: future::send_future_state_event::unstable::Response) -> Self { - SendEventResponse { - room_id: None, - event_id: None, - send_token: Some(val.send_token), - cancel_token: Some(val.cancel_token), - future_group_id: Some(val.future_group_id), - refresh_token: val.refresh_token, - } +impl From for SendEventResponse { + fn from(val: send_future_state_event::unstable::Response) -> Self { + SendEventResponse { room_id: None, event_id: None, future_id: Some(val.future_id) } } } + +/// Widget request for updating Future Events. +#[derive(Serialize, Deserialize, Debug)] +pub(crate) struct UpdateFutureRequest { + /// The update action. + pub(crate) action: update_future::unstable::UpdateAction, + /// The future id that should be updated with this action. + #[serde(rename = "delay_id")] + pub(crate) future_id: String, +} diff --git a/crates/matrix-sdk/src/widget/machine/incoming.rs b/crates/matrix-sdk/src/widget/machine/incoming.rs index db9d8e1248f..039068f76ee 100644 --- a/crates/matrix-sdk/src/widget/machine/incoming.rs +++ b/crates/matrix-sdk/src/widget/machine/incoming.rs @@ -12,7 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ruma::{api::client::account::request_openid_token, events::AnyTimelineEvent, serde::Raw}; +use ruma::{ + api::client::{account::request_openid_token, future::update_future}, + events::AnyTimelineEvent, + serde::Raw, +}; use serde::{de, Deserialize, Deserializer}; use serde_json::value::RawValue as RawJsonValue; use uuid::Uuid; @@ -58,6 +62,7 @@ pub(crate) enum MatrixDriverResponse { /// Client sent some matrix event. The response contains the event ID. /// A response to an `Action::SendMatrixEvent` command. MatrixEventSent(SendEventResponse), + MatrixFutureUpdate(update_future::unstable::Response), } pub(super) struct IncomingWidgetMessage { diff --git a/crates/matrix-sdk/src/widget/machine/mod.rs b/crates/matrix-sdk/src/widget/machine/mod.rs index 85979e8311b..9939aa31b26 100644 --- a/crates/matrix-sdk/src/widget/machine/mod.rs +++ b/crates/matrix-sdk/src/widget/machine/mod.rs @@ -18,6 +18,7 @@ use std::{fmt, iter, time::Duration}; +use driver_req::UpdateFutureRequest; use indexmap::IndexMap; use ruma::{ serde::{JsonObject, Raw}, @@ -48,6 +49,7 @@ use self::{ #[cfg(doc)] use super::WidgetDriver; use super::{ + capabilities, filter::{MatrixEventContent, MatrixEventFilterInput}, Capabilities, StateKeySelector, }; @@ -245,6 +247,31 @@ impl WidgetMachine { let response = self.send_from_widget_response(raw_request, OpenIdResponse::Pending); iter::once(response).chain(request_action).collect() } + FromWidgetRequest::FutureUpdate(req) => { + let CapabilitiesState::Negotiated(capabilities) = &self.capabilities else { + let text = + "Received send update future event request before capabilities were negotiated"; + return vec![self.send_from_widget_error_response(raw_request, text)]; + }; + if !capabilities.update_future_event { + return vec![self.send_from_widget_error_response( + raw_request, + format!( + "Not allowed: missing the {} capability.", + capabilities::UPDATE_FUTURE_EVENT + ), + )]; + } + let (request, request_action) = + self.send_matrix_driver_request(UpdateFutureRequest { + action: req.action, + future_id: req.future_id, + }); + request.then(|res, machine| { + vec![machine.send_from_widget_result_response(raw_request, res)] + }); + request_action.map(|a| vec![a]).unwrap_or_default() + } } } @@ -338,7 +365,12 @@ impl WidgetMachine { Default::default() }), }; - + if !capabilities.send_future_event && request.future_event_parameters.is_some() { + return Some(self.send_from_widget_error_response( + raw_request, + format!("Not allowed: missing the {} capability.", capabilities::SEND_FUTURE_EVENT), + )); + } if !capabilities.send.iter().any(|filter| filter.matches(&filter_in)) { return Some(self.send_from_widget_error_response(raw_request, "Not allowed")); } diff --git a/crates/matrix-sdk/src/widget/machine/tests/send_event.rs b/crates/matrix-sdk/src/widget/machine/tests/send_event.rs index a29ab3f73a0..102beab4582 100644 --- a/crates/matrix-sdk/src/widget/machine/tests/send_event.rs +++ b/crates/matrix-sdk/src/widget/machine/tests/send_event.rs @@ -32,12 +32,12 @@ fn parse_future_event_widget_action() { FromWidgetRequest::SendEvent(send_event_request) = incoming_request.deserialize().unwrap() ); assert_let!( - FutureParameters::Timeout { timeout, group_id } = + FutureParameters::Timeout { timeout, future_parent_id } = send_event_request.future_event_parameters.unwrap() ); assert_eq!(timeout, Duration::from_millis(10000)); - assert_eq!(group_id, None); + assert_eq!(future_parent_id, None); assert_eq!(send_event_request.event_type, TimelineEventType::CallMember); assert_eq!(send_event_request.state_key.unwrap(), "_@abc:example.org_VFKPEKYWMP".to_owned()); } diff --git a/crates/matrix-sdk/src/widget/matrix.rs b/crates/matrix-sdk/src/widget/matrix.rs index 7d6338c706e..b01719913b3 100644 --- a/crates/matrix-sdk/src/widget/matrix.rs +++ b/crates/matrix-sdk/src/widget/matrix.rs @@ -22,7 +22,7 @@ use ruma::{ api::client::{ account::request_openid_token::v3::{Request as OpenIdRequest, Response as OpenIdResponse}, filter::RoomEventFilter, - future, + future::{self, update_future::unstable::UpdateAction}, }, assign, events::{ @@ -146,6 +146,18 @@ impl MatrixDriver { }) } + /// Send a request to the `/delayed_events`` endpoint ([MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)) + /// This can be used to refresh cancel or send a Future Event (delayed event). (An Event that is send ahead of time to the homeserver + /// and gets distributed once it times out.) + pub(crate) async fn update_future( + &self, + future_id: String, + action: UpdateAction, + ) -> HttpResult { + let r = future::update_future::unstable::Request::new(future_id, action); + self.room.client.send(r, None).await + } + /// Starts forwarding new room events. Once the returned `EventReceiver` /// is dropped, forwarding will be stopped. pub(crate) fn events(&self) -> EventReceiver { diff --git a/crates/matrix-sdk/src/widget/mod.rs b/crates/matrix-sdk/src/widget/mod.rs index 96cd60d59dc..20207eb9b37 100644 --- a/crates/matrix-sdk/src/widget/mod.rs +++ b/crates/matrix-sdk/src/widget/mod.rs @@ -28,7 +28,7 @@ use self::{ }, matrix::MatrixDriver, }; -use crate::{room::Room, Result}; +use crate::{room::Room, HttpError, Result}; mod capabilities; mod filter; @@ -235,8 +235,14 @@ impl ProcessingContext { .send(event_type, state_key, content, future_event_parameters) .await .map(MatrixDriverResponse::MatrixEventSent) - .map_err(|e| e.to_string()) + .map_err(|e: crate::Error| e.to_string()) } + MatrixDriverRequestData::UpdateFuture(req) => self + .matrix_driver + .update_future(req.future_id, req.action) + .await + .map(|r| MatrixDriverResponse::MatrixFutureUpdate(r)) + .map_err(|e: HttpError| e.to_string()), }; self.events_tx diff --git a/crates/matrix-sdk/tests/integration/widget.rs b/crates/matrix-sdk/tests/integration/widget.rs index 441fdbe88b3..fc6a62a3c0b 100644 --- a/crates/matrix-sdk/tests/integration/widget.rs +++ b/crates/matrix-sdk/tests/integration/widget.rs @@ -609,13 +609,10 @@ async fn send_future_room_message_event() { Mock::given(method("PUT")) .and(path_regex( - r"^/_matrix/client/unstable/org.matrix.msc4140/rooms/.*/send_future/m.room.message/.*$", + r"^/_matrix/client/unstable/org.matrix.msc4140/rooms/.*/send/m.room.message/.*$", )) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ - "future_group_id": "1234", - "send_token": "my_send_token", - "refresh_token":"my_refresh_token", - "cancel_token": "my_cancel_token" + "delay_id": "1234", }))) .expect(1) .mount(&mock_server) @@ -631,7 +628,7 @@ async fn send_future_room_message_event() { "msgtype": "m.text", "body": "Message from a widget!", }, - "future_timeout":1000, + "delay":1000, }), ) .await; @@ -640,14 +637,8 @@ async fn send_future_room_message_event() { let msg = recv_message(&driver_handle).await; assert_eq!(msg["api"], "fromWidget"); assert_eq!(msg["action"], "send_event"); - let future_group_id = msg["response"]["future_group_id"].as_str().unwrap(); - assert_eq!(future_group_id, "1234"); - let cancel_token = msg["response"]["cancel_token"].as_str().unwrap(); - assert_eq!(cancel_token, "my_cancel_token"); - let refresh_token = msg["response"]["refresh_token"].as_str().unwrap(); - assert_eq!(refresh_token, "my_refresh_token"); - let send_token = msg["response"]["send_token"].as_str().unwrap(); - assert_eq!(send_token, "my_send_token"); + let future_id = msg["response"]["delay_id"].as_str().unwrap(); + assert_eq!(future_id, "1234"); // Make sure the event-sending endpoint was hit exactly once mock_server.verify().await; @@ -665,13 +656,10 @@ async fn send_future_state_event() { Mock::given(method("PUT")) .and(path_regex( - r"^/_matrix/client/unstable/org.matrix.msc4140/rooms/.*/state_future/m.room.name/?$", + r"^/_matrix/client/unstable/org.matrix.msc4140/rooms/.*/state/m.room.name/?$", )) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ - "future_group_id": "1234", - "send_token": "my_send_token", - "refresh_token":"my_refresh_token", - "cancel_token": "my_cancel_token" + "delay_id": "1234", }))) .expect(1) .mount(&mock_server) @@ -687,7 +675,7 @@ async fn send_future_state_event() { "content": { "name": "Room Name set by Widget", }, - "future_timeout":1000, + "delay":1000, }), ) .await; @@ -696,14 +684,55 @@ async fn send_future_state_event() { let msg = recv_message(&driver_handle).await; assert_eq!(msg["api"], "fromWidget"); assert_eq!(msg["action"], "send_event"); - let future_group_id = msg["response"]["future_group_id"].as_str().unwrap(); - assert_eq!(future_group_id, "1234"); - let cancel_token = msg["response"]["cancel_token"].as_str().unwrap(); - assert_eq!(cancel_token, "my_cancel_token"); - let refresh_token = msg["response"]["refresh_token"].as_str().unwrap(); - assert_eq!(refresh_token, "my_refresh_token"); - let send_token = msg["response"]["send_token"].as_str().unwrap(); - assert_eq!(send_token, "my_send_token"); + let future_id = msg["response"]["future_id"].as_str().unwrap(); + assert_eq!(future_id, "1234"); + + // Make sure the event-sending endpoint was hit exactly once + mock_server.verify().await; +} + +#[async_test] +async fn update_future() { + let (_, mock_server, driver_handle) = run_test_driver(false).await; + + negotiate_capabilities( + &driver_handle, + json!(["org.matrix.msc2762.send.state_event:m.room.name#"]), + ) + .await; + + Mock::given(method("PUT")) + .and(path_regex( + r"^/_matrix/client/unstable/org.matrix.msc4140/rooms/.*/state/m.room.name/?$", + )) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "delay_id": "1234", + }))) + .expect(1) + .mount(&mock_server) + .await; + + send_request( + &driver_handle, + "send-room-message", + "send_event", + json!({ + "type": "m.room.name", + "state_key": "", + "content": { + "name": "Room Name set by Widget", + }, + "delay":1000, + }), + ) + .await; + + // Receive the response + let msg = recv_message(&driver_handle).await; + assert_eq!(msg["api"], "fromWidget"); + assert_eq!(msg["action"], "send_event"); + let future_id = msg["response"]["delay_id"].as_str().unwrap(); + assert_eq!(future_id, "1234"); // Make sure the event-sending endpoint was hit exactly once mock_server.verify().await;