widget: Implement fromWidget response sending
… and send an error response when the request fails to deserialize.
This commit is contained in:
committed by
Jonas Platte
parent
9c71c2b733
commit
ea36eaa09a
@@ -12,8 +12,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(super) enum FromWidgetRequest {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(super) struct FromWidgetErrorResponse {
|
||||
error: FromWidgetError,
|
||||
}
|
||||
|
||||
impl FromWidgetErrorResponse {
|
||||
pub(super) fn new(e: impl fmt::Display) -> Self {
|
||||
Self { error: FromWidgetError { message: e.to_string() } }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FromWidgetError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ pub(super) struct IncomingWidgetMessage {
|
||||
}
|
||||
|
||||
pub(super) enum IncomingWidgetMessageKind {
|
||||
Request(FromWidgetRequest),
|
||||
Request(Raw<FromWidgetRequest>),
|
||||
Response(ToWidgetResponse),
|
||||
}
|
||||
|
||||
@@ -95,9 +95,7 @@ impl<'de> Deserialize<'de> for IncomingWidgetMessage {
|
||||
serde_json::from_str(raw.get()).map_err(de::Error::custom)?;
|
||||
|
||||
let kind = match api {
|
||||
ApiTag::FromWidget => serde_json::from_str(raw.get())
|
||||
.map(IncomingWidgetMessageKind::Request)
|
||||
.map_err(de::Error::custom)?,
|
||||
ApiTag::FromWidget => IncomingWidgetMessageKind::Request(Raw::from_json(raw)),
|
||||
ApiTag::ToWidget => serde_json::from_str(raw.get())
|
||||
.map(IncomingWidgetMessageKind::Response)
|
||||
.map_err(de::Error::custom)?,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#![warn(unreachable_pub)]
|
||||
|
||||
use indexmap::{map::Entry, IndexMap};
|
||||
use ruma::serde::Raw;
|
||||
use serde::Serialize;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
@@ -25,6 +26,7 @@ use uuid::Uuid;
|
||||
|
||||
use self::{
|
||||
driver_req::{AcquirePermissions, MatrixDriverRequest, MatrixDriverRequestHandle},
|
||||
from_widget::{FromWidgetErrorResponse, FromWidgetRequest},
|
||||
incoming::{IncomingWidgetMessage, IncomingWidgetMessageKind},
|
||||
to_widget::{
|
||||
NotifyPermissionsChanged, RequestPermissions, ToWidgetRequest, ToWidgetRequestHandle,
|
||||
@@ -113,13 +115,28 @@ impl WidgetMachine {
|
||||
}
|
||||
|
||||
match message.kind {
|
||||
IncomingWidgetMessageKind::Request(req) => match req {},
|
||||
IncomingWidgetMessageKind::Request(request) => {
|
||||
self.process_from_widget_request(request);
|
||||
}
|
||||
IncomingWidgetMessageKind::Response(response) => {
|
||||
self.process_to_widget_response(message.request_id, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(request_id))]
|
||||
fn process_from_widget_request(&mut self, raw_request: Raw<FromWidgetRequest>) {
|
||||
let request = match raw_request.deserialize() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
self.send_from_widget_response(raw_request, FromWidgetErrorResponse::new(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match request {}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(?request_id))]
|
||||
fn process_to_widget_response(&mut self, request_id: String, response: ToWidgetResponse) {
|
||||
let Ok(request_id) = Uuid::parse_str(&request_id) else {
|
||||
@@ -173,6 +190,41 @@ impl WidgetMachine {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(request_id))]
|
||||
fn send_from_widget_response(
|
||||
&self,
|
||||
raw_request: Raw<FromWidgetRequest>,
|
||||
response_data: impl Serialize,
|
||||
) {
|
||||
let mut object = match raw_request.deserialize_as::<IndexMap<String, Box<RawJsonValue>>>() {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
error!("Failed to converted FromWidgetRequest to object representation: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let response_data = match serde_json::value::to_raw_value(&response_data) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
error!("Failed to serialize response data: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
object.insert("response".to_owned(), response_data);
|
||||
|
||||
let serialized = match serde_json::to_string(&object) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("Failed to serialize response: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self.actions_sender.send(Action::SendToWidget(serialized)) {
|
||||
error!("Failed to send action: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(action = T::ACTION))]
|
||||
fn send_to_widget_request<T: ToWidgetRequest>(
|
||||
&mut self,
|
||||
|
||||
@@ -13,28 +13,13 @@
|
||||
// limitations under the License.
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use ruma::serde::JsonObject;
|
||||
use serde_json::{from_value, json, Value as JsonValue};
|
||||
use uuid::Uuid;
|
||||
use serde_json::{from_value, json};
|
||||
|
||||
use super::{parse_msg, WIDGET_ID};
|
||||
use crate::widget::machine::{
|
||||
incoming::MatrixDriverResponse, Action, IncomingMessage, MatrixDriverRequestData, WidgetMachine,
|
||||
};
|
||||
|
||||
const WIDGET_ID: &str = "test-widget";
|
||||
|
||||
/// Create a JSON string from a [`json!`][serde_json::json] "literal".
|
||||
#[macro_export]
|
||||
macro_rules! json_string {
|
||||
($( $tt:tt )*) => { ::serde_json::json!( $($tt)* ).to_string() };
|
||||
}
|
||||
|
||||
fn parse_msg(msg: &str) -> (JsonValue, Uuid) {
|
||||
let mut deserialized: JsonObject = serde_json::from_str(msg).unwrap();
|
||||
let request_id = from_value(deserialized.remove("requestId").unwrap()).unwrap();
|
||||
(JsonValue::Object(deserialized), request_id)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn machine_can_negotiate_capabilities_immediately() {
|
||||
let (mut machine, mut actions_recv) = WidgetMachine::new(WIDGET_ID.to_owned(), false);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use serde_json::json;
|
||||
|
||||
use super::{parse_msg, WIDGET_ID};
|
||||
use crate::widget::machine::{Action, IncomingMessage, WidgetMachine};
|
||||
|
||||
#[test]
|
||||
fn machine_sends_error_for_unknown_request() {
|
||||
let (mut machine, mut actions_recv) = WidgetMachine::new(WIDGET_ID.to_owned(), true);
|
||||
|
||||
// No messages from the machine at first
|
||||
assert_matches!(actions_recv.try_recv(), Err(_));
|
||||
|
||||
machine.process(IncomingMessage::WidgetMessage(json_string!({
|
||||
"api": "fromWidget",
|
||||
"widgetId": WIDGET_ID,
|
||||
"requestId": "invalid-req",
|
||||
"action": "I AM ERROR",
|
||||
"data": {
|
||||
"some": "field",
|
||||
},
|
||||
})));
|
||||
|
||||
let action = actions_recv.try_recv().unwrap();
|
||||
let msg = assert_matches!(action, Action::SendToWidget(msg) => msg);
|
||||
let (msg, request_id) = parse_msg(&msg);
|
||||
assert_eq!(request_id, "invalid-req");
|
||||
assert_eq!(msg["api"], "fromWidget");
|
||||
assert_eq!(msg["widgetId"], WIDGET_ID);
|
||||
assert_eq!(msg["action"], "I AM ERROR");
|
||||
assert_eq!(msg["data"], json!({ "some": "field" }));
|
||||
assert!(msg["response"]["error"]["message"].is_string());
|
||||
}
|
||||
@@ -12,4 +12,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use ruma::serde::JsonObject;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
/// Create a JSON string from a [`json!`][serde_json::json] "literal".
|
||||
#[macro_export]
|
||||
macro_rules! json_string {
|
||||
($( $tt:tt )*) => { ::serde_json::json!( $($tt)* ).to_string() };
|
||||
}
|
||||
|
||||
mod capabilities;
|
||||
mod error;
|
||||
|
||||
const WIDGET_ID: &str = "test-widget";
|
||||
|
||||
fn parse_msg(msg: &str) -> (JsonValue, String) {
|
||||
let mut deserialized: JsonObject = serde_json::from_str(msg).unwrap();
|
||||
let request_id =
|
||||
assert_matches!(deserialized.remove("requestId").unwrap(), JsonValue::String(s) => s);
|
||||
(JsonValue::Object(deserialized), request_id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user