Skip to content

Commit 7555e1a

Browse files
committed
Implement MSC3912: Relation based redactions
1 parent 2dd06e3 commit 7555e1a

File tree

4 files changed

+81
-5
lines changed

4 files changed

+81
-5
lines changed

spec/unit/matrix-client.spec.ts

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Filter } from "../../src/filter";
2222
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE } from "../../src/models/MSC3089TreeSpace";
2323
import {
2424
EventType,
25+
RelationType,
2526
RoomCreateTypeField,
2627
RoomType,
2728
UNSTABLE_MSC3088_ENABLED,
@@ -120,6 +121,10 @@ describe("MatrixClient", function () {
120121
data: SYNC_DATA,
121122
};
122123

124+
const unstableFeatures: Record<string, boolean> = {
125+
"org.matrix.msc3440.stable": true,
126+
};
127+
123128
// items are popped off when processed and block if no items left.
124129
let httpLookups: HttpLookup[] = [];
125130
let acceptKeepalives: boolean;
@@ -131,9 +136,7 @@ describe("MatrixClient", function () {
131136
function httpReq(method: Method, path: string, qp?: QueryDict, data?: BodyInit, opts?: IRequestOpts) {
132137
if (path === KEEP_ALIVE_PATH && acceptKeepalives) {
133138
return Promise.resolve({
134-
unstable_features: {
135-
"org.matrix.msc3440.stable": true,
136-
},
139+
unstable_features: unstableFeatures,
137140
versions: ["r0.6.0", "r0.6.1"],
138141
});
139142
}
@@ -1067,6 +1070,59 @@ describe("MatrixClient", function () {
10671070

10681071
await client.redactEvent(roomId, eventId, txnId, { reason });
10691072
});
1073+
1074+
describe("when calling with with_relations", () => {
1075+
const eventId = "$event42:example.org";
1076+
1077+
it("should raise an error if server has no support for relation based redactions", async () => {
1078+
// load supported features
1079+
await client.getVersions();
1080+
1081+
const txnId = client.makeTxnId();
1082+
1083+
expect(() => {
1084+
client.redactEvent(roomId, eventId, txnId, {
1085+
with_relations: [RelationType.Reference],
1086+
});
1087+
}).toThrowError(
1088+
new Error(
1089+
"Server does not support relation based redactions " +
1090+
`roomId ${roomId} eventId ${eventId} txnId: ${txnId} threadId null`,
1091+
),
1092+
);
1093+
});
1094+
1095+
describe("and the server supports relation based redactions", () => {
1096+
beforeEach(async () => {
1097+
unstableFeatures["org.matrix.msc3912"] = true;
1098+
// load supported features
1099+
await client.getVersions();
1100+
});
1101+
1102+
it("should send with_relations in the request body", async () => {
1103+
const txnId = client.makeTxnId();
1104+
1105+
httpLookups = [
1106+
{
1107+
method: "PUT",
1108+
path:
1109+
`/rooms/${encodeURIComponent(roomId)}/redact/${encodeURIComponent(eventId)}` +
1110+
`/${encodeURIComponent(txnId)}`,
1111+
expectBody: {
1112+
reason: "redaction test",
1113+
with_relations: [RelationType.Reference],
1114+
},
1115+
data: { event_id: eventId },
1116+
},
1117+
];
1118+
1119+
await client.redactEvent(roomId, eventId, txnId, {
1120+
reason: "redaction test",
1121+
with_relations: [RelationType.Reference],
1122+
});
1123+
});
1124+
});
1125+
});
10701126
});
10711127

10721128
describe("cancelPendingEvent", () => {

src/@types/requests.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { IRoomEventFilter } from "../filter";
2121
import { Direction } from "../models/event-timeline";
2222
import { PushRuleAction } from "./PushRules";
2323
import { IRoomEvent } from "../sync-accumulator";
24-
import { EventType, RoomType } from "./event";
24+
import { EventType, RelationType, RoomType } from "./event";
2525

2626
// allow camelcase as these are things that go onto the wire
2727
/* eslint-disable camelcase */
@@ -47,6 +47,8 @@ export interface IJoinRoomOpts {
4747

4848
export interface IRedactOpts {
4949
reason?: string;
50+
// also delete related events, if the server supports it (MSC3912)
51+
with_relations?: Array<RelationType | string>;
5052
}
5153

5254
export interface ISendEventResponse {

src/client.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4365,12 +4365,26 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
43654365
threadId = null;
43664366
}
43674367
const reason = opts?.reason;
4368+
4369+
if (
4370+
opts?.with_relations &&
4371+
this.canSupport.get(Feature.RelationBasedRedactions) === ServerSupport.Unsupported
4372+
) {
4373+
throw new Error(
4374+
"Server does not support relation based redactions " +
4375+
`roomId ${roomId} eventId ${eventId} txnId: ${txnId} threadId ${threadId}`,
4376+
);
4377+
}
4378+
43684379
return this.sendCompleteEvent(
43694380
roomId,
43704381
threadId,
43714382
{
43724383
type: EventType.RoomRedaction,
4373-
content: { reason },
4384+
content: {
4385+
...(opts?.with_relations ? { with_relations: opts?.with_relations } : {}),
4386+
reason,
4387+
},
43744388
redacts: eventId,
43754389
},
43764390
txnId as string,

src/feature.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export enum Feature {
2626
Thread = "Thread",
2727
ThreadUnreadNotifications = "ThreadUnreadNotifications",
2828
LoginTokenRequest = "LoginTokenRequest",
29+
RelationBasedRedactions = "RelationBasedRedactions",
2930
}
3031

3132
type FeatureSupportCondition = {
@@ -45,6 +46,9 @@ const featureSupportResolver: Record<string, FeatureSupportCondition> = {
4546
[Feature.LoginTokenRequest]: {
4647
unstablePrefixes: ["org.matrix.msc3882"],
4748
},
49+
[Feature.RelationBasedRedactions]: {
50+
unstablePrefixes: ["org.matrix.msc3912"],
51+
},
4852
};
4953

5054
export async function buildFeatureSupportMap(versions: IServerVersions): Promise<Map<Feature, ServerSupport>> {

0 commit comments

Comments
 (0)