Skip to content

Commit 120a290

Browse files
authored
Split delayed event management methods (#143)
Instead of using a single "update" method to encompass all update actions (send/restart/cancel), give each action its own method. This makes it easier for widget driver implementations to call the newly-proposed split endpoints for those actions in the CS-API.
1 parent f3d80a3 commit 120a290

File tree

5 files changed

+240
-77
lines changed

5 files changed

+240
-77
lines changed

src/ClientWidgetApi.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -686,25 +686,31 @@ export class ClientWidgetApi extends EventEmitter {
686686
});
687687
}
688688

689+
let updateDelayedEvent: (delayId: string) => Promise<void>;
689690
switch (request.data.action) {
690691
case UpdateDelayedEventAction.Cancel:
692+
updateDelayedEvent = this.driver.cancelScheduledDelayedEvent;
693+
break;
691694
case UpdateDelayedEventAction.Restart:
695+
updateDelayedEvent = this.driver.restartScheduledDelayedEvent;
696+
break;
692697
case UpdateDelayedEventAction.Send:
693-
this.driver
694-
.updateDelayedEvent(request.data.delay_id, request.data.action)
695-
.then(() => {
696-
return this.transport.reply<IWidgetApiAcknowledgeResponseData>(request, {});
697-
})
698-
.catch((e: unknown) => {
699-
console.error("error updating delayed event: ", e);
700-
this.handleDriverError(e, request, "Error updating delayed event");
701-
});
698+
updateDelayedEvent = this.driver.sendScheduledDelayedEvent;
702699
break;
703700
default:
704701
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
705702
error: { message: "Invalid request - unsupported action" },
706703
});
707704
}
705+
updateDelayedEvent
706+
.call(this.driver, request.data.delay_id)
707+
.then(() => {
708+
return this.transport.reply<IWidgetApiAcknowledgeResponseData>(request, {});
709+
})
710+
.catch((e: unknown) => {
711+
console.error("error updating delayed event: ", e);
712+
this.handleDriverError(e, request, "Error updating delayed event");
713+
});
708714
}
709715

710716
private async handleSendToDevice(request: ISendToDeviceFromWidgetActionRequest): Promise<void> {

src/WidgetApi.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -476,15 +476,38 @@ export class WidgetApi extends EventEmitter {
476476
/**
477477
* @deprecated This currently relies on an unstable MSC (MSC4157).
478478
*/
479-
public updateDelayedEvent(
480-
delayId: string,
481-
action: UpdateDelayedEventAction,
482-
): Promise<IUpdateDelayedEventFromWidgetResponseData> {
479+
public cancelScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
483480
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
484481
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
485482
{
486483
delay_id: delayId,
487-
action,
484+
action: UpdateDelayedEventAction.Cancel,
485+
},
486+
);
487+
}
488+
489+
/**
490+
* @deprecated This currently relies on an unstable MSC (MSC4157).
491+
*/
492+
public restartScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
493+
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
494+
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
495+
{
496+
delay_id: delayId,
497+
action: UpdateDelayedEventAction.Restart,
498+
},
499+
);
500+
}
501+
502+
/**
503+
* @deprecated This currently relies on an unstable MSC (MSC4157).
504+
*/
505+
public sendScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
506+
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
507+
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
508+
{
509+
delay_id: delayId,
510+
action: UpdateDelayedEventAction.Send,
488511
},
489512
);
490513
}

src/driver/WidgetDriver.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
IRoomAccountData,
2424
ITurnServer,
2525
IWidgetApiErrorResponseDataDetails,
26-
UpdateDelayedEventAction,
2726
} from "..";
2827

