1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Improve compliance with MSC3266 (#4155)

* Fix fields of MSC 3266 summary object

Also remove redundant room_type field which is inherited from elsewhere

* Export the MSC 3266 summary type

* Use proper endpoint for MSC 3266 summary lookup

Use the endpoint recommended by the MSC

* Rename newly-exported symbol to not start with I

* Use "export type"

* Lint

* Fix type of "encryption" field

* Add TSDoc documentation

* Add basic integration test for getRoomSummary

* Lint

* Use fallback endpoint for MSC3266

* Improve test coverage

* Lint

* Refactor async catch to satisfy linter

* Increase test coverage
This commit is contained in:
Andrew Ferrazzutti
2024-04-23 02:39:10 +09:00
committed by GitHub
parent 6fedda91f9
commit e874468ba3
3 changed files with 139 additions and 9 deletions

View File

@ -19,7 +19,16 @@ import { Mocked } from "jest-mock";
import * as utils from "../test-utils/test-utils"; import * as utils from "../test-utils/test-utils";
import { CRYPTO_ENABLED, IStoredClientOpts, MatrixClient } from "../../src/client"; import { CRYPTO_ENABLED, IStoredClientOpts, MatrixClient } from "../../src/client";
import { MatrixEvent } from "../../src/models/event"; import { MatrixEvent } from "../../src/models/event";
import { Filter, KnockRoomOpts, MemoryStore, Method, Room, SERVICE_TYPES } from "../../src/matrix"; import {
Filter,
JoinRule,
KnockRoomOpts,
MemoryStore,
Method,
Room,
RoomSummary,
SERVICE_TYPES,
} from "../../src/matrix";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
import { THREAD_RELATION_TYPE } from "../../src/models/thread"; import { THREAD_RELATION_TYPE } from "../../src/models/thread";
import { IFilterDefinition } from "../../src/filter"; import { IFilterDefinition } from "../../src/filter";
@ -1710,6 +1719,102 @@ describe("MatrixClient", function () {
await Promise.all([client.unbindThreePid("email", "alice@server.com"), httpBackend.flushAllExpected()]); await Promise.all([client.unbindThreePid("email", "alice@server.com"), httpBackend.flushAllExpected()]);
}); });
}); });
describe("getRoomSummary", () => {
const roomId = "!foo:bar";
const encodedRoomId = encodeURIComponent(roomId);
const roomSummary: RoomSummary = {
"room_id": roomId,
"name": "My Room",
"avatar_url": "",
"topic": "My room topic",
"world_readable": false,
"guest_can_join": false,
"num_joined_members": 1,
"room_type": "",
"join_rule": JoinRule.Public,
"membership": "leave",
"im.nheko.summary.room_version": "6",
"im.nheko.summary.encryption": "algo",
};
const prefix = "/_matrix/client/unstable/im.nheko.summary/";
const suffix = `summary/${encodedRoomId}`;
const deprecatedSuffix = `rooms/${encodedRoomId}/summary`;
const errorUnrecogStatus = 404;
const errorUnrecogBody = {
errcode: "M_UNRECOGNIZED",
error: "Unsupported endpoint",
};
const errorBadreqStatus = 400;
const errorBadreqBody = {
errcode: "M_UNKNOWN",
error: "Invalid request",
};
it("should respond with a valid room summary object", () => {
httpBackend.when("GET", prefix + suffix).respond(200, roomSummary);
const prom = client.getRoomSummary(roomId).then((response) => {
expect(response).toEqual(roomSummary);
});
httpBackend.flush("");
return prom;
});
it("should allow fallback to the deprecated endpoint", () => {
httpBackend.when("GET", prefix + suffix).respond(errorUnrecogStatus, errorUnrecogBody);
httpBackend.when("GET", prefix + deprecatedSuffix).respond(200, roomSummary);
const prom = client.getRoomSummary(roomId).then((response) => {
expect(response).toEqual(roomSummary);
});
httpBackend.flush("");
return prom;
});
it("should respond to unsupported path with error", () => {
httpBackend.when("GET", prefix + suffix).respond(errorUnrecogStatus, errorUnrecogBody);
httpBackend.when("GET", prefix + deprecatedSuffix).respond(errorUnrecogStatus, errorUnrecogBody);
const prom = client.getRoomSummary(roomId).then(
function (response) {
throw Error("request not failed");
},
function (error) {
expect(error.httpStatus).toEqual(errorUnrecogStatus);
expect(error.errcode).toEqual(errorUnrecogBody.errcode);
expect(error.message).toEqual(`MatrixError: [${errorUnrecogStatus}] ${errorUnrecogBody.error}`);
},
);
httpBackend.flush("");
return prom;
});
it("should respond to invalid path arguments with error", () => {
httpBackend.when("GET", prefix).respond(errorBadreqStatus, errorBadreqBody);
const prom = client.getRoomSummary("notAroom").then(
function (response) {
throw Error("request not failed");
},
function (error) {
expect(error.httpStatus).toEqual(errorBadreqStatus);
expect(error.errcode).toEqual(errorBadreqBody.errcode);
expect(error.message).toEqual(`MatrixError: [${errorBadreqStatus}] ${errorBadreqBody.error}`);
},
);
httpBackend.flush("");
return prom;
});
});
}); });
function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent { function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent {

View File

@ -865,10 +865,24 @@ interface IThirdPartyUser {
fields: object; fields: object;
} }
interface IRoomSummary extends Omit<IPublicRoomsChunkRoom, "canonical_alias" | "aliases"> { /**
room_type?: RoomType; * The summary of a room as defined by an initial version of MSC3266 and implemented in Synapse
membership?: Membership; * Proposed at https://github.com/matrix-org/matrix-doc/pull/3266
is_encrypted: boolean; */
export interface RoomSummary extends Omit<IPublicRoomsChunkRoom, "canonical_alias" | "aliases"> {
/**
* The current membership of this user in the room.
* Usually "leave" if the room is fetched over federation.
*/
"membership"?: Membership;
/**
* Version of the room.
*/
"im.nheko.summary.room_version"?: string;
/**
* The encryption algorithm used for this room, if the room is encrypted.
*/
"im.nheko.summary.encryption"?: string;
} }
interface IRoomKeysResponse { interface IRoomKeysResponse {
@ -9786,11 +9800,21 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param roomIdOrAlias - The ID or alias of the room to get the summary of. * @param roomIdOrAlias - The ID or alias of the room to get the summary of.
* @param via - The list of servers which know about the room if only an ID was provided. * @param via - The list of servers which know about the room if only an ID was provided.
*/ */
public async getRoomSummary(roomIdOrAlias: string, via?: string[]): Promise<IRoomSummary> { public async getRoomSummary(roomIdOrAlias: string, via?: string[]): Promise<RoomSummary> {
const path = utils.encodeUri("/rooms/$roomid/summary", { $roomid: roomIdOrAlias }); const paramOpts = {
return this.http.authedRequest(Method.Get, path, { via }, undefined, {
prefix: "/_matrix/client/unstable/im.nheko.summary", prefix: "/_matrix/client/unstable/im.nheko.summary",
}); };
try {
const path = utils.encodeUri("/summary/$roomid", { $roomid: roomIdOrAlias });
return await this.http.authedRequest(Method.Get, path, { via }, undefined, paramOpts);
} catch (e) {
if (e instanceof MatrixError && e.errcode === "M_UNRECOGNIZED") {
const path = utils.encodeUri("/rooms/$roomid/summary", { $roomid: roomIdOrAlias });
return await this.http.authedRequest(Method.Get, path, { via }, undefined, paramOpts);
} else {
throw e;
}
}
} }
/** /**

View File

@ -77,6 +77,7 @@ export * from "./@types/extensible_events";
export * from "./@types/IIdentityServerProvider"; export * from "./@types/IIdentityServerProvider";
export * from "./models/room-summary"; export * from "./models/room-summary";
export * from "./models/event-status"; export * from "./models/event-status";
export type { RoomSummary } from "./client";
export * as ContentHelpers from "./content-helpers"; export * as ContentHelpers from "./content-helpers";
export * as SecretStorage from "./secret-storage"; export * as SecretStorage from "./secret-storage";
export type { ICryptoCallbacks } from "./crypto"; // used to be located here export type { ICryptoCallbacks } from "./crypto"; // used to be located here