Skip to content

Commit df88edf

Browse files
Delayed event management: split endpoints, no auth (#5066)
* Delayed event management: split endpoints, no auth Add dedicated endpoints for each of the cancel/restart/send actions for updating a delayed event, and make them unauthenticated. Also keep support for the original endpoint where the update action is in the request body, and make the split-endpoint versions fall back to it if they are unsupported by the homeserver. * Don't @link parameters in method docstrings as TypeDoc doesn't support that * Reduce code duplication * Reduce code duplication again * Add a little more test coverage * Use split delayed event management for widgets * Specify which eslint rule to ignore Co-authored-by: Will Hunt <[email protected]> * Restore embedded non-split delay evt update method Keep supporting it to not break widgets that currently use it. Also add back the test for it. * Deprecate the non-split delay evt update methods * Comment to explain fallback to non-split endpoint * Add backwards compatibility with authed endpoints * Comment backwards compatibility helper method * Await returned promises because `return await promise` is at least as fast as `return promise` --------- Co-authored-by: Will Hunt <[email protected]>
1 parent 1dee1ba commit df88edf

File tree

12 files changed

+484
-82
lines changed

12 files changed

+484
-82
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"jwt-decode": "^4.0.0",
5757
"loglevel": "^1.9.2",
5858
"matrix-events-sdk": "0.0.1",
59-
"matrix-widget-api": "^1.10.0",
59+
"matrix-widget-api": "^1.14.0",
6060
"oidc-client-ts": "^3.0.1",
6161
"p-retry": "7",
6262
"sdp-transform": "^3.0.0",

spec/unit/embedded.spec.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ class MockWidgetApi extends EventEmitter {
8686
? { event_id: `$${Math.random()}` }
8787
: { delay_id: `id-${Math.random()}` },
8888
);
89-
public updateDelayedEvent = jest.fn().mockResolvedValue(undefined);
89+
public cancelScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
90+
public restartScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
91+
public sendScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
9092
public sendToDevice = jest.fn().mockResolvedValue(undefined);
9193
public requestOpenIDConnectToken = jest.fn(async () => {
9294
return testOIDCToken;
@@ -531,17 +533,49 @@ describe("RoomWidgetClient", () => {
531533
).rejects.toThrow();
532534
});
533535

534-
it("updates delayed events", async () => {
536+
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
537+
"can %s scheduled delayed events (action in parameter)",
538+
async (action: UpdateDelayedEventAction) => {
539+
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
540+
expect(widgetApi.requestCapability).toHaveBeenCalledWith(
541+
MatrixCapabilities.MSC4157UpdateDelayedEvent,
542+
);
543+
await client._unstable_updateDelayedEvent("id", action);
544+
let updateDelayedEvent: (delayId: string) => Promise<unknown>;
545+
switch (action) {
546+
case UpdateDelayedEventAction.Cancel:
547+
updateDelayedEvent = widgetApi.cancelScheduledDelayedEvent;
548+
break;
549+
case UpdateDelayedEventAction.Restart:
550+
updateDelayedEvent = widgetApi.cancelScheduledDelayedEvent;
551+
break;
552+
case UpdateDelayedEventAction.Send:
553+
updateDelayedEvent = widgetApi.sendScheduledDelayedEvent;
554+
break;
555+
}
556+
expect(updateDelayedEvent).toHaveBeenCalledWith("id");
557+
},
558+
);
559+
560+
it("can cancel scheduled delayed events (action in method)", async () => {
535561
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
536562
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
537-
for (const action of [
538-
UpdateDelayedEventAction.Cancel,
539-
UpdateDelayedEventAction.Restart,
540-
UpdateDelayedEventAction.Send,
541-
]) {
542-
await client._unstable_updateDelayedEvent("id", action);
543-
expect(widgetApi.updateDelayedEvent).toHaveBeenCalledWith("id", action);
544-
}
563+
await client._unstable_cancelScheduledDelayedEvent("id");
564+
expect(widgetApi.cancelScheduledDelayedEvent).toHaveBeenCalledWith("id");
565+
});
566+
567+
it("can restart scheduled delayed events (action in method)", async () => {
568+
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
569+
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
570+
await client._unstable_restartScheduledDelayedEvent("id");
571+
expect(widgetApi.restartScheduledDelayedEvent).toHaveBeenCalledWith("id");
572+
});
573+
574+
it("can send scheduled delayed events (action in method)", async () => {
575+
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
576+
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
577+
await client._unstable_sendScheduledDelayedEvent("id");
578+
expect(widgetApi.sendScheduledDelayedEvent).toHaveBeenCalledWith("id");
545579
});
546580
});
547581

@@ -583,6 +617,13 @@ describe("RoomWidgetClient", () => {
583617
"Server does not support",
584618
);
585619
}
620+
for (const updateDelayedEvent of [
621+
client._unstable_cancelScheduledDelayedEvent,
622+
client._unstable_restartScheduledDelayedEvent,
623+
client._unstable_sendScheduledDelayedEvent,
624+
]) {
625+
await expect(updateDelayedEvent.call(client, "id")).rejects.toThrow("Server does not support");
626+
}
586627
});
587628
});
588629
});

