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

Commit b80bb7e

Browse files
authored
Fix /room/.../event/... to return the *original* event after any edits (#12476)
This is what the MSC (now) requires. Fixes #10310.
1 parent 798deb3 commit b80bb7e

File tree

4 files changed

+79
-39
lines changed

4 files changed

+79
-39
lines changed

changelog.d/12476.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug which incorrectly caused `GET /_matrix/client/r3/rooms/{roomId}/event/{eventId}` to return edited events rather than the original.

synapse/events/utils.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -402,17 +402,18 @@ def serialize_event(
402402
*,
403403
config: SerializeEventConfig = _DEFAULT_SERIALIZE_EVENT_CONFIG,
404404
bundle_aggregations: Optional[Dict[str, "BundledAggregations"]] = None,
405+
apply_edits: bool = True,
405406
) -> JsonDict:
406407
"""Serializes a single event.
407408
408409
Args:
409410
event: The event being serialized.
410411
time_now: The current time in milliseconds
411412
config: Event serialization config
412-
bundle_aggregations: Whether to include the bundled aggregations for this
413-
event. Only applies to non-state events. (State events never include
414-
bundled aggregations.)
415-
413+
bundle_aggregations: A map from event_id to the aggregations to be bundled
414+
into the event.
415+
apply_edits: Whether the content of the event should be modified to reflect
416+
any replacement in `bundle_aggregations[<event_id>].replace`.
416417
Returns:
417418
The serialized event
418419
"""
@@ -430,8 +431,9 @@ def serialize_event(
430431
event,
431432
time_now,
432433
config,
433-
bundle_aggregations[event.event_id],
434+
event_aggregations,
434435
serialized_event,
436+
apply_edits=apply_edits,
435437
)
436438

437439
return serialized_event
@@ -470,6 +472,7 @@ def _inject_bundled_aggregations(
470472
config: SerializeEventConfig,
471473
aggregations: "BundledAggregations",
472474
serialized_event: JsonDict,
475+
apply_edits: bool,
473476
) -> None:
474477
"""Potentially injects bundled aggregations into the unsigned portion of the serialized event.
475478
@@ -479,7 +482,8 @@ def _inject_bundled_aggregations(
479482
aggregations: The bundled aggregation to serialize.
480483
serialized_event: The serialized event which may be modified.
481484
config: Event serialization config
482-
485+
apply_edits: Whether the content of the event should be modified to reflect
486+
any replacement in `aggregations.replace`.
483487
"""
484488
serialized_aggregations = {}
485489

@@ -490,9 +494,10 @@ def _inject_bundled_aggregations(
490494
serialized_aggregations[RelationTypes.REFERENCE] = aggregations.references
491495

492496
if aggregations.replace:
493-
# If there is an edit, apply it to the event.
497+
# If there is an edit, optionally apply it to the event.
494498
edit = aggregations.replace
495-
self._apply_edit(event, serialized_event, edit)
499+
if apply_edits:
500+
self._apply_edit(event, serialized_event, edit)
496501

497502
# Include information about it in the relations dict.
498503
serialized_aggregations[RelationTypes.REPLACE] = {

synapse/rest/client/room.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -669,8 +669,10 @@ async def on_GET(
669669
)
670670

671671
time_now = self.clock.time_msec()
672+
# per MSC2676, /rooms/{roomId}/event/{eventId}, should return the
673+
# *original* event, rather than the edited version
672674
event_dict = self._event_serializer.serialize_event(
673-
event, time_now, bundle_aggregations=aggregations
675+
event, time_now, bundle_aggregations=aggregations, apply_edits=False
674676
)
675677
return 200, event_dict
676678

tests/rest/client/test_relations.py

+62-30
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,16 @@ def assert_bundle(event_json: JsonDict) -> None:
380380
{"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict
381381
)
382382

383+
# /event should return the *original* event
383384
channel = self.make_request(
384385
"GET",
385386
f"/rooms/{self.room}/event/{self.parent_id}",
386387
access_token=self.user_token,
387388
)
388389
self.assertEqual(200, channel.code, channel.json_body)
389-
self.assertEqual(channel.json_body["content"], new_body)
390+
self.assertEqual(
391+
channel.json_body["content"], {"body": "Hi!", "msgtype": "m.text"}
392+
)
390393
assert_bundle(channel.json_body)
391394

392395
# Request the room messages.
@@ -399,13 +402,15 @@ def assert_bundle(event_json: JsonDict) -> None:
399402
assert_bundle(self._find_event_in_chunk(channel.json_body["chunk"]))
400403

401404
# Request the room context.
405+
# /context should return the edited event.
402406
channel = self.make_request(
403407
"GET",
404408
f"/rooms/{self.room}/context/{self.parent_id}",
405409
access_token=self.user_token,
406410
)
407411
self.assertEqual(200, channel.code, channel.json_body)
408412
assert_bundle(channel.json_body["event"])
413+
self.assertEqual(channel.json_body["event"]["content"], new_body)
409414

410415
# Request sync, but limit the timeline so it becomes limited (and includes
411416
# bundled aggregations).
@@ -470,14 +475,14 @@ def test_multi_edit(self) -> None:
470475

471476
channel = self.make_request(
472477
"GET",
473-
f"/rooms/{self.room}/event/{self.parent_id}",
478+
f"/rooms/{self.room}/context/{self.parent_id}",
474479
access_token=self.user_token,
475480
)
476481
self.assertEqual(200, channel.code, channel.json_body)
477482

478-
self.assertEqual(channel.json_body["content"], new_body)
483+
self.assertEqual(channel.json_body["event"]["content"], new_body)
479484

480-
relations_dict = channel.json_body["unsigned"].get("m.relations")
485+
relations_dict = channel.json_body["event"]["unsigned"].get("m.relations")
481486
self.assertIn(RelationTypes.REPLACE, relations_dict)
482487

483488
m_replace_dict = relations_dict[RelationTypes.REPLACE]
@@ -492,10 +497,9 @@ def test_edit_reply(self) -> None:
492497
"""Test that editing a reply works."""
493498

494499
# Create a reply to edit.
500+
original_body = {"msgtype": "m.text", "body": "A reply!"}
495501
channel = self._send_relation(
496-
RelationTypes.REFERENCE,
497-
"m.room.message",
498-
content={"msgtype": "m.text", "body": "A reply!"},
502+
RelationTypes.REFERENCE, "m.room.message", content=original_body
499503
)
500504
reply = channel.json_body["event_id"]
501505

@@ -508,38 +512,54 @@ def test_edit_reply(self) -> None:
508512
)
509513
edit_event_id = channel.json_body["event_id"]
510514

515+
# /event returns the original event
511516
channel = self.make_request(
512517
"GET",
513518
f"/rooms/{self.room}/event/{reply}",
514519
access_token=self.user_token,
515520
)
516521
self.assertEqual(200, channel.code, channel.json_body)
522+
event_result = channel.json_body
523+
self.assertDictContainsSubset(original_body, event_result["content"])
517524

518-
# We expect to see the new body in the dict, as well as the reference
519-
# metadata sill intact.
520-
self.assertDictContainsSubset(new_body, channel.json_body["content"])
521-
self.assertDictContainsSubset(
522-
{
523-
"m.relates_to": {
524-
"event_id": self.parent_id,
525-
"rel_type": "m.reference",
526-
}
527-
},
528-
channel.json_body["content"],
525+
# also check /context, which returns the *edited* event
526+
channel = self.make_request(
527+
"GET",
528+
f"/rooms/{self.room}/context/{reply}",
529+
access_token=self.user_token,
529530
)
531+
self.assertEqual(200, channel.code, channel.json_body)
532+
context_result = channel.json_body["event"]
530533

531-
# We expect that the edit relation appears in the unsigned relations
532-
# section.
533-
relations_dict = channel.json_body["unsigned"].get("m.relations")
534-
self.assertIn(RelationTypes.REPLACE, relations_dict)
534+
# check that the relations are correct for both APIs
535+
for result_event_dict, desc in (
536+
(event_result, "/event"),
537+
(context_result, "/context"),
538+
):
539+
# The reference metadata should still be intact.
540+
self.assertDictContainsSubset(
541+
{
542+
"m.relates_to": {
543+
"event_id": self.parent_id,
544+
"rel_type": "m.reference",
545+
}
546+
},
547+
result_event_dict["content"],
548+
desc,
549+
)
535550

536-
m_replace_dict = relations_dict[RelationTypes.REPLACE]
537-
for key in ["event_id", "sender", "origin_server_ts"]:
538-
self.assertIn(key, m_replace_dict)
551+
# We expect that the edit relation appears in the unsigned relations
552+
# section.
553+
relations_dict = result_event_dict["unsigned"].get("m.relations")
554+
self.assertIn(RelationTypes.REPLACE, relations_dict, desc)
539555

540-
self.assert_dict(
541-
{"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict
542-
)
556+
m_replace_dict = relations_dict[RelationTypes.REPLACE]
557+
for key in ["event_id", "sender", "origin_server_ts"]:
558+
self.assertIn(key, m_replace_dict, desc)
559+
560+
self.assert_dict(
561+
{"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict
562+
)
543563

544564
def test_edit_thread(self) -> None:
545565
"""Test that editing a thread works."""
@@ -605,19 +625,31 @@ def test_edit_edit(self) -> None:
605625
)
606626

607627
# Request the original event.
628+
# /event should return the original event.
608629
channel = self.make_request(
609630
"GET",
610631
f"/rooms/{self.room}/event/{self.parent_id}",
611632
access_token=self.user_token,
612633
)
613634
self.assertEqual(200, channel.code, channel.json_body)
614-
# The edit to the edit should be ignored.
615-
self.assertEqual(channel.json_body["content"], new_body)
635+
self.assertEqual(
636+
channel.json_body["content"], {"body": "Hi!", "msgtype": "m.text"}
637+
)
616638

617639
# The relations information should not include the edit to the edit.
618640
relations_dict = channel.json_body["unsigned"].get("m.relations")
619641
self.assertIn(RelationTypes.REPLACE, relations_dict)
620642

643+
# /context should return the event updated for the *first* edit
644+
# (The edit to the edit should be ignored.)
645+
channel = self.make_request(
646+
"GET",
647+
f"/rooms/{self.room}/context/{self.parent_id}",
648+
access_token=self.user_token,
649+
)
650+
self.assertEqual(200, channel.code, channel.json_body)
651+
self.assertEqual(channel.json_body["event"]["content"], new_body)
652+
621653
m_replace_dict = relations_dict[RelationTypes.REPLACE]
622654
for key in ["event_id", "sender", "origin_server_ts"]:
623655
self.assertIn(key, m_replace_dict)

0 commit comments

Comments
 (0)