Skip to content

Commit b83c372

Browse files
authored
Implement MSC3912: Relation-based redactions (#2954)
1 parent 6d58a54 commit b83c372

File tree

5 files changed

+113
-6
lines changed

5 files changed

+113
-6
lines changed

spec/unit/matrix-client.spec.ts

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ 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,
2829
UNSTABLE_MSC3088_PURPOSE,
2930
UNSTABLE_MSC3089_TREE_SUBTYPE,
31+
MSC3912_RELATION_BASED_REDACTIONS_PROP,
3032
} from "../../src/@types/event";
3133
import { MEGOLM_ALGORITHM } from "../../src/crypto/olmlib";
3234
import { Crypto } from "../../src/crypto";
@@ -121,6 +123,10 @@ describe("MatrixClient", function () {
121123
data: SYNC_DATA,
122124
};
123125

126+
const unstableFeatures: Record<string, boolean> = {
127+
"org.matrix.msc3440.stable": true,
128+
};
129+
124130
// items are popped off when processed and block if no items left.
125131
let httpLookups: HttpLookup[] = [];
126132
let acceptKeepalives: boolean;
@@ -132,9 +138,7 @@ describe("MatrixClient", function () {
132138
function httpReq(method: Method, path: string, qp?: QueryDict, data?: BodyInit, opts?: IRequestOpts) {
133139
if (path === KEEP_ALIVE_PATH && acceptKeepalives) {
134140
return Promise.resolve({
135-
unstable_features: {
136-
"org.matrix.msc3440.stable": true,
137-
},
141+
unstable_features: unstableFeatures,
138142
versions: ["r0.6.0", "r0.6.1"],
139143
});
140144
}
@@ -1085,6 +1089,59 @@ describe("MatrixClient", function () {
10851089

10861090
await client.redactEvent(roomId, eventId, txnId, { reason });
10871091
});
1092+
1093+
describe("when calling with with_relations", () => {
1094+
const eventId = "$event42:example.org";
1095+
1096+
it("should raise an error if server has no support for relation based redactions", async () => {
1097+
// load supported features
1098+
await client.getVersions();
1099+
1100+
const txnId = client.makeTxnId();
1101+
1102+
expect(() => {
1103+
client.redactEvent(roomId, eventId, txnId, {
1104+
with_relations: [RelationType.Reference],
1105+
});
1106+
}).toThrowError(
1107+
new Error(
1108+
"Server does not support relation based redactions " +
1109+
`roomId ${roomId} eventId ${eventId} txnId: ${txnId} threadId null`,
1110+
),
1111+
);
1112+
});
1113+
1114+
describe("and the server supports relation based redactions (unstable)", () => {
1115+
beforeEach(async () => {
1116+
unstableFeatures["org.matrix.msc3912"] = true;
1117+
// load supported features
1118+
await client.getVersions();
1119+
});
1120+
1121+
it("should send with_relations in the request body", async () => {
1122+
const txnId = client.makeTxnId();
1123+
1124+
httpLookups = [
1125+
{
1126+
method: "PUT",
1127+
path:
1128+
`/rooms/${encodeURIComponent(roomId)}/redact/${encodeURIComponent(eventId)}` +
1129+
`/${encodeURIComponent(txnId)}`,
1130+
expectBody: {
1131+
reason: "redaction test",
1132+
[MSC3912_RELATION_BASED_REDACTIONS_PROP.unstable!]: [RelationType.Reference],
1133+
},
1134+
data: { event_id: eventId },
1135+
},
1136+
];
1137+
1138+
await client.redactEvent(roomId, eventId, txnId, {
1139+
reason: "redaction test",
1140+
with_relations: [RelationType.Reference],
1141+
});
1142+
});
1143+
});
1144+
});
10881145
});
10891146

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

src/@types/event.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ export const UNSTABLE_MSC3089_BRANCH = new UnstableValue("m.branch", "org.matrix
165165
*/
166166
export const UNSTABLE_MSC2716_MARKER = new UnstableValue("m.room.marker", "org.matrix.msc2716.marker");
167167

168+
/**
169+
* Name of the "with_relations" request property for relation based redactions.
170+
* {@link https://github.com/matrix-org/matrix-spec-proposals/pull/3912}
171+
*/
172+
export const MSC3912_RELATION_BASED_REDACTIONS_PROP = new UnstableValue(
173+
"with_relations",
174+
"org.matrix.msc3912.with_relations",
175+
);
176+
168177
/**
169178
* Functional members type for declaring a purpose of room members (e.g. helpful bots).
170179
* Note that this reference is UNSTABLE and subject to breaking changes, including its

src/@types/requests.ts

Lines changed: 13 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,18 @@ export interface IJoinRoomOpts {
4747

4848
export interface IRedactOpts {
4949
reason?: string;
50+
/**
51+
* Whether events related to the redacted event should be redacted.
52+
*
53+
* If specified, then any events which relate to the event being redacted with
54+
* any of the relationship types listed will also be redacted.
55+
*
56+
* <b>Raises an Error if the server does not support it.</b>
57+
* Check for server-side support before using this param with
58+
* <code>client.canSupport.get(Feature.RelationBasedRedactions)</code>.
59+
* {@link https://github.com/matrix-org/matrix-spec-proposals/pull/3912}
60+
*/
61+
with_relations?: Array<RelationType | string>;
5062
}
5163

5264
export interface ISendEventResponse {

src/client.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ import {
154154
UNSTABLE_MSC3088_ENABLED,
155155
UNSTABLE_MSC3088_PURPOSE,
156156
UNSTABLE_MSC3089_TREE_SUBTYPE,
157+
MSC3912_RELATION_BASED_REDACTIONS_PROP,
157158
} from "./@types/event";
158159
import { IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials";
159160
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper";
@@ -4444,9 +4445,11 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
44444445

44454446
/**
44464447
* @param txnId - transaction id. One will be made up if not supplied.
4447-
* @param opts - Options to pass on, may contain `reason`.
4448+
* @param opts - Options to pass on, may contain `reason` and `with_relations` (MSC3912)
44484449
* @returns Promise which resolves: TODO
44494450
* @returns Rejects: with an error response.
4451+
* @throws Error if called with `with_relations` (MSC3912) but the server does not support it.
4452+
* Callers should check whether the server supports MSC3912 via `MatrixClient.canSupport`.
44504453
*/
44514454
public redactEvent(
44524455
roomId: string,
@@ -4475,12 +4478,34 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
44754478
threadId = null;
44764479
}
44774480
const reason = opts?.reason;
4481+
4482+
if (
4483+
opts?.with_relations &&
4484+
this.canSupport.get(Feature.RelationBasedRedactions) === ServerSupport.Unsupported
4485+
) {
4486+
throw new Error(
4487+
"Server does not support relation based redactions " +
4488+
`roomId ${roomId} eventId ${eventId} txnId: ${txnId} threadId ${threadId}`,
4489+
);
4490+
}
4491+
4492+
const withRelations = opts?.with_relations
4493+
? {
4494+
[this.canSupport.get(Feature.RelationBasedRedactions) === ServerSupport.Stable
4495+
? MSC3912_RELATION_BASED_REDACTIONS_PROP.stable!
4496+
: MSC3912_RELATION_BASED_REDACTIONS_PROP.unstable!]: opts?.with_relations,
4497+
}
4498+
: {};
4499+
44784500
return this.sendCompleteEvent(
44794501
roomId,
44804502
threadId,
44814503
{
44824504
type: EventType.RoomRedaction,
4483-
content: { reason },
4505+
content: {
4506+
...withRelations,
4507+
reason,
4508+
},
44844509
redacts: eventId,
44854510
},
44864511
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
AccountDataDeletion = "AccountDataDeletion",
3031
}
3132

@@ -46,6 +47,9 @@ const featureSupportResolver: Record<string, FeatureSupportCondition> = {
4647
[Feature.LoginTokenRequest]: {
4748
unstablePrefixes: ["org.matrix.msc3882"],
4849
},
50+
[Feature.RelationBasedRedactions]: {
51+
unstablePrefixes: ["org.matrix.msc3912"],
52+
},
4953
[Feature.AccountDataDeletion]: {
5054
unstablePrefixes: ["org.matrix.msc3391"],
5155
},

0 commit comments

Comments
 (0)