widget: Add support for read-state-event fromWidget requests

Co-authored-by: Daniel Abramov <daniel.abramov@element.io>
This commit is contained in:
Jonas Platte
2023-10-19 11:32:39 +02:00
committed by Jonas Platte
parent 946976a561
commit 3ce7126d58
3 changed files with 132 additions and 11 deletions

View File

@@ -24,7 +24,7 @@ use ruma::{
use tracing::error;
use super::{
actions::{MatrixDriverRequestData, ReadMessageLikeEventCommand},
actions::{MatrixDriverRequestData, ReadMessageLikeEventCommand, ReadStateEventCommand},
incoming::MatrixDriverResponse,
MatrixDriverRequestMeta, SendEventCommand, WidgetMachine,
};
@@ -129,15 +129,15 @@ impl FromMatrixDriverResponse for request_openid_token::v3::Response {
/// Ask the client to read matrix event(s) that corresponds to the given
/// description and return a list of events as a response.
#[derive(Debug)]
pub(crate) struct ReadMatrixEvent(pub(crate) ReadMessageLikeEventCommand);
pub(crate) struct ReadMatrixMessageLikeEvent(pub(crate) ReadMessageLikeEventCommand);
impl From<ReadMatrixEvent> for MatrixDriverRequestData {
fn from(value: ReadMatrixEvent) -> Self {
impl From<ReadMatrixMessageLikeEvent> for MatrixDriverRequestData {
fn from(value: ReadMatrixMessageLikeEvent) -> Self {
MatrixDriverRequestData::ReadMessageLikeEvent(value.0)
}
}
impl MatrixDriverRequest for ReadMatrixEvent {
impl MatrixDriverRequest for ReadMatrixMessageLikeEvent {
type Response = Vec<Raw<AnyTimelineEvent>>;
}
@@ -153,6 +153,21 @@ impl FromMatrixDriverResponse for Vec<Raw<AnyTimelineEvent>> {
}
}
/// Ask the client to read matrix event(s) that corresponds to the given
/// description and return a list of events as a response.
#[derive(Debug)]
pub(crate) struct ReadMatrixStateEvent(pub(crate) ReadStateEventCommand);
impl From<ReadMatrixStateEvent> for MatrixDriverRequestData {
fn from(value: ReadMatrixStateEvent) -> Self {
MatrixDriverRequestData::ReadStateEvent(value.0)
}
}
impl MatrixDriverRequest for ReadMatrixStateEvent {
type Response = Vec<Raw<AnyTimelineEvent>>;
}
/// Ask the client to send matrix event that corresponds to the given
/// description and return an event ID as a response.
#[derive(Debug)]

View File

@@ -14,13 +14,21 @@
use std::fmt;
use ruma::{
events::{AnyTimelineEvent, MessageLikeEventType, StateEventType},
serde::Raw,
};
use serde::{Deserialize, Serialize};
use crate::widget::StateKeySelector;
#[derive(Deserialize)]
#[serde(tag = "action", rename_all = "snake_case", content = "data")]
pub(super) enum FromWidgetRequest {
SupportedApiVersions {},
ContentLoaded {},
#[serde(rename = "org.matrix.msc2876.read_events")]
ReadEvent(ReadEventRequest),
}
#[derive(Serialize)]
@@ -97,3 +105,24 @@ pub(super) enum ApiVersion {
#[serde(rename = "town.robin.msc3846")]
MSC3846,
}
#[derive(Deserialize)]
#[serde(untagged)]
pub(super) enum ReadEventRequest {
ReadStateEvent {
#[serde(rename = "type")]
event_type: StateEventType,
state_key: StateKeySelector,
},
#[allow(dead_code)]
ReadMessageLikeEvent {
#[serde(rename = "type")]
event_type: MessageLikeEventType,
limit: Option<u32>,
},
}
#[derive(Debug, Serialize)]
pub(super) struct ReadEventResponse {
pub(super) events: Vec<Raw<AnyTimelineEvent>>,
}

View File

@@ -16,6 +16,8 @@
#![warn(unreachable_pub)]
use std::fmt;
use indexmap::{map::Entry, IndexMap};
use ruma::serde::{JsonObject, Raw};
use serde::Serialize;
@@ -25,14 +27,26 @@ use tracing::{error, info_span, instrument, trace, warn};
use uuid::Uuid;
use self::{
driver_req::{AcquireCapabilities, MatrixDriverRequest, MatrixDriverRequestHandle},
from_widget::{FromWidgetErrorResponse, FromWidgetRequest, SupportedApiVersionsResponse},
actions::ReadStateEventCommand,
driver_req::{
AcquireCapabilities, MatrixDriverRequest, MatrixDriverRequestHandle, ReadMatrixStateEvent,
},
from_widget::{
FromWidgetErrorResponse, FromWidgetRequest, ReadEventRequest, ReadEventResponse,
SupportedApiVersionsResponse,
},
incoming::{IncomingWidgetMessage, IncomingWidgetMessageKind},
to_widget::{
NotifyPermissionsChanged, RequestPermissions, ToWidgetRequest, ToWidgetRequestHandle,
ToWidgetResponse,
},
};
#[cfg(doc)]
use super::WidgetDriver;
use super::{
filter::{MatrixEventContent, MatrixEventFilterInput},
Capabilities, StateKeySelector,
};
mod actions;
mod driver_req;
@@ -47,9 +61,6 @@ pub(crate) use self::{
actions::{Action, MatrixDriverRequestData, SendEventCommand},
incoming::{IncomingMessage, MatrixDriverResponse},
};
use super::Capabilities;
#[cfg(doc)]
use super::WidgetDriver;
/// No I/O state machine.
///
@@ -134,7 +145,7 @@ impl WidgetMachine {
let request = match raw_request.deserialize() {
Ok(r) => r,
Err(e) => {
self.send_from_widget_response(raw_request, FromWidgetErrorResponse::new(e));
self.send_from_widget_error_response(raw_request, e);
return;
}
};
@@ -143,12 +154,70 @@ impl WidgetMachine {
FromWidgetRequest::SupportedApiVersions {} => {
self.send_from_widget_response(raw_request, SupportedApiVersionsResponse::new());
}
FromWidgetRequest::ContentLoaded {} => {
self.send_from_widget_response(raw_request, JsonObject::new());
if self.capabilities.is_unset() {
self.negotiate_capabilities();
}
}
FromWidgetRequest::ReadEvent(req) => {
self.process_read_event_request(req, raw_request);
}
}
}
fn process_read_event_request(
&mut self,
request: ReadEventRequest,
raw_request: Raw<FromWidgetRequest>,
) {
let CapabilitiesState::Negotiated(capabilities) = &self.capabilities else {
self.send_from_widget_error_response(
raw_request,
"Received read event request before capabilities were negotiated",
);
return;
};
match request {
ReadEventRequest::ReadMessageLikeEvent { .. } => {
self.send_from_widget_error_response(
raw_request,
"Reading of message events is not yet supported",
);
}
ReadEventRequest::ReadStateEvent { event_type, state_key } => {
let allowed = match &state_key {
StateKeySelector::Any => capabilities
.read
.iter()
.any(|filter| filter.matches_state_event_with_any_state_key(&event_type)),
StateKeySelector::Key(state_key) => {
let filter_in = MatrixEventFilterInput {
event_type: event_type.to_string().into(),
state_key: Some(state_key.clone()),
// content doesn't matter for state events
content: MatrixEventContent::default(),
};
capabilities.read.iter().any(|filter| filter.matches(&filter_in))
}
};
if allowed {
let request =
ReadMatrixStateEvent(ReadStateEventCommand { event_type, state_key });
self.send_matrix_driver_request(request).then(|events, machine| {
machine
.send_from_widget_response(raw_request, ReadEventResponse { events });
});
} else {
self.send_from_widget_error_response(raw_request, "Not allowed");
}
}
}
}
@@ -240,6 +309,14 @@ impl WidgetMachine {
}
}
fn send_from_widget_error_response(
&self,
raw_request: Raw<FromWidgetRequest>,
error: impl fmt::Display,
) {
self.send_from_widget_response(raw_request, FromWidgetErrorResponse::new(error))
}
#[instrument(skip_all, fields(action = T::ACTION))]
fn send_to_widget_request<T: ToWidgetRequest>(
&mut self,