From 370223762b0f215e65901887ceb623526e0fcd61 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 28 Jun 2024 09:50:34 -0400 Subject: [PATCH 01/15] Support sending delayed events (Futures) / MSC4140 --- src/ClientWidgetApi.ts | 72 +++++++++++++++++++++++++++--- src/WidgetApi.ts | 38 +++++++++++++++- src/driver/WidgetDriver.ts | 38 +++++++++++++++- src/interfaces/SendFutureAction.ts | 39 ++++++++++++++++ src/interfaces/WidgetApiAction.ts | 3 +- 5 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 src/interfaces/SendFutureAction.ts diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index 529a707..87fbad6 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2024 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. @@ -24,7 +24,7 @@ import { IContentLoadedActionRequest } from "./interfaces/ContentLoadedAction"; import { WidgetApiFromWidgetAction, WidgetApiToWidgetAction } from "./interfaces/WidgetApiAction"; import { IWidgetApiErrorResponseData } from "./interfaces/IWidgetApiErrorResponse"; import { Capability, MatrixCapabilities } from "./interfaces/Capabilities"; -import { IOpenIDUpdate, ISendEventDetails, WidgetDriver } from "./driver/WidgetDriver"; +import { IOpenIDUpdate, ISendEventDetails, ISendFutureDetails, WidgetDriver } from "./driver/WidgetDriver"; import { ICapabilitiesActionResponseData, INotifyCapabilitiesActionRequestData, @@ -94,6 +94,10 @@ import { IUploadFileActionFromWidgetActionRequest, IUploadFileActionFromWidgetResponseData, } from "./interfaces/UploadFileAction"; +import { + ISendFutureFromWidgetActionRequest, + ISendFutureFromWidgetResponseData, +} from "./interfaces/SendFutureAction"; /** * API handler for the client side of widgets. This raises events @@ -477,10 +481,9 @@ export class ClientWidgetApi extends EventEmitter { }); } - const isState = request.data.state_key !== null && request.data.state_key !== undefined; let sendEventPromise: Promise; - if (isState) { - if (!this.canSendStateEvent(request.data.type, request.data.state_key!)) { + if (request.data.state_key != null) { + if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { return this.transport.reply(request, { error: {message: "Cannot send state events of this type"}, }); @@ -522,6 +525,63 @@ export class ClientWidgetApi extends EventEmitter { }); } + private handleSendFuture(request: ISendFutureFromWidgetActionRequest) { + if (!request.data.type) { + return this.transport.reply(request, { + error: {message: "Invalid request - missing event type"}, + }); + } + + let sendFuturePromise: Promise; + if (request.data.state_key != null) { + if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { + return this.transport.reply(request, { + error: {message: "Cannot send state futures of this type"}, + }); + } + + sendFuturePromise = this.driver.sendFuture( + request.data.future_group_id ?? null, + request.data.future_timeout ?? null, + request.data.type, + request.data.content || {}, + request.data.state_key, + request.data.room_id, + ); + } else { + const content = request.data.content as { msgtype?: string } || {}; + const msgtype = content['msgtype']; + if (!this.canSendRoomEvent(request.data.type, msgtype)) { + return this.transport.reply(request, { + error: {message: "Cannot send room futures of this type"}, + }); + } + + sendFuturePromise = this.driver.sendFuture( + request.data.future_group_id ?? null, + request.data.future_timeout ?? null, + request.data.type, + content, + null, // not sending a state event + request.data.room_id, + ); + } + + sendFuturePromise.then(sentFuture => { + return this.transport.reply(request, { + future_group_id: sentFuture.futureGroupId, + send_token: sentFuture.sendToken, + cancel_token: sentFuture.cancelToken, + refresh_token: sentFuture.refreshToken, + }); + }).catch(e => { + console.error("error sending future: ", e); + return this.transport.reply(request, { + error: {message: "Error sending future"}, + }); + }); + } + private async handleSendToDevice(request: ISendToDeviceFromWidgetActionRequest): Promise { if (!request.data.type) { await this.transport.reply(request, { @@ -770,6 +830,8 @@ export class ClientWidgetApi extends EventEmitter { return this.replyVersions(ev.detail); case WidgetApiFromWidgetAction.SendEvent: return this.handleSendEvent(ev.detail); + case WidgetApiFromWidgetAction.SendFuture: + return this.handleSendFuture(ev.detail); case WidgetApiFromWidgetAction.SendToDevice: return this.handleSendToDevice(ev.detail); case WidgetApiFromWidgetAction.GetOpenIDCredentials: diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index 1dc17ee..59895da 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2024 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. @@ -54,6 +54,11 @@ import { } from "./interfaces/ModalWidgetActions"; import { ISetModalButtonEnabledActionRequestData } from "./interfaces/SetModalButtonEnabledAction"; import { ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData } from "./interfaces/SendEventAction"; +import { + ISendFutureFromWidgetRequestData, + ISendFutureFromWidgetResponseData, + ISendFutureOptions, +} from "./interfaces/SendFutureAction"; import { ISendToDeviceFromWidgetRequestData, ISendToDeviceFromWidgetResponseData, @@ -419,6 +424,37 @@ export class WidgetApi extends EventEmitter { ); } + /** + * @experimental This currently relies on an unstable MSC (MSC4140). + */ + public sendRoomFuture( + futureOpts: ISendFutureOptions, + eventType: string, + content: unknown, + roomId?: string, + ): Promise { + return this.transport.send( + WidgetApiFromWidgetAction.SendFuture, + {type: eventType, content, room_id: roomId, ...futureOpts}, + ); + } + + /** + * @experimental This currently relies on an unstable MSC (MSC4140). + */ + public sendStateFuture( + futureOpts: ISendFutureOptions, + eventType: string, + stateKey: string, + content: unknown, + roomId?: string, + ): Promise { + return this.transport.send( + WidgetApiFromWidgetAction.SendFuture, + {type: eventType, content, state_key: stateKey, room_id: roomId, ...futureOpts}, + ); + } + /** * Sends a to-device event. * @param {string} eventType The type of events being sent. diff --git a/src/driver/WidgetDriver.ts b/src/driver/WidgetDriver.ts index c98722a..292a07e 100644 --- a/src/driver/WidgetDriver.ts +++ b/src/driver/WidgetDriver.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2024 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. @@ -29,6 +29,14 @@ export interface ISendEventDetails { eventId: string; } +export interface ISendFutureDetails { + roomId: string; + futureGroupId: string; + sendToken: string; + cancelToken: string; + refreshToken?: string; +} + export interface IOpenIDUpdate { state: OpenIDRequestState; token?: IOpenIDCredentials; @@ -103,6 +111,34 @@ export abstract class WidgetDriver { return Promise.reject(new Error("Failed to override function")); } + /** + * Sends a future into a room. If `roomId` is falsy, the client should send the future + * into the room the user is currently looking at. The widget API will have already + * verified that the widget is capable of sending the future's event to that room. + * @param {string|null} futureGroupId The ID of the group the future belongs to, + * or null if it will be put in a new group. + * @param {number|null} futureTimeout The future's timeout, or null for an action future. + * @param {string} eventType The event type of the event to be sent by the future. + * @param {*} content The content for the event to be sent by the future. + * @param {string|null} stateKey The state key if the event to be sent by the future is + * a state event, otherwise null. May be an empty string. + * @param {string|null} roomId The room ID to send the future to. If falsy, the room the + * user is currently looking at. + * @returns {Promise} Resolves when the future has been sent with + * details of that future. + * @throws Rejected when the future could not be sent. + */ + public sendFuture( + futureGroupId: string | null, + futureTimeout: number | null, + eventType: string, + content: unknown, + stateKey: string | null = null, + roomId: string | null = null, + ): Promise { + return Promise.reject(new Error("Failed to override function")); + } + /** * Sends a to-device event. The widget API will have already verified that the widget * is capable of sending the event. diff --git a/src/interfaces/SendFutureAction.ts b/src/interfaces/SendFutureAction.ts new file mode 100644 index 0000000..dcee90c --- /dev/null +++ b/src/interfaces/SendFutureAction.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2020 - 2024 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. + */ + +import { IWidgetApiRequest } from "./IWidgetApiRequest"; +import { IWidgetApiResponseData } from "./IWidgetApiResponse"; +import { ISendEventFromWidgetRequestData } from "./SendEventAction"; +import { WidgetApiFromWidgetAction } from "./WidgetApiAction"; + +export interface ISendFutureOptions { + future_group_id?: string; // eslint-disable-line camelcase + future_timeout?: number; // eslint-disable-line camelcase +} + +export interface ISendFutureFromWidgetRequestData extends ISendEventFromWidgetRequestData, ISendFutureOptions {} + +export interface ISendFutureFromWidgetActionRequest extends IWidgetApiRequest { + action: WidgetApiFromWidgetAction.SendFuture; + data: ISendFutureFromWidgetRequestData; +} + +export interface ISendFutureFromWidgetResponseData extends IWidgetApiResponseData { + future_group_id: string; // eslint-disable-line camelcase + send_token: string; // eslint-disable-line camelcase + cancel_token: string; // eslint-disable-line camelcase + refresh_token?: string; // eslint-disable-line camelcase +} diff --git a/src/interfaces/WidgetApiAction.ts b/src/interfaces/WidgetApiAction.ts index de78e52..6dd8244 100644 --- a/src/interfaces/WidgetApiAction.ts +++ b/src/interfaces/WidgetApiAction.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2024 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. @@ -39,6 +39,7 @@ export enum WidgetApiFromWidgetAction { OpenModalWidget = "open_modal", SetModalButtonEnabled = "set_button_enabled", SendEvent = "send_event", + SendFuture = "send_future", SendToDevice = "send_to_device", WatchTurnServers = "watch_turn_servers", UnwatchTurnServers = "unwatch_turn_servers", From d6bc72024266aaca1d5253ec00118f428fc002ec Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 3 Jul 2024 09:29:50 -0400 Subject: [PATCH 02/15] Follow MSC4157 Remove "send_future" types in favour of extending "send_event" --- src/ClientWidgetApi.ts | 121 +++++++++++------------------ src/WidgetApi.ts | 27 +++---- src/interfaces/SendEventAction.ts | 10 +++ src/interfaces/SendFutureAction.ts | 39 ---------- src/interfaces/WidgetApiAction.ts | 1 - 5 files changed, 66 insertions(+), 132 deletions(-) delete mode 100644 src/interfaces/SendFutureAction.ts diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index 87fbad6..c09b485 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -94,10 +94,6 @@ import { IUploadFileActionFromWidgetActionRequest, IUploadFileActionFromWidgetResponseData, } from "./interfaces/UploadFileAction"; -import { - ISendFutureFromWidgetActionRequest, - ISendFutureFromWidgetResponseData, -} from "./interfaces/SendFutureAction"; /** * API handler for the client side of widgets. This raises events @@ -481,7 +477,7 @@ export class ClientWidgetApi extends EventEmitter { }); } - let sendEventPromise: Promise; + let sendEventPromise: Promise; if (request.data.state_key != null) { if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { return this.transport.reply(request, { @@ -489,12 +485,23 @@ export class ClientWidgetApi extends EventEmitter { }); } - sendEventPromise = this.driver.sendEvent( - request.data.type, - request.data.content || {}, - request.data.state_key, - request.data.room_id, - ); + if (request.data.group_id === undefined && request.data.timeout === undefined) { + sendEventPromise = this.driver.sendEvent( + request.data.type, + request.data.content || {}, + request.data.state_key, + request.data.room_id, + ); + } else { + sendEventPromise = this.driver.sendFuture( + request.data.group_id ?? null, + request.data.timeout ?? null, + request.data.type, + request.data.content || {}, + request.data.state_key, + request.data.room_id, + ); + } } else { const content = request.data.content as { msgtype?: string } || {}; const msgtype = content['msgtype']; @@ -504,18 +511,37 @@ export class ClientWidgetApi extends EventEmitter { }); } - sendEventPromise = this.driver.sendEvent( - request.data.type, - content, - null, // not sending a state event - request.data.room_id, - ); + if (request.data.group_id === undefined && request.data.timeout === undefined) { + sendEventPromise = this.driver.sendEvent( + request.data.type, + content, + null, // not sending a state event + request.data.room_id, + ); + } else { + sendEventPromise = this.driver.sendFuture( + request.data.group_id ?? null, + request.data.timeout ?? null, + request.data.type, + content, + null, // not sending a state event + request.data.room_id, + ); + } } sendEventPromise.then(sentEvent => { return this.transport.reply(request, { room_id: sentEvent.roomId, - event_id: sentEvent.eventId, + ...("eventId" in sentEvent ? { + event_id: sentEvent.eventId, + } : { + event_id: "", + group_id: sentEvent.futureGroupId, + send_token: sentEvent.sendToken, + cancel_token: sentEvent.cancelToken, + refresh_token: sentEvent.refreshToken, + }), }); }).catch(e => { console.error("error sending event: ", e); @@ -525,63 +551,6 @@ export class ClientWidgetApi extends EventEmitter { }); } - private handleSendFuture(request: ISendFutureFromWidgetActionRequest) { - if (!request.data.type) { - return this.transport.reply(request, { - error: {message: "Invalid request - missing event type"}, - }); - } - - let sendFuturePromise: Promise; - if (request.data.state_key != null) { - if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { - return this.transport.reply(request, { - error: {message: "Cannot send state futures of this type"}, - }); - } - - sendFuturePromise = this.driver.sendFuture( - request.data.future_group_id ?? null, - request.data.future_timeout ?? null, - request.data.type, - request.data.content || {}, - request.data.state_key, - request.data.room_id, - ); - } else { - const content = request.data.content as { msgtype?: string } || {}; - const msgtype = content['msgtype']; - if (!this.canSendRoomEvent(request.data.type, msgtype)) { - return this.transport.reply(request, { - error: {message: "Cannot send room futures of this type"}, - }); - } - - sendFuturePromise = this.driver.sendFuture( - request.data.future_group_id ?? null, - request.data.future_timeout ?? null, - request.data.type, - content, - null, // not sending a state event - request.data.room_id, - ); - } - - sendFuturePromise.then(sentFuture => { - return this.transport.reply(request, { - future_group_id: sentFuture.futureGroupId, - send_token: sentFuture.sendToken, - cancel_token: sentFuture.cancelToken, - refresh_token: sentFuture.refreshToken, - }); - }).catch(e => { - console.error("error sending future: ", e); - return this.transport.reply(request, { - error: {message: "Error sending future"}, - }); - }); - } - private async handleSendToDevice(request: ISendToDeviceFromWidgetActionRequest): Promise { if (!request.data.type) { await this.transport.reply(request, { @@ -830,8 +799,6 @@ export class ClientWidgetApi extends EventEmitter { return this.replyVersions(ev.detail); case WidgetApiFromWidgetAction.SendEvent: return this.handleSendEvent(ev.detail); - case WidgetApiFromWidgetAction.SendFuture: - return this.handleSendFuture(ev.detail); case WidgetApiFromWidgetAction.SendToDevice: return this.handleSendToDevice(ev.detail); case WidgetApiFromWidgetAction.GetOpenIDCredentials: diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index 59895da..f387f79 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -54,11 +54,6 @@ import { } from "./interfaces/ModalWidgetActions"; import { ISetModalButtonEnabledActionRequestData } from "./interfaces/SetModalButtonEnabledAction"; import { ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData } from "./interfaces/SendEventAction"; -import { - ISendFutureFromWidgetRequestData, - ISendFutureFromWidgetResponseData, - ISendFutureOptions, -} from "./interfaces/SendFutureAction"; import { ISendToDeviceFromWidgetRequestData, ISendToDeviceFromWidgetResponseData, @@ -428,14 +423,15 @@ export class WidgetApi extends EventEmitter { * @experimental This currently relies on an unstable MSC (MSC4140). */ public sendRoomFuture( - futureOpts: ISendFutureOptions, + timeout: number, eventType: string, content: unknown, + groupId?: string, roomId?: string, - ): Promise { - return this.transport.send( - WidgetApiFromWidgetAction.SendFuture, - {type: eventType, content, room_id: roomId, ...futureOpts}, + ): Promise { + return this.transport.send( + WidgetApiFromWidgetAction.SendEvent, + {type: eventType, content, room_id: roomId, timeout: timeout, group_id: groupId}, ); } @@ -443,15 +439,16 @@ export class WidgetApi extends EventEmitter { * @experimental This currently relies on an unstable MSC (MSC4140). */ public sendStateFuture( - futureOpts: ISendFutureOptions, + timeout: number, eventType: string, stateKey: string, content: unknown, + groupId?: string, roomId?: string, - ): Promise { - return this.transport.send( - WidgetApiFromWidgetAction.SendFuture, - {type: eventType, content, state_key: stateKey, room_id: roomId, ...futureOpts}, + ): Promise { + return this.transport.send( + WidgetApiFromWidgetAction.SendEvent, + {type: eventType, content, state_key: stateKey, room_id: roomId, timeout: timeout, group_id: groupId}, ); } diff --git a/src/interfaces/SendEventAction.ts b/src/interfaces/SendEventAction.ts index 8fe6da0..cdf4375 100644 --- a/src/interfaces/SendEventAction.ts +++ b/src/interfaces/SendEventAction.ts @@ -24,6 +24,10 @@ export interface ISendEventFromWidgetRequestData extends IWidgetApiRequestData { type: string; content: unknown; room_id?: string; // eslint-disable-line camelcase + + // MSC4140: Futures + timeout?: number; + group_id?: string; // eslint-disable-line camelcase } export interface ISendEventFromWidgetActionRequest extends IWidgetApiRequest { @@ -34,6 +38,12 @@ export interface ISendEventFromWidgetActionRequest extends IWidgetApiRequest { export interface ISendEventFromWidgetResponseData extends IWidgetApiResponseData { room_id: string; // eslint-disable-line camelcase event_id: string; // eslint-disable-line camelcase + + // MSC4140: Futures + group_id?: string; // eslint-disable-line camelcase + send_token?: string; // eslint-disable-line camelcase + cancel_token?: string; // eslint-disable-line camelcase + refresh_token?: string; // eslint-disable-line camelcase } export interface ISendEventFromWidgetActionResponse extends ISendEventFromWidgetActionRequest { diff --git a/src/interfaces/SendFutureAction.ts b/src/interfaces/SendFutureAction.ts deleted file mode 100644 index dcee90c..0000000 --- a/src/interfaces/SendFutureAction.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2020 - 2024 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. - */ - -import { IWidgetApiRequest } from "./IWidgetApiRequest"; -import { IWidgetApiResponseData } from "./IWidgetApiResponse"; -import { ISendEventFromWidgetRequestData } from "./SendEventAction"; -import { WidgetApiFromWidgetAction } from "./WidgetApiAction"; - -export interface ISendFutureOptions { - future_group_id?: string; // eslint-disable-line camelcase - future_timeout?: number; // eslint-disable-line camelcase -} - -export interface ISendFutureFromWidgetRequestData extends ISendEventFromWidgetRequestData, ISendFutureOptions {} - -export interface ISendFutureFromWidgetActionRequest extends IWidgetApiRequest { - action: WidgetApiFromWidgetAction.SendFuture; - data: ISendFutureFromWidgetRequestData; -} - -export interface ISendFutureFromWidgetResponseData extends IWidgetApiResponseData { - future_group_id: string; // eslint-disable-line camelcase - send_token: string; // eslint-disable-line camelcase - cancel_token: string; // eslint-disable-line camelcase - refresh_token?: string; // eslint-disable-line camelcase -} diff --git a/src/interfaces/WidgetApiAction.ts b/src/interfaces/WidgetApiAction.ts index 6dd8244..684dc1b 100644 --- a/src/interfaces/WidgetApiAction.ts +++ b/src/interfaces/WidgetApiAction.ts @@ -39,7 +39,6 @@ export enum WidgetApiFromWidgetAction { OpenModalWidget = "open_modal", SetModalButtonEnabled = "set_button_enabled", SendEvent = "send_event", - SendFuture = "send_future", SendToDevice = "send_to_device", WatchTurnServers = "watch_turn_servers", UnwatchTurnServers = "unwatch_turn_servers", From d0f024dd0bbdd3113727023687835f28033c9cd1 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 3 Jul 2024 11:03:03 -0400 Subject: [PATCH 03/15] Allow optional timeout for future send requests --- src/WidgetApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index f387f79..deff5d2 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -423,9 +423,9 @@ export class WidgetApi extends EventEmitter { * @experimental This currently relies on an unstable MSC (MSC4140). */ public sendRoomFuture( - timeout: number, eventType: string, content: unknown, + timeout?: number, groupId?: string, roomId?: string, ): Promise { @@ -439,10 +439,10 @@ export class WidgetApi extends EventEmitter { * @experimental This currently relies on an unstable MSC (MSC4140). */ public sendStateFuture( - timeout: number, eventType: string, stateKey: string, content: unknown, + timeout?: number, groupId?: string, roomId?: string, ): Promise { From 43e7e74a50966ff2cdcc380dabb122097b41fc57 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 4 Jul 2024 11:40:15 -0400 Subject: [PATCH 04/15] Prefix futures fields with "future_" --- src/ClientWidgetApi.ts | 14 +++++++------- src/WidgetApi.ts | 4 ++-- src/interfaces/SendEventAction.ts | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index c09b485..0e21ef3 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -485,7 +485,7 @@ export class ClientWidgetApi extends EventEmitter { }); } - if (request.data.group_id === undefined && request.data.timeout === undefined) { + if (request.data.future_group_id === undefined && request.data.future_timeout === undefined) { sendEventPromise = this.driver.sendEvent( request.data.type, request.data.content || {}, @@ -494,8 +494,8 @@ export class ClientWidgetApi extends EventEmitter { ); } else { sendEventPromise = this.driver.sendFuture( - request.data.group_id ?? null, - request.data.timeout ?? null, + request.data.future_group_id ?? null, + request.data.future_timeout ?? null, request.data.type, request.data.content || {}, request.data.state_key, @@ -511,7 +511,7 @@ export class ClientWidgetApi extends EventEmitter { }); } - if (request.data.group_id === undefined && request.data.timeout === undefined) { + if (request.data.future_group_id === undefined && request.data.future_timeout === undefined) { sendEventPromise = this.driver.sendEvent( request.data.type, content, @@ -520,8 +520,8 @@ export class ClientWidgetApi extends EventEmitter { ); } else { sendEventPromise = this.driver.sendFuture( - request.data.group_id ?? null, - request.data.timeout ?? null, + request.data.future_group_id ?? null, + request.data.future_timeout ?? null, request.data.type, content, null, // not sending a state event @@ -537,7 +537,7 @@ export class ClientWidgetApi extends EventEmitter { event_id: sentEvent.eventId, } : { event_id: "", - group_id: sentEvent.futureGroupId, + future_group_id: sentEvent.futureGroupId, send_token: sentEvent.sendToken, cancel_token: sentEvent.cancelToken, refresh_token: sentEvent.refreshToken, diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index deff5d2..d1df7ef 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -431,7 +431,7 @@ export class WidgetApi extends EventEmitter { ): Promise { return this.transport.send( WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, room_id: roomId, timeout: timeout, group_id: groupId}, + {type: eventType, content, room_id: roomId, future_timeout: timeout, future_group_id: groupId}, ); } @@ -448,7 +448,7 @@ export class WidgetApi extends EventEmitter { ): Promise { return this.transport.send( WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, state_key: stateKey, room_id: roomId, timeout: timeout, group_id: groupId}, + {type: eventType, content, state_key: stateKey, room_id: roomId, future_timeout: timeout, future_group_id: groupId}, ); } diff --git a/src/interfaces/SendEventAction.ts b/src/interfaces/SendEventAction.ts index cdf4375..b689047 100644 --- a/src/interfaces/SendEventAction.ts +++ b/src/interfaces/SendEventAction.ts @@ -25,9 +25,9 @@ export interface ISendEventFromWidgetRequestData extends IWidgetApiRequestData { content: unknown; room_id?: string; // eslint-disable-line camelcase - // MSC4140: Futures - timeout?: number; - group_id?: string; // eslint-disable-line camelcase + // MSC4157: Futures + future_timeout?: number; + future_group_id?: string; // eslint-disable-line camelcase } export interface ISendEventFromWidgetActionRequest extends IWidgetApiRequest { @@ -39,8 +39,8 @@ export interface ISendEventFromWidgetResponseData extends IWidgetApiResponseData room_id: string; // eslint-disable-line camelcase event_id: string; // eslint-disable-line camelcase - // MSC4140: Futures - group_id?: string; // eslint-disable-line camelcase + // MSC4157: Futures + future_group_id?: string; // eslint-disable-line camelcase send_token?: string; // eslint-disable-line camelcase cancel_token?: string; // eslint-disable-line camelcase refresh_token?: string; // eslint-disable-line camelcase From 3c388f0b5a55c1ceb93f791f267854b0afbb5b42 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 4 Jul 2024 11:42:39 -0400 Subject: [PATCH 05/15] Merge send*Future into send*Event --- src/WidgetApi.ts | 54 +++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index d1df7ef..222cc3c 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -400,10 +400,18 @@ export class WidgetApi extends EventEmitter { eventType: string, content: unknown, roomId?: string, + futureTimeout?: number, + futureGroupId?: string, ): Promise { return this.transport.send( WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, room_id: roomId}, + { + type: eventType, + content, + ...(roomId !== undefined && { room_id: roomId }), + ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), + ...(futureGroupId !== undefined && { future_group_id: futureGroupId }) + }, ); } @@ -412,43 +420,19 @@ export class WidgetApi extends EventEmitter { stateKey: string, content: unknown, roomId?: string, + futureTimeout?: number, + futureGroupId?: string, ): Promise { return this.transport.send( WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, state_key: stateKey, room_id: roomId}, - ); - } - - /** - * @experimental This currently relies on an unstable MSC (MSC4140). - */ - public sendRoomFuture( - eventType: string, - content: unknown, - timeout?: number, - groupId?: string, - roomId?: string, - ): Promise { - return this.transport.send( - WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, room_id: roomId, future_timeout: timeout, future_group_id: groupId}, - ); - } - - /** - * @experimental This currently relies on an unstable MSC (MSC4140). - */ - public sendStateFuture( - eventType: string, - stateKey: string, - content: unknown, - timeout?: number, - groupId?: string, - roomId?: string, - ): Promise { - return this.transport.send( - WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, state_key: stateKey, room_id: roomId, future_timeout: timeout, future_group_id: groupId}, + { + type: eventType, + content, + state_key: stateKey, + ...(roomId !== undefined && { room_id: roomId }), + ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), + ...(futureGroupId !== undefined && { future_group_id: futureGroupId }) + }, ); } From c3eb46b004d87ed944dea4911c65b595f37901b7 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 4 Jul 2024 15:47:37 -0400 Subject: [PATCH 06/15] Lint --- src/WidgetApi.ts | 4 ++-- src/interfaces/SendEventAction.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index 222cc3c..7e09bdb 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -410,7 +410,7 @@ export class WidgetApi extends EventEmitter { content, ...(roomId !== undefined && { room_id: roomId }), ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), - ...(futureGroupId !== undefined && { future_group_id: futureGroupId }) + ...(futureGroupId !== undefined && { future_group_id: futureGroupId }), }, ); } @@ -431,7 +431,7 @@ export class WidgetApi extends EventEmitter { state_key: stateKey, ...(roomId !== undefined && { room_id: roomId }), ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), - ...(futureGroupId !== undefined && { future_group_id: futureGroupId }) + ...(futureGroupId !== undefined && { future_group_id: futureGroupId }), }, ); } diff --git a/src/interfaces/SendEventAction.ts b/src/interfaces/SendEventAction.ts index b689047..2b30527 100644 --- a/src/interfaces/SendEventAction.ts +++ b/src/interfaces/SendEventAction.ts @@ -26,7 +26,7 @@ export interface ISendEventFromWidgetRequestData extends IWidgetApiRequestData { room_id?: string; // eslint-disable-line camelcase // MSC4157: Futures - future_timeout?: number; + future_timeout?: number; // eslint-disable-line camelcase future_group_id?: string; // eslint-disable-line camelcase } From d3ed37da59c34243f93c91f0eda84cf297238698 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Mon, 8 Jul 2024 08:20:57 -0400 Subject: [PATCH 07/15] Revert copyright year in unchanged file --- src/interfaces/WidgetApiAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/WidgetApiAction.ts b/src/interfaces/WidgetApiAction.ts index 684dc1b..de78e52 100644 --- a/src/interfaces/WidgetApiAction.ts +++ b/src/interfaces/WidgetApiAction.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2024 The Matrix.org Foundation C.I.C. + * Copyright 2020 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. From 39f89c4de53649eecdc20ce7357be07536e0f346 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 11:24:15 -0400 Subject: [PATCH 08/15] Update some comments --- src/driver/WidgetDriver.ts | 4 +++- src/interfaces/SendEventAction.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/driver/WidgetDriver.ts b/src/driver/WidgetDriver.ts index 292a07e..10873d1 100644 --- a/src/driver/WidgetDriver.ts +++ b/src/driver/WidgetDriver.ts @@ -112,12 +112,14 @@ export abstract class WidgetDriver { } /** + * @experimental Part of MSC4140 & MSC4157 * Sends a future into a room. If `roomId` is falsy, the client should send the future * into the room the user is currently looking at. The widget API will have already * verified that the widget is capable of sending the future's event to that room. * @param {string|null} futureGroupId The ID of the group the future belongs to, - * or null if it will be put in a new group. + * or null if it will be put in a new group. Must not be null if {@link futureTimeout} is null. * @param {number|null} futureTimeout The future's timeout, or null for an action future. + * Must not be null if {@link futureGroupId} is null. * @param {string} eventType The event type of the event to be sent by the future. * @param {*} content The content for the event to be sent by the future. * @param {string|null} stateKey The state key if the event to be sent by the future is diff --git a/src/interfaces/SendEventAction.ts b/src/interfaces/SendEventAction.ts index 2b30527..5654fc6 100644 --- a/src/interfaces/SendEventAction.ts +++ b/src/interfaces/SendEventAction.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2024 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. From 0c2699370237f5bc7a13bc093389756fdc69ed25 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 12:46:38 -0400 Subject: [PATCH 09/15] Omit refresh token from response if undefined --- src/ClientWidgetApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index 0e21ef3..da766ae 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -540,7 +540,7 @@ export class ClientWidgetApi extends EventEmitter { future_group_id: sentEvent.futureGroupId, send_token: sentEvent.sendToken, cancel_token: sentEvent.cancelToken, - refresh_token: sentEvent.refreshToken, + ...("refreshToken" in sentEvent && { refresh_token: sentEvent.refreshToken }), }), }); }).catch(e => { From 327741b305e6cd4b6020517f411e9ab61eac3d57 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 16:41:06 -0400 Subject: [PATCH 10/15] Don't check for impossible null --- src/ClientWidgetApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index da766ae..68406ad 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -478,7 +478,7 @@ export class ClientWidgetApi extends EventEmitter { } let sendEventPromise: Promise; - if (request.data.state_key != null) { + if (request.data.state_key !== undefined) { if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { return this.transport.reply(request, { error: {message: "Cannot send state events of this type"}, From 29e70bd8d09b077fe29132d7064dfe0c918b7e05 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 17:59:38 -0400 Subject: [PATCH 11/15] Make timeout & group_id parameter order consistent with timeout coming first because all future groups require a timeout. --- src/ClientWidgetApi.ts | 8 ++++---- src/driver/WidgetDriver.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index 68406ad..6a4e614 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -485,7 +485,7 @@ export class ClientWidgetApi extends EventEmitter { }); } - if (request.data.future_group_id === undefined && request.data.future_timeout === undefined) { + if (request.data.future_timeout === undefined && request.data.future_group_id === undefined) { sendEventPromise = this.driver.sendEvent( request.data.type, request.data.content || {}, @@ -494,8 +494,8 @@ export class ClientWidgetApi extends EventEmitter { ); } else { sendEventPromise = this.driver.sendFuture( - request.data.future_group_id ?? null, request.data.future_timeout ?? null, + request.data.future_group_id ?? null, request.data.type, request.data.content || {}, request.data.state_key, @@ -511,7 +511,7 @@ export class ClientWidgetApi extends EventEmitter { }); } - if (request.data.future_group_id === undefined && request.data.future_timeout === undefined) { + if (request.data.future_timeout === undefined && request.data.future_group_id === undefined) { sendEventPromise = this.driver.sendEvent( request.data.type, content, @@ -520,8 +520,8 @@ export class ClientWidgetApi extends EventEmitter { ); } else { sendEventPromise = this.driver.sendFuture( - request.data.future_group_id ?? null, request.data.future_timeout ?? null, + request.data.future_group_id ?? null, request.data.type, content, null, // not sending a state event diff --git a/src/driver/WidgetDriver.ts b/src/driver/WidgetDriver.ts index 10873d1..f847bdf 100644 --- a/src/driver/WidgetDriver.ts +++ b/src/driver/WidgetDriver.ts @@ -116,10 +116,10 @@ export abstract class WidgetDriver { * Sends a future into a room. If `roomId` is falsy, the client should send the future * into the room the user is currently looking at. The widget API will have already * verified that the widget is capable of sending the future's event to that room. - * @param {string|null} futureGroupId The ID of the group the future belongs to, - * or null if it will be put in a new group. Must not be null if {@link futureTimeout} is null. * @param {number|null} futureTimeout The future's timeout, or null for an action future. - * Must not be null if {@link futureGroupId} is null. + * May not be null if {@link futureGroupId} is null. + * @param {string|null} futureGroupId The ID of the group the future belongs to, + * or null if it will be put in a new group. May not be null if {@link futureTimeout} is null. * @param {string} eventType The event type of the event to be sent by the future. * @param {*} content The content for the event to be sent by the future. * @param {string|null} stateKey The state key if the event to be sent by the future is @@ -131,8 +131,8 @@ export abstract class WidgetDriver { * @throws Rejected when the future could not be sent. */ public sendFuture( - futureGroupId: string | null, futureTimeout: number | null, + futureGroupId: string | null, eventType: string, content: unknown, stateKey: string | null = null, From 595250d153de259abd84d1972eba54f5c7cc0046 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 18:02:41 -0400 Subject: [PATCH 12/15] Commonize sendRoom/StateEvent to increase code reuse --- src/WidgetApi.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index 7e09bdb..807dc40 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -403,16 +403,7 @@ export class WidgetApi extends EventEmitter { futureTimeout?: number, futureGroupId?: string, ): Promise { - return this.transport.send( - WidgetApiFromWidgetAction.SendEvent, - { - type: eventType, - content, - ...(roomId !== undefined && { room_id: roomId }), - ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), - ...(futureGroupId !== undefined && { future_group_id: futureGroupId }), - }, - ); + return this.sendEvent(eventType, undefined, content, roomId, futureTimeout, futureGroupId); } public sendStateEvent( @@ -422,13 +413,24 @@ export class WidgetApi extends EventEmitter { roomId?: string, futureTimeout?: number, futureGroupId?: string, + ): Promise { + return this.sendEvent(eventType, stateKey, content, roomId, futureTimeout, futureGroupId); + } + + private sendEvent( + eventType: string, + stateKey: string | undefined, + content: unknown, + roomId?: string, + futureTimeout?: number, + futureGroupId?: string, ): Promise { return this.transport.send( WidgetApiFromWidgetAction.SendEvent, { type: eventType, content, - state_key: stateKey, + ...(stateKey !== undefined && { state_key: stateKey }), ...(roomId !== undefined && { room_id: roomId }), ...(futureTimeout !== undefined && { future_timeout: futureTimeout }), ...(futureGroupId !== undefined && { future_group_id: futureGroupId }), From fe15b72b0107305964b8483634730617bdae2bf5 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 10 Jul 2024 18:02:51 -0400 Subject: [PATCH 13/15] Make event_id in the send_event response optional as proposed by MSC4157 --- src/ClientWidgetApi.ts | 1 - src/interfaces/SendEventAction.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index 6a4e614..7415bef 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -536,7 +536,6 @@ export class ClientWidgetApi extends EventEmitter { ...("eventId" in sentEvent ? { event_id: sentEvent.eventId, } : { - event_id: "", future_group_id: sentEvent.futureGroupId, send_token: sentEvent.sendToken, cancel_token: sentEvent.cancelToken, diff --git a/src/interfaces/SendEventAction.ts b/src/interfaces/SendEventAction.ts index 5654fc6..aa3d1df 100644 --- a/src/interfaces/SendEventAction.ts +++ b/src/interfaces/SendEventAction.ts @@ -37,7 +37,7 @@ export interface ISendEventFromWidgetActionRequest extends IWidgetApiRequest { export interface ISendEventFromWidgetResponseData extends IWidgetApiResponseData { room_id: string; // eslint-disable-line camelcase - event_id: string; // eslint-disable-line camelcase + event_id?: string; // eslint-disable-line camelcase // MSC4157: Futures future_group_id?: string; // eslint-disable-line camelcase From 79a06630555a67515b4f674260013a9dbbac950e Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Mon, 15 Jul 2024 10:00:03 -0400 Subject: [PATCH 14/15] Test event/future sending --- test/ClientWidgetApi-test.ts | 202 ++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 1 deletion(-) diff --git a/test/ClientWidgetApi-test.ts b/test/ClientWidgetApi-test.ts index 9d7ff80..476bac4 100644 --- a/test/ClientWidgetApi-test.ts +++ b/test/ClientWidgetApi-test.ts @@ -28,7 +28,7 @@ import { WidgetApiFromWidgetAction } from '../src/interfaces/WidgetApiAction'; import { WidgetApiDirection } from '../src/interfaces/WidgetApiDirection'; import { Widget } from '../src/models/Widget'; import { PostmessageTransport } from '../src/transport/PostmessageTransport'; -import { IReadEventFromWidgetActionRequest } from '../src'; +import { IReadEventFromWidgetActionRequest, ISendEventFromWidgetActionRequest } from '../src'; import { IGetMediaConfigActionFromWidgetActionRequest } from '../src/interfaces/GetMediaConfigAction'; import { IUploadFileActionFromWidgetActionRequest } from '../src/interfaces/UploadFileAction'; @@ -79,6 +79,8 @@ describe('ClientWidgetApi', () => { driver = { readStateEvents: jest.fn(), readEventRelations: jest.fn(), + sendEvent: jest.fn(), + sendFuture: jest.fn(), validateCapabilities: jest.fn(), searchUserDirectory: jest.fn(), getMediaConfig: jest.fn(), @@ -117,6 +119,204 @@ describe('ClientWidgetApi', () => { expect(clientWidgetApi.hasCapability('m.sticker')).toBe(false); }); + describe('send_event action', () => { + it('sends message events', async () => { + const roomId = '!room:example.org'; + const eventId = '$event:example.org'; + + driver.sendEvent.mockResolvedValue({ + roomId, + eventId, + }); + + const event: ISendEventFromWidgetActionRequest = { + api: WidgetApiDirection.FromWidget, + widgetId: 'test', + requestId: '0', + action: WidgetApiFromWidgetAction.SendEvent, + data: { + type: 'm.room.message', + content: {}, + room_id: roomId, + }, + }; + + await loadIframe([ + `org.matrix.msc2762.timeline:${event.data.room_id}`, + `org.matrix.msc2762.send.event:${event.data.type}`, + ]); + + emitEvent(new CustomEvent('', { detail: event })); + + await waitFor(() => { + expect(transport.reply).toHaveBeenCalledWith(event, { + room_id: roomId, + event_id: eventId, + }); + }); + + expect(driver.sendEvent).toHaveBeenCalledWith( + event.data.type, + event.data.content, + null, + roomId, + ); + }); + + it('sends state events', async () => { + const roomId = '!room:example.org'; + const eventId = '$event:example.org'; + + driver.sendEvent.mockResolvedValue({ + roomId, + eventId, + }); + + const event: ISendEventFromWidgetActionRequest = { + api: WidgetApiDirection.FromWidget, + widgetId: 'test', + requestId: '0', + action: WidgetApiFromWidgetAction.SendEvent, + data: { + type: 'm.room.topic', + content: {}, + state_key: '', + room_id: roomId, + }, + }; + + await loadIframe([ + `org.matrix.msc2762.timeline:${event.data.room_id}`, + `org.matrix.msc2762.send.state_event:${event.data.type}`, + ]); + + emitEvent(new CustomEvent('', { detail: event })); + + await waitFor(() => { + expect(transport.reply).toHaveBeenCalledWith(event, { + room_id: roomId, + event_id: eventId, + }); + }); + + expect(driver.sendEvent).toHaveBeenCalledWith( + event.data.type, + event.data.content, + '', + roomId, + ); + }); + }); + + describe('send_event action for futures', () => { + it('sends message futures', async () => { + const roomId = '!room:example.org'; + const futureGroupId = 'fg'; + + driver.sendFuture.mockResolvedValue({ + roomId, + futureGroupId, + sendToken: 'st', + cancelToken: 'ct', + refreshToken: 'rt', + }); + + const event: ISendEventFromWidgetActionRequest = { + api: WidgetApiDirection.FromWidget, + widgetId: 'test', + requestId: '0', + action: WidgetApiFromWidgetAction.SendEvent, + data: { + type: 'm.room.message', + content: {}, + room_id: roomId, + future_timeout: 5000, + future_group_id: futureGroupId, + }, + }; + + await loadIframe([ + `org.matrix.msc2762.timeline:${event.data.room_id}`, + `org.matrix.msc2762.send.event:${event.data.type}`, + ]); + + emitEvent(new CustomEvent('', { detail: event })); + + await waitFor(() => { + expect(transport.reply).toHaveBeenCalledWith(event, { + room_id: roomId, + future_group_id: futureGroupId, + send_token: 'st', + cancel_token: 'ct', + refresh_token: 'rt', + }); + }); + + expect(driver.sendFuture).toHaveBeenCalledWith( + event.data.future_timeout, + event.data.future_group_id, + event.data.type, + event.data.content, + null, + roomId, + ); + }); + + it('sends state future', async () => { + const roomId = '!room:example.org'; + const futureGroupId = 'fg'; + + driver.sendFuture.mockResolvedValue({ + roomId, + futureGroupId, + sendToken: 'st', + cancelToken: 'ct', + refreshToken: 'rt', + }); + + const event: ISendEventFromWidgetActionRequest = { + api: WidgetApiDirection.FromWidget, + widgetId: 'test', + requestId: '0', + action: WidgetApiFromWidgetAction.SendEvent, + data: { + type: 'm.room.topic', + content: {}, + state_key: '', + room_id: roomId, + future_timeout: 5000, + future_group_id: futureGroupId, + }, + }; + + await loadIframe([ + `org.matrix.msc2762.timeline:${event.data.room_id}`, + `org.matrix.msc2762.send.state_event:${event.data.type}`, + ]); + + emitEvent(new CustomEvent('', { detail: event })); + + await waitFor(() => { + expect(transport.reply).toHaveBeenCalledWith(event, { + room_id: roomId, + future_group_id: futureGroupId, + send_token: 'st', + cancel_token: 'ct', + refresh_token: 'rt', + }); + }); + + expect(driver.sendFuture).toHaveBeenCalledWith( + event.data.future_timeout, + event.data.future_group_id, + event.data.type, + event.data.content, + '', + roomId, + ); + }); + }); + describe('org.matrix.msc2876.read_events action', () => { it('reads state events with any state key', async () => { driver.readStateEvents.mockResolvedValue([ From 299e50ad8158b2b5fe62aa902de9db2bac4ffafb Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Mon, 15 Jul 2024 10:06:06 -0400 Subject: [PATCH 15/15] Consistent grammar --- test/ClientWidgetApi-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ClientWidgetApi-test.ts b/test/ClientWidgetApi-test.ts index 476bac4..c630286 100644 --- a/test/ClientWidgetApi-test.ts +++ b/test/ClientWidgetApi-test.ts @@ -262,7 +262,7 @@ describe('ClientWidgetApi', () => { ); }); - it('sends state future', async () => { + it('sends state futures', async () => { const roomId = '!room:example.org'; const futureGroupId = 'fg';