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

Update Mutual Rooms (MSC2666) support (#3381)

* update mutual rooms support

* clarify docs and switch eslint comment with todo

* please the holy linter

* change query variable names around

* add mock tests and fix issue

* ye holy linter
This commit is contained in:
Jonathan de Jong
2023-06-05 10:23:44 +02:00
committed by GitHub
parent 258f157ebc
commit 1a5af9d8e3
2 changed files with 197 additions and 15 deletions

View File

@ -14,9 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import fetchMock from "fetch-mock-jest";
import * as utils from "../test-utils/test-utils";
import { RoomMember, RoomMemberEvent } from "../../src/models/room-member";
import { EventType, RoomState } from "../../src";
import {
createClient,
EventType,
MatrixClient,
RoomState,
UNSTABLE_MSC2666_MUTUAL_ROOMS,
UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS,
UNSTABLE_MSC2666_SHARED_ROOMS,
} from "../../src";
describe("RoomMember", function () {
const roomId = "!foo:bar";
@ -481,3 +491,125 @@ describe("RoomMember", function () {
});
});
});
describe("MutualRooms", () => {
let client: MatrixClient;
const HS_URL = "https://example.com";
const TEST_USER_ID = "@alice:localhost";
const TEST_DEVICE_ID = "xzcvb";
const QUERIED_USER = "@user:example.com";
beforeEach(async () => {
// anything that we don't have a specific matcher for silently returns a 404
fetchMock.catch(404);
fetchMock.config.warnOnFallback = true;
client = createClient({
baseUrl: HS_URL,
userId: TEST_USER_ID,
accessToken: "akjgkrgjs",
deviceId: TEST_DEVICE_ID,
});
});
afterEach(async () => {
await client.stopClient();
fetchMock.mockReset();
});
function enableFeature(feature: string) {
const mapping: Record<string, boolean> = {};
mapping[feature] = true;
fetchMock.get(`${HS_URL}/_matrix/client/versions`, {
unstable_features: mapping,
versions: ["v1.1"],
});
}
it("supports the initial MSC version (shared rooms)", async () => {
enableFeature(UNSTABLE_MSC2666_SHARED_ROOMS);
fetchMock.get("express:/_matrix/client/unstable/uk.half-shot.msc2666/user/shared_rooms/:user_id", (rawUrl) => {
const segments = rawUrl.split("/");
const lastSegment = decodeURIComponent(segments[segments.length - 1]);
expect(lastSegment).toEqual(QUERIED_USER);
return {
joined: ["!test:example.com"],
};
});
const rooms = await client._unstable_getSharedRooms(QUERIED_USER);
expect(rooms).toEqual(["!test:example.com"]);
});
it("supports the renaming MSC version (mutual rooms)", async () => {
enableFeature(UNSTABLE_MSC2666_MUTUAL_ROOMS);
fetchMock.get("express:/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms/:user_id", (rawUrl) => {
const segments = rawUrl.split("/");
const lastSegment = decodeURIComponent(segments[segments.length - 1]);
expect(lastSegment).toEqual(QUERIED_USER);
return {
joined: ["!test2:example.com"],
};
});
const rooms = await client._unstable_getSharedRooms(QUERIED_USER);
expect(rooms).toEqual(["!test2:example.com"]);
});
describe("can work the latest MSC version (query mutual rooms)", () => {
beforeEach(() => {
enableFeature(UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS);
});
it("works with a simple response", async () => {
fetchMock.get("express:/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms", (rawUrl) => {
const url = new URL(rawUrl);
expect(url.searchParams.get("user_id")).toEqual(QUERIED_USER);
return {
joined: ["!test3:example.com"],
};
});
const rooms = await client._unstable_getSharedRooms(QUERIED_USER);
expect(rooms).toEqual(["!test3:example.com"]);
});
it("works with a paginated response", async () => {
fetchMock.get("express:/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms", (rawUrl) => {
const url = new URL(rawUrl);
expect(url.searchParams.get("user_id")).toEqual(QUERIED_USER);
const token = url.searchParams.get("batch_token");
if (token == "yahaha") {
return {
joined: ["!korok:example.com"],
};
} else {
return {
joined: ["!rock:example.com"],
next_batch_token: "yahaha",
};
}
});
const rooms = await client._unstable_getSharedRooms(QUERIED_USER);
expect(rooms).toEqual(["!rock:example.com", "!korok:example.com"]);
});
});
});