2928
export interface ISendEventDetails {
@@ -142,10 +141,32 @@ export abstract class WidgetDriver {
142141

143142
/**
144143
* @experimental Part of MSC4140 & MSC4157
145-
* Run the specified {@link action} for the delayed event matching the provided {@link delayId}.
146-
* @throws Rejected when there is no matching delayed event, or when the action failed to run.
144+
* Cancel the scheduled delivery of the delayed event matching the provided {@link delayId}.
145+
* @throws Rejected when there is no matching delayed event,
146+
* or when the delayed event failed to be cancelled.
147147
*/
148-
public updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<void> {
148+
public cancelScheduledDelayedEvent(delayId: string): Promise<void> {
149+
return Promise.reject(new Error("Failed to override function"));
150+
}
151+
152+
/**
153+
* @experimental Part of MSC4140 & MSC4157
154+
* Restart the scheduled delivery of the delayed event matching the provided {@link delayId}.
155+
* @throws Rejected when there is no matching delayed event,
156+
* or when the delayed event failed to be restarted.
157+
*/
158+
public restartScheduledDelayedEvent(delayId: string): Promise<void> {
159+
return Promise.reject(new Error("Failed to override function"));
160+
}
161+
162+
/**
163+
* @experimental Part of MSC4140 & MSC4157
164+
* Immediately send the delayed event matching the provided {@link delayId},
165+
* instead of waiting for its scheduled delivery.
166+
* @throws Rejected when there is no matching delayed event,
167+
* or when the delayed event failed to be sent.
168+
*/
169+
public sendScheduledDelayedEvent(delayId: string): Promise<void> {
149170
return Promise.reject(new Error("Failed to override function"));
150171
}
151172

test/ClientWidgetApi-test.ts

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ describe("ClientWidgetApi", () => {
128128
readEventRelations: jest.fn(),
129129
sendEvent: jest.fn(),
130130
sendDelayedEvent: jest.fn(),
131-
updateDelayedEvent: jest.fn(),
131+
cancelScheduledDelayedEvent: jest.fn(),
132+
restartScheduledDelayedEvent: jest.fn(),
133+
sendScheduledDelayedEvent: jest.fn(),
132134
sendToDevice: jest.fn(),
133135
askOpenID: jest.fn(),
134136
readRoomAccountData: jest.fn(),
@@ -903,7 +905,57 @@ describe("ClientWidgetApi", () => {
903905
});
904906

905907
describe("update_delayed_event action", () => {
906-
it("fails to update delayed events", async () => {
908+
it("fails to cancel delayed events", async () => {
909+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
910+
api: WidgetApiDirection.FromWidget,
911+
widgetId: "test",
912+
requestId: "0",
913+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
914+
data: {
915+
delay_id: "f",
916+
action: UpdateDelayedEventAction.Cancel,
917+
},
918+
};
919+
920+
await loadIframe([]); // Without the required capability
921+
922+
emitEvent(new CustomEvent("", { detail: event }));
923+
924+
await waitFor(() => {
925+
expect(transport.reply).toBeCalledWith(event, {
926+
error: { message: expect.any(String) },
927+
});
928+
});
929+
930+
expect(driver.cancelScheduledDelayedEvent).not.toBeCalled();
931+
});
932+
933+
it("fails to restart delayed events", async () => {
934+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
935+
api: WidgetApiDirection.FromWidget,
936+
widgetId: "test",
937+
requestId: "0",
938+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
939+
data: {
940+
delay_id: "f",
941+
action: UpdateDelayedEventAction.Restart,
942+
},
943+
};
944+
945+
await loadIframe([]); // Without the required capability
946+
947+
emitEvent(new CustomEvent("", { detail: event }));
948+
949+
await waitFor(() => {
950+
expect(transport.reply).toBeCalledWith(event, {
951+
error: { message: expect.any(String) },
952+
});
953+
});
954+
955+
expect(driver.restartScheduledDelayedEvent).not.toBeCalled();
956+
});
957+
958+
it("fails to send delayed events", async () => {
907959
const event: IUpdateDelayedEventFromWidgetActionRequest = {
908960
api: WidgetApiDirection.FromWidget,
909961
widgetId: "test",
@@ -925,7 +977,7 @@ describe("ClientWidgetApi", () => {
925977
});
926978
});
927979

928-
expect(driver.updateDelayedEvent).not.toBeCalled();
980+
expect(driver.sendScheduledDelayedEvent).not.toBeCalled();
929981
});
930982

931983
it("fails to update delayed events with unsupported action", async () => {
@@ -950,42 +1002,88 @@ describe("ClientWidgetApi", () => {
9501002
});
9511003
});
9521004

953-
expect(driver.updateDelayedEvent).not.toBeCalled();
1005+
expect(driver.cancelScheduledDelayedEvent).not.toBeCalled();
1006+
expect(driver.restartScheduledDelayedEvent).not.toBeCalled();
1007+
expect(driver.sendScheduledDelayedEvent).not.toBeCalled();
9541008
});
9551009

956-
it("updates delayed events", async () => {
957-
driver.updateDelayedEvent.mockResolvedValue(undefined);
1010+
it("can cancel delayed events", async () => {
1011+
driver.cancelScheduledDelayedEvent.mockResolvedValue(undefined);
9581012

959-
for (const action of [
960-
UpdateDelayedEventAction.Cancel,
961-
UpdateDelayedEventAction.Restart,
962-
UpdateDelayedEventAction.Send,
963-
]) {
964-
const event: IUpdateDelayedEventFromWidgetActionRequest = {
965-
api: WidgetApiDirection.FromWidget,
966-
widgetId: "test",
967-
requestId: "0",
968-
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
969-
data: {
970-
delay_id: "f",
971-
action,
972-
},
973-
};
1013+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1014+
api: WidgetApiDirection.FromWidget,
1015+
widgetId: "test",
1016+
requestId: "0",
1017+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1018+
data: {
1019+
delay_id: "f",
1020+
action: UpdateDelayedEventAction.Cancel,
1021+
},
1022+
};
9741023

975-
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1024+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
9761025

977-
emitEvent(new CustomEvent("", { detail: event }));
1026+
emitEvent(new CustomEvent("", { detail: event }));
9781027

979-
await waitFor(() => {
980-
expect(transport.reply).toHaveBeenCalledWith(event, {});
981-
});
1028+
await waitFor(() => {
1029+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1030+
});
1031+
1032+
expect(driver.cancelScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
1033+
});
1034+
1035+
it("can restart delayed events", async () => {
1036+
driver.restartScheduledDelayedEvent.mockResolvedValue(undefined);
1037+
1038+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1039+
api: WidgetApiDirection.FromWidget,
1040+
widgetId: "test",
1041+
requestId: "0",
1042+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1043+
data: {
1044+
delay_id: "f",
1045+
action: UpdateDelayedEventAction.Restart,
1046+
},
1047+
};
1048+
1049+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1050+
1051+
emitEvent(new CustomEvent("", { detail: event }));
1052+
1053+
await waitFor(() => {
1054+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1055+
});
1056+
1057+
expect(driver.restartScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
1058+
});
1059+
1060+
it("can send delayed events", async () => {
1061+
driver.sendScheduledDelayedEvent.mockResolvedValue(undefined);
1062+
1063+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1064+
api: WidgetApiDirection.FromWidget,
1065+
widgetId: "test",
1066+
requestId: "0",
1067+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1068+
data: {
1069+
delay_id: "f",
1070+
action: UpdateDelayedEventAction.Send,
1071+
},
1072+
};
1073+
1074+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1075+
1076+
emitEvent(new CustomEvent("", { detail: event }));
1077+
1078+
await waitFor(() => {
1079+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1080+
});
9821081

983-
expect(driver.updateDelayedEvent).toHaveBeenCalledWith(event.data.delay_id, event.data.action);
984-
}
1082+
expect(driver.sendScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
9851083
});
9861084

9871085
it("should reject requests when the driver throws an exception", async () => {
988-
driver.updateDelayedEvent.mockRejectedValue(new Error("M_BAD_JSON: Content must be a JSON object"));
1086+
driver.sendScheduledDelayedEvent.mockRejectedValue(new Error("M_BAD_JSON: Content must be a JSON object"));
9891087

9901088
const event: IUpdateDelayedEventFromWidgetActionRequest = {
9911089
api: WidgetApiDirection.FromWidget,
@@ -1012,7 +1110,7 @@ describe("ClientWidgetApi", () => {
10121110
it("should reject with Matrix API error response thrown by driver", async () => {
10131111
driver.processError.mockImplementation(processCustomMatrixError);
10141112

1015-
driver.updateDelayedEvent.mockRejectedValue(
1113+
driver.sendScheduledDelayedEvent.mockRejectedValue(
10161114
new CustomMatrixError("failed to update delayed event", 400, "M_NOT_JSON", {
10171115
reason: "Content must be a JSON object.",
10181116
}),

0 commit comments

Comments
 (0)