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 () => {
|
it("should create (unstable) file trees", async () => {
|
||||||
const userId = "@test:example.org";
|
const userId = "@test:example.org";
|
||||||
const roomId = "!room: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_MSC4140_DELAYED_EVENTS = "org.matrix.msc4140";
|
||||||
|
|
||||||
|
export const UNSTABLE_MSC4133_EXTENDED_PROFILES = "uk.tcpip.msc4133";
|
||||||
|
|
||||||
enum CrossSigningKeyType {
|
enum CrossSigningKeyType {
|
||||||
MasterKey = "master_key",
|
MasterKey = "master_key",
|
||||||
SelfSigningKey = "self_signing_key",
|
SelfSigningKey = "self_signing_key",
|
||||||
@ -8806,6 +8808,183 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
return this.http.authedRequest(Method.Get, path);
|
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 Promise which resolves to a list of the user's threepids.
|
||||||
* @returns Rejects: with an error response.
|
* @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 ISetAvatarUrlCapability extends ICapability {}
|
||||||
|
|
||||||
|
export interface IProfileFieldsCapability extends ICapability {}
|
||||||
|
|
||||||
export enum RoomVersionStability {
|
export enum RoomVersionStability {
|
||||||
Stable = "stable",
|
Stable = "stable",
|
||||||
Unstable = "unstable",
|
Unstable = "unstable",
|
||||||
@ -61,6 +63,7 @@ export interface Capabilities {
|
|||||||
"org.matrix.msc3882.get_login_token"?: IGetLoginTokenCapability;
|
"org.matrix.msc3882.get_login_token"?: IGetLoginTokenCapability;
|
||||||
"m.set_displayname"?: ISetDisplayNameCapability;
|
"m.set_displayname"?: ISetDisplayNameCapability;
|
||||||
"m.set_avatar_url"?: ISetAvatarUrlCapability;
|
"m.set_avatar_url"?: ISetAvatarUrlCapability;
|
||||||
|
"uk.tcpip.msc4133.profile_fields"?: IProfileFieldsCapability;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CapabilitiesResponse = {
|
type CapabilitiesResponse = {
|
||||||
|
Reference in New Issue
Block a user