Skip to content

Commit 7d1e926

Browse files
toger5robintown
andauthored
Fixes for reading room state (#126)
* fixes for reading room state * fix tests to mock readRoomState instead of readRoomTimeline * Check for MSC2762_UPDATE_STATE version to decide if read_event (state events) reads from the state or the timeline * add warning if we reach the deprecated code path. * Review simpler return value Co-authored-by: Robin <[email protected]> * more verbal comment * add default implementation for readRoomState. * more review --------- Co-authored-by: Robin <[email protected]>
1 parent ad0cb68 commit 7d1e926

File tree

3 files changed

+95
-24
lines changed

3 files changed

+95
-24
lines changed

src/ClientWidgetApi.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ export class ClientWidgetApi extends EventEmitter {
354354
});
355355
}
356356

357+
private async supportsUpdateState(): Promise<boolean> {
358+
return (await this.getWidgetVersions()).includes(UnstableApiVersion.MSC2762_UPDATE_STATE);
359+
}
360+
357361
private handleCapabilitiesRenegotiate(request: IRenegotiateCapabilitiesActionRequest): void {
358362
// acknowledge first
359363
this.transport.reply<IWidgetApiAcknowledgeResponseData>(request, {});
@@ -527,21 +531,53 @@ export class ClientWidgetApi extends EventEmitter {
527531
}
528532
}
529533

530-
// For backwards compatibility we still call the deprecated
531-
// readRoomEvents and readStateEvents methods in case the client isn't
532-
// letting us know the currently viewed room via setViewedRoomId
533-
const events =
534-
request.data.room_ids === undefined && askRoomIds.length === 0
535-
? await (request.data.state_key === undefined
536-
? this.driver.readRoomEvents(request.data.type, msgtype, limit, null, since)
537-
: this.driver.readStateEvents(request.data.type, stateKey, limit, null))
538-
: (
539-
await Promise.all(
534+
let events: IRoomEvent[];
535+
536+
if (request.data.room_ids === undefined && askRoomIds.length === 0) {
537+
// For backwards compatibility we still call the deprecated
538+
// readRoomEvents and readStateEvents methods in case the client isn't
539+
// letting us know the currently viewed room via setViewedRoomId
540+
//
541+
// This can be considered as a deprecated implementation.
542+
// A driver should call `setViewedRoomId` on the widget messaging and implement the new readRoomState and readRoomTimeline
543+
// Methods.
544+
// This block makes sure that it is also possible to not use setViewedRoomId.
545+
// readRoomTimeline and readRoomState are required however! Otherwise widget requests that include
546+
// `room_ids` will fail.
547+
console.warn(
548+
"The widgetDriver uses deprecated behaviour:\n It does not set the viewedRoomId using `setViewedRoomId`",
549+
);
550+
events = await // This returns [] with the current driver of Element Web.
551+
// Add default implementations of the `readRoomEvents` and `readStateEvents`
552+
// methods to use `readRoomTimeline` and `readRoomState` if they are not overwritten.
553+
(request.data.state_key === undefined
554+
? this.driver.readRoomEvents(request.data.type, msgtype, limit, null, since)
555+
: this.driver.readStateEvents(request.data.type, stateKey, limit, null));
556+
} else if (await this.supportsUpdateState()) {
557+
// Calling read_events with a stateKey still reads from the rooms timeline (not the room state).
558+
events = (
559+
await Promise.all(
560+
askRoomIds.map((roomId) =>
561+
this.driver.readRoomTimeline(roomId, request.data.type, msgtype, stateKey, limit, since),
562+
),
563+
)
564+
).flat(1);
565+
} else {
566+
// TODO: remove this once `UnstableApiVersion.MSC2762_UPDATE_STATE` becomes stable.
567+
// Before version `MSC2762_UPDATE_STATE` we used readRoomState for read_events actions.
568+
events = (
569+
request.data.state_key === undefined
570+
? await Promise.all(
540571
askRoomIds.map((roomId) =>
541572
this.driver.readRoomTimeline(roomId, request.data.type, msgtype, stateKey, limit, since),
542573
),
543574
)
544-
).flat(1);
575+
: await Promise.all(
576+
askRoomIds.map((roomId) => this.driver.readRoomState(roomId, request.data.type, stateKey)),
577+
)
578+
).flat(1);
579+
}
580+
545581
this.transport.reply<IReadEventFromWidgetResponseData>(request, { events });
546582
}
547583

