1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Allow knocking rooms (#3647)

Signed-off-by: Charly Nguyen <charly.nguyen@nordeck.net>
This commit is contained in:
Charly Nguyen
2023-08-03 10:16:18 +02:00
committed by GitHub
parent 61c0a49971
commit 2ef7ae7661
3 changed files with 120 additions and 1 deletions

View File

@@ -19,7 +19,7 @@ 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, MemoryStore, Method, Room, SERVICE_TYPES } from "../../src/matrix"; import { Filter, KnockRoomOpts, MemoryStore, Method, Room, 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";
@@ -205,6 +205,84 @@ describe("MatrixClient", function () {
}); });
}); });
describe("knockRoom", function () {
const roomId = "!some-room-id:example.org";
const reason = "some reason";
const viaServers = "example.com";
type TestCase = [string, KnockRoomOpts];
const testCases: TestCase[] = [
["should knock a room", {}],
["should knock a room for a reason", { reason }],
["should knock a room via given servers", { viaServers }],
["should knock a room for a reason via given servers", { reason, viaServers }],
];
it.each(testCases)("%s", async (_, opts) => {
httpBackend
.when("POST", "/knock/" + encodeURIComponent(roomId))
.check((request) => {
expect(request.data).toEqual({ reason: opts.reason });
expect(request.queryParams).toEqual({ server_name: opts.viaServers });
})
.respond(200, { room_id: roomId });
const prom = client.knockRoom(roomId, opts);
await httpBackend.flushAllExpected();
expect((await prom).room_id).toBe(roomId);
});
it("should no-op if you've already knocked a room", function () {
const room = new Room(roomId, client, userId);
client.fetchRoomEvent = () =>
Promise.resolve({
type: "test",
content: {},
});
room.addLiveEvents([
utils.mkMembership({
user: userId,
room: roomId,
mship: "knock",
event: true,
}),
]);
httpBackend.verifyNoOutstandingRequests();
store.storeRoom(room);
client.knockRoom(roomId);
httpBackend.verifyNoOutstandingRequests();
});
describe("errors", function () {
type TestCase = [number, { errcode: string; error?: string }, string];
const testCases: TestCase[] = [
[
403,
{ errcode: "M_FORBIDDEN", error: "You don't have permission to knock" },
"[M_FORBIDDEN: MatrixError: [403] You don't have permission to knock]",
],
[
500,
{ errcode: "INTERNAL_SERVER_ERROR" },
"[INTERNAL_SERVER_ERROR: MatrixError: [500] Unknown message]",
],
];
it.each(testCases)("should handle %s error", async (code, { errcode, error }, snapshot) => {
httpBackend.when("POST", "/knock/" + encodeURIComponent(roomId)).respond(code, { errcode, error });
const prom = client.knockRoom(roomId);
await Promise.all([
httpBackend.flushAllExpected(),
expect(prom).rejects.toMatchInlineSnapshot(snapshot),
]);
});
});
});
describe("getFilter", function () { describe("getFilter", function () {
const filterId = "f1lt3r1d"; const filterId = "f1lt3r1d";

View File

@@ -45,6 +45,18 @@ export interface IJoinRoomOpts {
viaServers?: string[]; viaServers?: string[];
} }
export interface KnockRoomOpts {
/**
* The reason for the knock.
*/
reason?: string;
/**
* The server names to try and knock through in addition to those that are automatically chosen.
*/
viaServers?: string | string[];
}
export interface IRedactOpts { export interface IRedactOpts {
reason?: string; reason?: string;
/** /**

View File

@@ -134,6 +134,7 @@ import {
ITagsResponse, ITagsResponse,
IStatusResponse, IStatusResponse,
IAddThreePidBody, IAddThreePidBody,
KnockRoomOpts,
} from "./@types/requests"; } from "./@types/requests";
import { import {
EventType, EventType,
@@ -4158,6 +4159,34 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return syncRoom; return syncRoom;
} }
/**
* Knock a room. If you have already knocked the room, this will no-op.
* @param roomIdOrAlias - The room ID or room alias to knock.
* @param opts - Options when knocking the room.
* @returns Promise which resolves: `{room_id: {string}}`
* @returns Rejects: with an error response.
*/
public knockRoom(roomIdOrAlias: string, opts: KnockRoomOpts = {}): Promise<{ room_id: string }> {
const room = this.getRoom(roomIdOrAlias);
if (room?.hasMembershipState(this.credentials.userId!, "knock")) {
return Promise.resolve({ room_id: room.roomId });
}
const path = utils.encodeUri("/knock/$roomIdOrAlias", { $roomIdOrAlias: roomIdOrAlias });
const queryParams: Record<string, string | string[]> = {};
if (opts.viaServers) {
queryParams.server_name = opts.viaServers;
}
const body: Record<string, string> = {};
if (opts.reason) {
body.reason = opts.reason;
}
return this.http.authedRequest(Method.Post, path, queryParams, body);
}
/** /**
* Resend an event. Will also retry any to-device messages waiting to be sent. * Resend an event. Will also retry any to-device messages waiting to be sent.
* @param event - The event to resend. * @param event - The event to resend.