Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 05e0a40

Browse files
authored
Stop applying edits to event contents (MSC3925). (#15193)
Enables MSC3925 support by default, which: * Includes the full edit event in the bundled aggregations of an edited event. * Stops modifying the original event's content to return the new content from the edit event. This is a backwards-incompatible change that is considered to be "correct" by the spec.
1 parent fd9cadc commit 05e0a40

File tree

6 files changed

+15
-109
lines changed

6 files changed

+15
-109
lines changed

changelog.d/15193.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stop applying edits when bundling aggregations, per [MSC3925](https://github.com/matrix-org/matrix-spec-proposals/pull/3925).

synapse/config/experimental.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,6 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
166166
# MSC3391: Removing account data.
167167
self.msc3391_enabled = experimental.get("msc3391_enabled", False)
168168

169-
# MSC3925: do not replace events with their edits
170-
self.msc3925_inhibit_edit = experimental.get("msc3925_inhibit_edit", False)
171-
172169
# MSC3873: Disambiguate event_match keys.
173170
self.msc3873_escape_event_match_key = experimental.get(
174171
"msc3873_escape_event_match_key", False

synapse/events/utils.py

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from synapse.api.errors import Codes, SynapseError
4040
from synapse.api.room_versions import RoomVersion
4141
from synapse.types import JsonDict
42-
from synapse.util.frozenutils import unfreeze
4342

4443
from . import EventBase
4544

@@ -403,22 +402,13 @@ class EventClientSerializer:
403402
clients.
404403
"""
405404

406-
def __init__(self, inhibit_replacement_via_edits: bool = False):
407-
"""
408-
Args:
409-
inhibit_replacement_via_edits: If this is set to True, then events are
410-
never replaced by their edits.
411-
"""
412-
self._inhibit_replacement_via_edits = inhibit_replacement_via_edits
413-
414405
def serialize_event(
415406
self,
416407
event: Union[JsonDict, EventBase],
417408
time_now: int,
418409
*,
419410
config: SerializeEventConfig = _DEFAULT_SERIALIZE_EVENT_CONFIG,
420411
bundle_aggregations: Optional[Dict[str, "BundledAggregations"]] = None,
421-
apply_edits: bool = True,
422412
) -> JsonDict:
423413
"""Serializes a single event.
424414
@@ -428,10 +418,7 @@ def serialize_event(
428418
config: Event serialization config
429419
bundle_aggregations: A map from event_id to the aggregations to be bundled
430420
into the event.
431-
apply_edits: Whether the content of the event should be modified to reflect
432-
any replacement in `bundle_aggregations[<event_id>].replace`.
433-
See also the `inhibit_replacement_via_edits` constructor arg: if that is
434-
set to True, then this argument is ignored.
421+
435422
Returns:
436423
The serialized event
437424
"""
@@ -450,46 +437,17 @@ def serialize_event(
450437
config,
451438
bundle_aggregations,
452439
serialized_event,
453-
apply_edits=apply_edits,
454440
)
455441

456442
return serialized_event
457443

458-
def _apply_edit(
459-
self, orig_event: EventBase, serialized_event: JsonDict, edit: EventBase
460-
) -> None:
461-
"""Replace the content, preserving existing relations of the serialized event.
462-
463-
Args:
464-
orig_event: The original event.
465-
serialized_event: The original event, serialized. This is modified.
466-
edit: The event which edits the above.
467-
"""
468-
469-
# Ensure we take copies of the edit content, otherwise we risk modifying
470-
# the original event.
471-
edit_content = edit.content.copy()
472-
473-
# Unfreeze the event content if necessary, so that we may modify it below
474-
edit_content = unfreeze(edit_content)
475-
serialized_event["content"] = edit_content.get("m.new_content", {})
476-
477-
# Check for existing relations
478-
relates_to = orig_event.content.get("m.relates_to")
479-
if relates_to:
480-
# Keep the relations, ensuring we use a dict copy of the original
481-
serialized_event["content"]["m.relates_to"] = relates_to.copy()
482-
else:
483-
serialized_event["content"].pop("m.relates_to", None)
484-
485444
def _inject_bundled_aggregations(
486445
self,
487446
event: EventBase,
488447
time_now: int,
489448
config: SerializeEventConfig,
490449
bundled_aggregations: Dict[str, "BundledAggregations"],
491450
serialized_event: JsonDict,
492-
apply_edits: bool,
493451
) -> None:
494452
"""Potentially injects bundled aggregations into the unsigned portion of the serialized event.
495453
@@ -504,9 +462,6 @@ def _inject_bundled_aggregations(
504462
While serializing the bundled aggregations this map may be searched
505463
again for additional events in a recursive manner.
506464
serialized_event: The serialized event which may be modified.
507-
apply_edits: Whether the content of the event should be modified to reflect
508-
any replacement in `aggregations.replace` (subject to the
509-
`inhibit_replacement_via_edits` constructor arg).
510465
"""
511466

512467
# We have already checked that aggregations exist for this event.
@@ -522,22 +477,14 @@ def _inject_bundled_aggregations(
522477
] = event_aggregations.references
523478

524479
if event_aggregations.replace:
525-
# If there is an edit, optionally apply it to the event.
526-
edit = event_aggregations.replace
527-
if apply_edits and not self._inhibit_replacement_via_edits:
528-
self._apply_edit(event, serialized_event, edit)
529-
530480
# Include information about it in the relations dict.
531481
#
532482
# Matrix spec v1.5 (https://spec.matrix.org/v1.5/client-server-api/#server-side-aggregation-of-mreplace-relationships)
533483
# said that we should only include the `event_id`, `origin_server_ts` and
534484
# `sender` of the edit; however MSC3925 proposes extending it to the whole
535485
# of the edit, which is what we do here.
536486
serialized_aggregations[RelationTypes.REPLACE] = self.serialize_event(
537-
edit,
538-
time_now,
539-
config=config,
540-
apply_edits=False,
487+
event_aggregations.replace, time_now, config=config
541488
)
542489

543490
# Include any threaded replies to this event.

synapse/rest/client/room.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ async def on_GET(
818818
# per MSC2676, /rooms/{roomId}/event/{eventId}, should return the
819819
# *original* event, rather than the edited version
820820
event_dict = self._event_serializer.serialize_event(
821-
event, time_now, bundle_aggregations=aggregations, apply_edits=False
821+
event, time_now, bundle_aggregations=aggregations
822822
)
823823
return 200, event_dict
824824

synapse/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ def get_oidc_handler(self) -> "OidcHandler":
743743

744744
@cache_in_self
745745
def get_event_client_serializer(self) -> EventClientSerializer:
746-
return EventClientSerializer(self.config.experimental.msc3925_inhibit_edit)
746+
return EventClientSerializer()
747747

748748
@cache_in_self
749749
def get_password_policy_handler(self) -> PasswordPolicyHandler:

tests/rest/client/test_relations.py

Lines changed: 10 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from tests.server import FakeChannel
3131
from tests.test_utils import make_awaitable
3232
from tests.test_utils.event_injection import inject_event
33-
from tests.unittest import override_config
3433

3534

3635
class BaseRelationsTestCase(unittest.HomeserverTestCase):
@@ -403,7 +402,7 @@ def _assert_edit_bundle(
403402

404403
def test_edit(self) -> None:
405404
"""Test that a simple edit works."""
406-
405+
orig_body = {"body": "Hi!", "msgtype": "m.text"}
407406
new_body = {"msgtype": "m.text", "body": "I've been edited!"}
408407
edit_event_content = {
409408
"msgtype": "m.text",
@@ -424,9 +423,7 @@ def test_edit(self) -> None:
424423
access_token=self.user_token,
425424
)
426425
self.assertEqual(200, channel.code, channel.json_body)
427-
self.assertEqual(
428-
channel.json_body["content"], {"body": "Hi!", "msgtype": "m.text"}
429-
)
426+
self.assertEqual(channel.json_body["content"], orig_body)
430427
self._assert_edit_bundle(channel.json_body, edit_event_id, edit_event_content)
431428

432429
# Request the room messages.
@@ -443,7 +440,7 @@ def test_edit(self) -> None:
443440
)
444441

445442
# Request the room context.
446-
# /context should return the edited event.
443+
# /context should return the event.
447444
channel = self.make_request(
448445
"GET",
449446
f"/rooms/{self.room}/context/{self.parent_id}",
@@ -453,7 +450,7 @@ def test_edit(self) -> None:
453450
self._assert_edit_bundle(
454451
channel.json_body["event"], edit_event_id, edit_event_content
455452
)
456-
self.assertEqual(channel.json_body["event"]["content"], new_body)
453+
self.assertEqual(channel.json_body["event"]["content"], orig_body)
457454

458455
# Request sync, but limit the timeline so it becomes limited (and includes
459456
# bundled aggregations).
@@ -491,45 +488,11 @@ def test_edit(self) -> None:
491488
edit_event_content,
492489
)
493490

494-
@override_config({"experimental_features": {"msc3925_inhibit_edit": True}})
495-
def test_edit_inhibit_replace(self) -> None:
496-
"""
497-
If msc3925_inhibit_edit is enabled, then the original event should not be
498-
replaced.
499-
"""
500-
501-
new_body = {"msgtype": "m.text", "body": "I've been edited!"}
502-
edit_event_content = {
503-
"msgtype": "m.text",
504-
"body": "foo",
505-
"m.new_content": new_body,
506-
}
507-
channel = self._send_relation(
508-
RelationTypes.REPLACE,
509-
"m.room.message",
510-
content=edit_event_content,
511-
)
512-
edit_event_id = channel.json_body["event_id"]
513-
514-
# /context should return the *original* event.
515-
channel = self.make_request(
516-
"GET",
517-
f"/rooms/{self.room}/context/{self.parent_id}",
518-
access_token=self.user_token,
519-
)
520-
self.assertEqual(200, channel.code, channel.json_body)
521-
self.assertEqual(
522-
channel.json_body["event"]["content"], {"body": "Hi!", "msgtype": "m.text"}
523-
)
524-
self._assert_edit_bundle(
525-
channel.json_body["event"], edit_event_id, edit_event_content
526-
)
527-
528491
def test_multi_edit(self) -> None:
529492
"""Test that multiple edits, including attempts by people who
530493
shouldn't be allowed, are correctly handled.
531494
"""
532-
495+
orig_body = orig_body = {"body": "Hi!", "msgtype": "m.text"}
533496
self._send_relation(
534497
RelationTypes.REPLACE,
535498
"m.room.message",
@@ -570,7 +533,7 @@ def test_multi_edit(self) -> None:
570533
)
571534
self.assertEqual(200, channel.code, channel.json_body)
572535

573-
self.assertEqual(channel.json_body["event"]["content"], new_body)
536+
self.assertEqual(channel.json_body["event"]["content"], orig_body)
574537
self._assert_edit_bundle(
575538
channel.json_body["event"], edit_event_id, edit_event_content
576539
)
@@ -642,6 +605,7 @@ def test_edit_reply(self) -> None:
642605

643606
def test_edit_edit(self) -> None:
644607
"""Test that an edit cannot be edited."""
608+
orig_body = {"body": "Hi!", "msgtype": "m.text"}
645609
new_body = {"msgtype": "m.text", "body": "Initial edit"}
646610
edit_event_content = {
647611
"msgtype": "m.text",
@@ -675,22 +639,20 @@ def test_edit_edit(self) -> None:
675639
access_token=self.user_token,
676640
)
677641
self.assertEqual(200, channel.code, channel.json_body)
678-
self.assertEqual(
679-
channel.json_body["content"], {"body": "Hi!", "msgtype": "m.text"}
680-
)
642+
self.assertEqual(channel.json_body["content"], orig_body)
681643

682644
# The relations information should not include the edit to the edit.
683645
self._assert_edit_bundle(channel.json_body, edit_event_id, edit_event_content)
684646

685-
# /context should return the event updated for the *first* edit
647+
# /context should return the bundled edit for the *first* edit
686648
# (The edit to the edit should be ignored.)
687649
channel = self.make_request(
688650
"GET",
689651
f"/rooms/{self.room}/context/{self.parent_id}",
690652
access_token=self.user_token,
691653
)
692654
self.assertEqual(200, channel.code, channel.json_body)
693-
self.assertEqual(channel.json_body["event"]["content"], new_body)
655+
self.assertEqual(channel.json_body["event"]["content"], orig_body)
694656
self._assert_edit_bundle(
695657
channel.json_body["event"], edit_event_id, edit_event_content
696658
)
@@ -1287,7 +1249,6 @@ def test_thread_edit_latest_event(self) -> None:
12871249
thread_summary = relations_dict[RelationTypes.THREAD]
12881250
self.assertIn("latest_event", thread_summary)
12891251
latest_event_in_thread = thread_summary["latest_event"]
1290-
self.assertEqual(latest_event_in_thread["content"]["body"], "I've been edited!")
12911252
# The latest event in the thread should have the edit appear under the
12921253
# bundled aggregations.
12931254
self.assertDictContainsSubset(

0 commit comments

Comments
 (0)