spec/unit/matrix-client.spec.ts

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,10 @@ describe("MatrixClient", function () {
801801
await expect(
802802
client._unstable_updateDelayedEvent("anyDelayId", UpdateDelayedEventAction.Send),
803803
).rejects.toThrow(errorMessage);
804+
805+
await expect(client._unstable_cancelScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
806+
await expect(client._unstable_restartScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
807+
await expect(client._unstable_sendScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
804808
});
805809

806810
it("works with null threadId", async () => {
@@ -1077,21 +1081,169 @@ describe("MatrixClient", function () {
10771081
});
10781082
});
10791083

1080-
it("can update delayed events", async () => {
1084+
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
1085+
"can %s scheduled delayed events (action in request body)",
1086+
async (action: UpdateDelayedEventAction) => {
1087+
const delayId = "id";
1088+
httpLookups = [
1089+
{
1090+
method: "POST",
1091+
prefix: unstableMSC4140Prefix,
1092+
path: `/delayed_events/${encodeURIComponent(delayId)}`,
1093+
data: {
1094+
action,
1095+
},
1096+
},
1097+
];
1098+
1099+
await client._unstable_updateDelayedEvent(delayId, action);
1100+
},
1101+
);
1102+
1103+
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
1104+
"can %s scheduled delayed events (action in request body fallback when auth required)",
1105+
async (action: UpdateDelayedEventAction) => {
1106+
const delayId = "id";
1107+
const baseLookup = {
1108+
method: "POST",
1109+
prefix: unstableMSC4140Prefix,
1110+
path: `/delayed_events/${encodeURIComponent(delayId)}`,
1111+
};
1112+
httpLookups = [
1113+
{
1114+
...baseLookup,
1115+
error: {
1116+
httpStatus: 401,
1117+
errcode: "M_MISSING_TOKEN",
1118+
},
1119+
},
1120+
{
1121+
...baseLookup,
1122+
data: {
1123+
action,
1124+
},
1125+
},
1126+
];
1127+
1128+
await client._unstable_updateDelayedEvent(delayId, action);
1129+
},
1130+
);
1131+
1132+
it("can cancel scheduled delayed events (action in request path)", async () => {
1133+
const delayId = "id";
1134+
httpLookups = [
1135+
{
1136+
method: "POST",
1137+
prefix: unstableMSC4140Prefix,
1138+
path: `/delayed_events/${encodeURIComponent(delayId)}/cancel`,
1139+
},
1140+
];
1141+
1142+
await client._unstable_cancelScheduledDelayedEvent(delayId);
1143+
});
1144+
1145+
it("can restart scheduled delayed events (action in request path)", async () => {
1146+
const delayId = "id";
1147+
httpLookups = [
1148+
{
1149+
method: "POST",
1150+
prefix: unstableMSC4140Prefix,
1151+
path: `/delayed_events/${encodeURIComponent(delayId)}/restart`,
1152+
},
1153+
];
1154+
1155+
await client._unstable_restartScheduledDelayedEvent(delayId);
1156+
});
1157+
1158+
it("can send scheduled delayed events (action in request path)", async () => {
10811159
const delayId = "id";
1082-
const action = UpdateDelayedEventAction.Restart;
10831160
httpLookups = [
1161+
{
1162+
method: "POST",
1163+
prefix: unstableMSC4140Prefix,
1164+
path: `/delayed_events/${encodeURIComponent(delayId)}/send`,
1165+
},
1166+
];
1167+
1168+
await client._unstable_sendScheduledDelayedEvent(delayId);
1169+
});
1170+
1171+
it("can cancel scheduled delayed events (action in request path fallback when unsupported)", async () => {
1172+
const delayId = "id";
1173+
httpLookups = [
1174+
{
1175+
method: "POST",
1176+
prefix: unstableMSC4140Prefix,
1177+
path: `/delayed_events/${encodeURIComponent(delayId)}/cancel`,
1178+
error: {
1179+
httpStatus: 400,
1180+
errcode: "M_UNRECOGNIZED",
1181+
},
1182+
},
10841183
{
10851184
method: "POST",
10861185
prefix: unstableMSC4140Prefix,
10871186
path: `/delayed_events/${encodeURIComponent(delayId)}`,
10881187
data: {
1089-
action,
1188+
action: UpdateDelayedEventAction.Cancel,
10901189
},
10911190
},
10921191
];
10931192

1094-
await client._unstable_updateDelayedEvent(delayId, action);
1193+
await client._unstable_cancelScheduledDelayedEvent(delayId);
1194+
expect(httpLookups).toHaveLength(0);
1195+
});
1196+
1197+
it("can restart scheduled delayed events (action in request path fallback when unsupported)", async () => {
1198+
const delayId = "id";
1199+
httpLookups = [
1200+
{
1201+
method: "POST",
1202+
prefix: unstableMSC4140Prefix,
1203+
path: `/delayed_events/${encodeURIComponent(delayId)}/restart`,
1204+
error: {
1205+
httpStatus: 400,
1206+
errcode: "M_UNRECOGNIZED",
1207+
},
1208+
},
1209+
{
1210+
method: "POST",
1211+
prefix: unstableMSC4140Prefix,
1212+
path: `/delayed_events/${encodeURIComponent(delayId)}`,
1213+
data: {
1214+
action: UpdateDelayedEventAction.Restart,
1215+
},
1216+
},
1217+
];
1218+
1219+
await client._unstable_restartScheduledDelayedEvent(delayId);
1220+
expect(httpLookups).toHaveLength(0);
1221+
});
1222+
1223+
it("can send scheduled delayed events (action in request path fallback when unsupported)", async () => {
1224+
const delayId = "id";
1225+
httpLookups = [
1226+
{
1227+
method: "POST",
1228+
prefix: unstableMSC4140Prefix,
1229+
path: `/delayed_events/${encodeURIComponent(delayId)}/send`,
1230+
error: {
1231+
httpStatus: 400,
1232+
errcode: "M_UNRECOGNIZED",
1233+
},
1234+
},
1235+
{
1236+
method: "POST",
1237+
prefix: unstableMSC4140Prefix,
1238+
path: `/delayed_events/${encodeURIComponent(delayId)}`,
1239+
data: {
1240+
action: UpdateDelayedEventAction.Send,
1241+
},
1242+
},
1243+
];
1244+
1245+
await client._unstable_sendScheduledDelayedEvent(delayId);
1246+
expect(httpLookups).toHaveLength(0);
10951247
});
10961248
});
10971249

spec/unit/matrixrtc/MatrixRTCSession.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,9 @@ describe("MatrixRTCSession", () => {
523523
client.sendEvent = sendEventMock;
524524

525525
client._unstable_updateDelayedEvent = jest.fn();
526+
client._unstable_cancelScheduledDelayedEvent = jest.fn();
527+
client._unstable_restartScheduledDelayedEvent = jest.fn();
528+
client._unstable_sendScheduledDelayedEvent = jest.fn();
526529

527530
mockRoom = makeMockRoom([]);
528531
sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession);

0 commit comments

Comments
 (0)