View File

@ -500,6 +500,10 @@ export interface IMSC3882GetLoginTokenCapability extends ICapability {}
export const UNSTABLE_MSC3882_CAPABILITY = new UnstableValue("m.get_login_token", "org.matrix.msc3882.get_login_token");
export const UNSTABLE_MSC2666_SHARED_ROOMS = "uk.half-shot.msc2666";
export const UNSTABLE_MSC2666_MUTUAL_ROOMS = "uk.half-shot.msc2666.mutual_rooms";
export const UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS = "uk.half-shot.msc2666.query_mutual_rooms";
/**
* A representation of the capabilities advertised by a homeserver as defined by
* [Capabilities negotiation](https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv3capabilities).
@ -7148,29 +7152,75 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
/**
* Gets a set of room IDs in common with another user
* Gets a set of room IDs in common with another user.
*
* Note: This endpoint is unstable, and can throw an `Error`.
* Check progress on [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666) for more details.
*
* @param userId - The userId to check.
* @returns Promise which resolves to a set of rooms
* @returns Promise which resolves to an array of rooms
* @returns Rejects: with an error response.
*/
// TODO: on spec release, rename this to getMutualRooms
// eslint-disable-next-line
public async _unstable_getSharedRooms(userId: string): Promise<string[]> {
const sharedRoomsSupport = await this.doesServerSupportUnstableFeature("uk.half-shot.msc2666");
const mutualRoomsSupport = await this.doesServerSupportUnstableFeature("uk.half-shot.msc2666.mutual_rooms");
// Initial variant of the MSC
const sharedRoomsSupport = await this.doesServerSupportUnstableFeature(UNSTABLE_MSC2666_SHARED_ROOMS);
if (!sharedRoomsSupport && !mutualRoomsSupport) {
throw Error("Server does not support mutual_rooms API");
// Newer variant that renamed shared rooms to mutual rooms
const mutualRoomsSupport = await this.doesServerSupportUnstableFeature(UNSTABLE_MSC2666_MUTUAL_ROOMS);
// Latest variant that changed from path elements to query elements
const queryMutualRoomsSupport = await this.doesServerSupportUnstableFeature(
UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS,
);
if (!sharedRoomsSupport && !mutualRoomsSupport && !queryMutualRoomsSupport) {
throw Error("Server does not support the Mutual Rooms API");
}
const path = utils.encodeUri(
let path;
let query;
// Cascading unstable support switching.
if (queryMutualRoomsSupport) {
path = "/uk.half-shot.msc2666/user/mutual_rooms";
query = { user_id: userId };
} else {
path = utils.encodeUri(
`/uk.half-shot.msc2666/user/${mutualRoomsSupport ? "mutual_rooms" : "shared_rooms"}/$userId`,
{ $userId: userId },
);
query = {};
}
const res = await this.http.authedRequest<{ joined: string[] }>(Method.Get, path, undefined, undefined, {
// Accumulated rooms
const rooms: string[] = [];
let token = null;
do {
const tokenQuery: Record<string, string> = {};
if (token != null && queryMutualRoomsSupport) {
tokenQuery["batch_token"] = token;
}
const res = await this.http.authedRequest<{
joined: string[];
next_batch_token?: string;
}>(Method.Get, path, { ...query, ...tokenQuery }, undefined, {
prefix: ClientPrefix.Unstable,
});
return res.joined;
rooms.push(...res.joined);
if (res.next_batch_token !== undefined) {
token = res.next_batch_token;
} else {
token = null;
}
} while (token != null);
return rooms;
}
/**