You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-31 15:24:23 +03:00
MSC4133 - Extended profiles (#4391)
* Add MSC4133 functionality. * Add MSC4133 capability. * Tidy * Add tests for extended profiles. * improve docs * undefined * Add a prefix function to reduce reptitiveness * Add a docstring
This commit is contained in:
@ -1029,6 +1029,124 @@ describe("MatrixClient", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("extended profiles", () => {
|
||||
const unstableMSC4133Prefix = `${ClientPrefix.Unstable}/uk.tcpip.msc4133`;
|
||||
const userId = "@profile_user:example.org";
|
||||
|
||||
beforeEach(() => {
|
||||
unstableFeatures["uk.tcpip.msc4133"] = true;
|
||||
});
|
||||
|
||||
it("throws when unsupported by server", async () => {
|
||||
unstableFeatures["uk.tcpip.msc4133"] = false;
|
||||
const errorMessage = "Server does not support extended profiles";
|
||||
|
||||
await expect(client.doesServerSupportExtendedProfiles()).resolves.toEqual(false);
|
||||
|
||||
await expect(client.getExtendedProfile(userId)).rejects.toThrow(errorMessage);
|
||||
await expect(client.getExtendedProfileProperty(userId, "test_key")).rejects.toThrow(errorMessage);
|
||||
await expect(client.setExtendedProfileProperty("test_key", "foo")).rejects.toThrow(errorMessage);
|
||||
await expect(client.deleteExtendedProfileProperty("test_key")).rejects.toThrow(errorMessage);
|
||||
await expect(client.patchExtendedProfile({ test_key: "foo" })).rejects.toThrow(errorMessage);
|
||||
await expect(client.setExtendedProfile({ test_key: "foo" })).rejects.toThrow(errorMessage);
|
||||
});
|
||||
|
||||
it("can fetch a extended user profile", async () => {
|
||||
const testProfile = {
|
||||
test_key: "foo",
|
||||
};
|
||||
httpLookups = [
|
||||
{
|
||||
method: "GET",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(userId),
|
||||
data: testProfile,
|
||||
},
|
||||
];
|
||||
await expect(client.getExtendedProfile(userId)).resolves.toEqual(testProfile);
|
||||
expect(httpLookups).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("can fetch a property from a extended user profile", async () => {
|
||||
const testProfile = {
|
||||
test_key: "foo",
|
||||
};
|
||||
httpLookups = [
|
||||
{
|
||||
method: "GET",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(userId) + "/test_key",
|
||||
data: testProfile,
|
||||
},
|
||||
];
|
||||
await expect(client.getExtendedProfileProperty(userId, "test_key")).resolves.toEqual("foo");
|
||||
expect(httpLookups).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("can set a property in our extended profile", async () => {
|
||||
httpLookups = [
|
||||
{
|
||||
method: "PUT",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(client.credentials.userId!) + "/test_key",
|
||||
expectBody: {
|
||||
test_key: "foo",
|
||||
},
|
||||
},
|
||||
];
|
||||
await expect(client.setExtendedProfileProperty("test_key", "foo")).resolves.toEqual(undefined);
|
||||
expect(httpLookups).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("can delete a property in our extended profile", async () => {
|
||||
httpLookups = [
|
||||
{
|
||||
method: "DELETE",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(client.credentials.userId!) + "/test_key",
|
||||
},
|
||||
];
|
||||
await expect(client.deleteExtendedProfileProperty("test_key")).resolves.toEqual(undefined);
|
||||
expect(httpLookups).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("can patch our extended profile", async () => {
|
||||
const testProfile = {
|
||||
test_key: "foo",
|
||||
};
|
||||
const patchedProfile = {
|
||||
existing: "key",
|
||||
test_key: "foo",
|
||||
};
|
||||
httpLookups = [
|
||||
{
|
||||
method: "PATCH",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(client.credentials.userId!),
|
||||
data: patchedProfile,
|
||||
expectBody: testProfile,
|
||||
},
|
||||
];
|
||||
await expect(client.patchExtendedProfile(testProfile)).resolves.toEqual(patchedProfile);
|
||||
});
|
||||
|
||||
it("can replace our extended profile", async () => {
|
||||
const testProfile = {
|
||||
test_key: "foo",
|
||||
};
|
||||
httpLookups = [
|
||||
{
|
||||
method: "PUT",
|
||||
prefix: unstableMSC4133Prefix,
|
||||
path: "/profile/" + encodeURIComponent(client.credentials.userId!),
|
||||
data: testProfile,
|
||||
expectBody: testProfile,
|
||||
},
|
||||
];
|
||||
await expect(client.setExtendedProfile(testProfile)).resolves.toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it("should create (unstable) file trees", async () => {
|
||||
const userId = "@test:example.org";
|
||||
const roomId = "!room:example.org";
|
||||
|
179
src/client.ts
179
src/client.ts
@ -544,6 +544,8 @@ export const UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS = "uk.half-shot.msc2666.query_m
|
||||
|
||||
export const UNSTABLE_MSC4140_DELAYED_EVENTS = "org.matrix.msc4140";
|
||||
|
||||
export const UNSTABLE_MSC4133_EXTENDED_PROFILES = "uk.tcpip.msc4133";
|
||||
|
||||
enum CrossSigningKeyType {
|
||||
MasterKey = "master_key",
|
||||
SelfSigningKey = "self_signing_key",
|
||||
@ -8806,6 +8808,183 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
return this.http.authedRequest(Method.Get, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the server supports extended profiles, as described by MSC4133.
|
||||
*
|
||||
* @returns `true` if supported, otherwise `false`
|
||||
*/
|
||||
public async doesServerSupportExtendedProfiles(): Promise<boolean> {
|
||||
return this.doesServerSupportUnstableFeature(UNSTABLE_MSC4133_EXTENDED_PROFILES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefix used for extended profile requests.
|
||||
*
|
||||
* @returns The prefix for use with `authedRequest`
|
||||
*/
|
||||
private async getExtendedProfileRequestPrefix(): Promise<string> {
|
||||
if (await this.doesServerSupportUnstableFeature("uk.tcpip.msc4133.stable")) {
|
||||
return ClientPrefix.V3;
|
||||
}
|
||||
return "/_matrix/client/unstable/uk.tcpip.msc4133";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a user's *extended* profile, which may include additonal keys.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param userId The user ID to fetch the profile of.
|
||||
* @returns A set of keys to property values.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133.
|
||||
* @throws A M_NOT_FOUND error if the profile could not be found.
|
||||
*/
|
||||
public async getExtendedProfile(userId: string): Promise<Record<string, unknown>> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
return this.http.authedRequest(
|
||||
Method.Get,
|
||||
utils.encodeUri("/profile/$userId", { $userId: userId }),
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a specific key from the user's *extended* profile.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param userId The user ID to fetch the profile of.
|
||||
* @param key The key of the property to fetch.
|
||||
* @returns The property value.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133.
|
||||
* @throws A M_NOT_FOUND error if the key was not set OR the profile could not be found.
|
||||
*/
|
||||
public async getExtendedProfileProperty(userId: string, key: string): Promise<unknown> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
const profile = (await this.http.authedRequest(
|
||||
Method.Get,
|
||||
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
)) as Record<string, unknown>;
|
||||
return profile[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property on your *extended* profile.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param key The key of the property to set.
|
||||
* @param value The value to set on the propety.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
|
||||
*/
|
||||
public async setExtendedProfileProperty(key: string, value: unknown): Promise<void> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
const userId = this.getUserId();
|
||||
|
||||
await this.http.authedRequest(
|
||||
Method.Put,
|
||||
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
|
||||
undefined,
|
||||
{ [key]: value },
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a property on your *extended* profile.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param key The key of the property to delete.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
|
||||
*/
|
||||
public async deleteExtendedProfileProperty(key: string): Promise<void> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
const userId = this.getUserId();
|
||||
|
||||
await this.http.authedRequest(
|
||||
Method.Delete,
|
||||
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple properties on your *extended* profile. This will
|
||||
* merge with any existing keys.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param profile The profile object to merge with the existing profile.
|
||||
* @returns The newly merged profile.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
|
||||
*/
|
||||
public async patchExtendedProfile(profile: Record<string, unknown>): Promise<Record<string, unknown>> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
const userId = this.getUserId();
|
||||
|
||||
return this.http.authedRequest(
|
||||
Method.Patch,
|
||||
utils.encodeUri("/profile/$userId", { $userId: userId }),
|
||||
{},
|
||||
profile,
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple properties on your *extended* profile. This will completely
|
||||
* replace the existing profile, removing any unspecified keys.
|
||||
*
|
||||
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
|
||||
* @param profile The profile object to set.
|
||||
*
|
||||
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
|
||||
*/
|
||||
public async setExtendedProfile(profile: Record<string, unknown>): Promise<void> {
|
||||
if (!(await this.doesServerSupportExtendedProfiles())) {
|
||||
throw new Error("Server does not support extended profiles");
|
||||
}
|
||||
const userId = this.getUserId();
|
||||
|
||||
await this.http.authedRequest(
|
||||
Method.Put,
|
||||
utils.encodeUri("/profile/$userId", { $userId: userId }),
|
||||
{},
|
||||
profile,
|
||||
{
|
||||
prefix: await this.getExtendedProfileRequestPrefix(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Promise which resolves to a list of the user's threepids.
|
||||
* @returns Rejects: with an error response.
|
||||
|
7
src/models/profile-keys.ts
Normal file
7
src/models/profile-keys.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* The timezone the user is currently in. The value of this property should
|
||||
* match a timezone provided in https://www.iana.org/time-zones.
|
||||
*
|
||||
* @see https://github.com/matrix-org/matrix-spec-proposals/blob/clokep/profile-tz/proposals/4175-profile-field-time-zone.md
|
||||
*/
|
||||
export const ProfileKeyMSC4175Timezone = "us.cloke.msc4175.tz";
|
@ -38,6 +38,8 @@ export interface ISetDisplayNameCapability extends ICapability {}
|
||||
|
||||
export interface ISetAvatarUrlCapability extends ICapability {}
|
||||
|
||||
export interface IProfileFieldsCapability extends ICapability {}
|
||||
|
||||
export enum RoomVersionStability {
|
||||
Stable = "stable",
|
||||
Unstable = "unstable",
|
||||
@ -61,6 +63,7 @@ export interface Capabilities {
|
||||
"org.matrix.msc3882.get_login_token"?: IGetLoginTokenCapability;
|
||||
"m.set_displayname"?: ISetDisplayNameCapability;
|
||||
"m.set_avatar_url"?: ISetAvatarUrlCapability;
|
||||
"uk.tcpip.msc4133.profile_fields"?: IProfileFieldsCapability;
|
||||
}
|
||||
|
||||
type CapabilitiesResponse = {
|
||||
|
Reference in New Issue
Block a user