src/driver/WidgetDriver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export abstract class WidgetDriver {
276276
* current values of the room state entries.
277277
*/
278278
public readRoomState(roomId: string, eventType: string, stateKey: string | undefined): Promise<IRoomEvent[]> {
279-
return Promise.resolve([]);
279+
return this.readStateEvents(eventType, stateKey, Number.MAX_SAFE_INTEGER, [roomId]);
280280
}
281281

282282
/**

test/ClientWidgetApi-test.ts

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,7 @@ describe("ClientWidgetApi", () => {
14371437
describe("org.matrix.msc2876.read_events action", () => {
14381438
it("reads events from a specific room", async () => {
14391439
const roomId = "!room:example.org";
1440+
jest.spyOn(clientWidgetApi, "getWidgetVersions").mockResolvedValue([]);
14401441
const event = createRoomEvent({ room_id: roomId, type: "net.example.test", content: "test" });
14411442
driver.readRoomTimeline.mockImplementation(async (rId) => {
14421443
if (rId === roomId) return [event];
@@ -1481,6 +1482,7 @@ describe("ClientWidgetApi", () => {
14811482
it("reads events from all rooms", async () => {
14821483
const roomId = "!room:example.org";
14831484
const otherRoomId = "!other-room:example.org";
1485+
jest.spyOn(clientWidgetApi, "getWidgetVersions").mockResolvedValue([]);
14841486
const event = createRoomEvent({ room_id: roomId, type: "net.example.test", content: "test" });
14851487
const otherRoomEvent = createRoomEvent({ room_id: otherRoomId, type: "net.example.test", content: "hi" });
14861488
driver.getKnownRooms.mockReturnValue([roomId, otherRoomId]);
@@ -1534,7 +1536,8 @@ describe("ClientWidgetApi", () => {
15341536
});
15351537

15361538
it("reads state events with any state key", async () => {
1537-
driver.readRoomTimeline.mockResolvedValue([
1539+
jest.spyOn(clientWidgetApi, "getWidgetVersions").mockResolvedValue([]);
1540+
driver.readRoomState.mockResolvedValue([
15381541
createRoomEvent({ type: "net.example.test", state_key: "A" }),
15391542
createRoomEvent({ type: "net.example.test", state_key: "B" }),
15401543
]);
@@ -1556,22 +1559,15 @@ describe("ClientWidgetApi", () => {
15561559
emitEvent(new CustomEvent("", { detail: event }));
15571560

15581561
await waitFor(() => {
1559-
expect(transport.reply).toBeCalledWith(event, {
1562+
expect(transport.reply).toHaveBeenCalledWith(event, {
15601563
events: [
15611564
createRoomEvent({ type: "net.example.test", state_key: "A" }),
15621565
createRoomEvent({ type: "net.example.test", state_key: "B" }),
15631566
],
15641567
});
15651568
});
15661569

1567-
expect(driver.readRoomTimeline).toBeCalledWith(
1568-
"!room-id",
1569-
"net.example.test",
1570-
undefined,
1571-
undefined,
1572-
0,
1573-
undefined,
1574-
);
1570+
expect(driver.readRoomState).toHaveBeenLastCalledWith("!room-id", "net.example.test", undefined);
15751571
});
15761572

15771573
it("fails to read state events with any state key", async () => {
@@ -1600,6 +1596,37 @@ describe("ClientWidgetApi", () => {
16001596
});
16011597

16021598
it("reads state events with a specific state key", async () => {
1599+
jest.spyOn(clientWidgetApi, "getWidgetVersions").mockResolvedValue([]);
1600+
driver.readRoomState.mockResolvedValue([createRoomEvent({ type: "net.example.test", state_key: "B" })]);
1601+
1602+
const event: IReadEventFromWidgetActionRequest = {
1603+
api: WidgetApiDirection.FromWidget,
1604+
widgetId: "test",
1605+
requestId: "0",
1606+
action: WidgetApiFromWidgetAction.MSC2876ReadEvents,
1607+
data: {
1608+
type: "net.example.test",
1609+
state_key: "B",
1610+
},
1611+
};
1612+
1613+
await loadIframe(["org.matrix.msc2762.receive.state_event:net.example.test#B"]);
1614+
clientWidgetApi.setViewedRoomId("!room-id");
1615+
1616+
emitEvent(new CustomEvent("", { detail: event }));
1617+
1618+
await waitFor(() => {
1619+
expect(transport.reply).toHaveBeenCalledWith(event, {
1620+
events: [createRoomEvent({ type: "net.example.test", state_key: "B" })],
1621+
});
1622+
});
1623+
1624+
expect(driver.readRoomState).toHaveBeenLastCalledWith("!room-id", "net.example.test", "B");
1625+
});
1626+
1627+
it("reads state events with a specific state key from the timeline when using UnstableApiVersion.MSC2762_UPDATE_STATE", async () => {
1628+
jest.spyOn(clientWidgetApi, "getWidgetVersions").mockResolvedValue(CurrentApiVersions);
1629+
// with version MSC2762_UPDATE_STATE we wan the read Events action to read state events from the timeline.
16031630
driver.readRoomTimeline.mockResolvedValue([createRoomEvent({ type: "net.example.test", state_key: "B" })]);
16041631

16051632
const event: IReadEventFromWidgetActionRequest = {
@@ -1614,24 +1641,32 @@ describe("ClientWidgetApi", () => {
16141641
};
16151642

16161643
await loadIframe(["org.matrix.msc2762.receive.state_event:net.example.test#B"]);
1644+
16171645
clientWidgetApi.setViewedRoomId("!room-id");
16181646

1647+
// we clear the mock here because setViewedRoomId will push the room state and therefore read it
1648+
// from the driver.
1649+
driver.readRoomState.mockClear();
1650+
// clearing this as well so it gets the same treatment as readRoomState for reference
1651+
driver.readRoomTimeline.mockClear();
1652+
16191653
emitEvent(new CustomEvent("", { detail: event }));
16201654

16211655
await waitFor(() => {
1622-
expect(transport.reply).toBeCalledWith(event, {
1656+
expect(transport.reply).toHaveBeenCalledWith(event, {
16231657
events: [createRoomEvent({ type: "net.example.test", state_key: "B" })],
16241658
});
16251659
});
16261660

1627-
expect(driver.readRoomTimeline).toBeCalledWith(
1661+
expect(driver.readRoomTimeline).toHaveBeenLastCalledWith(
16281662
"!room-id",
16291663
"net.example.test",
16301664
undefined,
16311665
"B",
16321666
0,
16331667
undefined,
16341668
);
1669+
expect(driver.readRoomState).not.toHaveBeenCalled();
16351670
});
16361671

16371672
it("fails to read state events with a specific state key", async () => {

0 commit comments

Comments
 (0)