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

Commit 63f28e4

Browse files
authored
Handle room upgrades for spaces (#10774)
By copying the `room_type` field of the create event and migrating any non-empty `m.space.child` events to the new room that is created.
1 parent 318162f commit 63f28e4

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

changelog.d/10774.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Properly handle room upgrades of spaces.

synapse/handlers/room.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Membership,
3434
RoomCreationPreset,
3535
RoomEncryptionAlgorithms,
36+
RoomTypes,
3637
)
3738
from synapse.api.errors import (
3839
AuthError,
@@ -397,7 +398,7 @@ async def clone_existing_room(
397398
initial_state = {}
398399

399400
# Replicate relevant room events
400-
types_to_copy = (
401+
types_to_copy: List[Tuple[str, Optional[str]]] = [
401402
(EventTypes.JoinRules, ""),
402403
(EventTypes.Name, ""),
403404
(EventTypes.Topic, ""),
@@ -408,7 +409,16 @@ async def clone_existing_room(
408409
(EventTypes.ServerACL, ""),
409410
(EventTypes.RelatedGroups, ""),
410411
(EventTypes.PowerLevels, ""),
411-
)
412+
]
413+
414+
# If the old room was a space, copy over the room type and the rooms in
415+
# the space.
416+
if (
417+
old_room_create_event.content.get(EventContentFields.ROOM_TYPE)
418+
== RoomTypes.SPACE
419+
):
420+
creation_content[EventContentFields.ROOM_TYPE] = RoomTypes.SPACE
421+
types_to_copy.append((EventTypes.SpaceChild, None))
412422

413423
old_room_state_ids = await self.store.get_filtered_current_state_ids(
414424
old_room_id, StateFilter.from_types(types_to_copy)
@@ -419,6 +429,11 @@ async def clone_existing_room(
419429
for k, old_event_id in old_room_state_ids.items():
420430
old_event = old_room_state_events.get(old_event_id)
421431
if old_event:
432+
# If the event is an space child event with empty content, it was
433+
# removed from the space and should be ignored.
434+
if k[0] == EventTypes.SpaceChild and not old_event.content:
435+
continue
436+
422437
initial_state[k] = old_event.content
423438

424439
# deep-copy the power-levels event before we start modifying it

tests/rest/client/test_upgrade_room.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
# limitations under the License.
1414
from typing import Optional
1515

16+
from synapse.api.constants import EventContentFields, EventTypes, RoomTypes
1617
from synapse.config.server import DEFAULT_ROOM_VERSION
1718
from synapse.rest import admin
1819
from synapse.rest.client import login, room, room_upgrade_rest_servlet
20+
from synapse.server import HomeServer
1921

2022
from tests import unittest
2123
from tests.server import FakeChannel
@@ -29,9 +31,8 @@ class UpgradeRoomTest(unittest.HomeserverTestCase):
2931
room_upgrade_rest_servlet.register_servlets,
3032
]
3133

32-
def prepare(self, reactor, clock, hs):
34+
def prepare(self, reactor, clock, hs: "HomeServer"):
3335
self.store = hs.get_datastore()
34-
self.handler = hs.get_user_directory_handler()
3536

3637
self.creator = self.register_user("creator", "pass")
3738
self.creator_token = self.login(self.creator, "pass")
@@ -42,13 +43,18 @@ def prepare(self, reactor, clock, hs):
4243
self.room_id = self.helper.create_room_as(self.creator, tok=self.creator_token)
4344
self.helper.join(self.room_id, self.other, tok=self.other_token)
4445

45-
def _upgrade_room(self, token: Optional[str] = None) -> FakeChannel:
46+
def _upgrade_room(
47+
self, token: Optional[str] = None, room_id: Optional[str] = None
48+
) -> FakeChannel:
4649
# We never want a cached response.
4750
self.reactor.advance(5 * 60 + 1)
4851

52+
if room_id is None:
53+
room_id = self.room_id
54+
4955
return self.make_request(
5056
"POST",
51-
"/_matrix/client/r0/rooms/%s/upgrade" % self.room_id,
57+
f"/_matrix/client/r0/rooms/{room_id}/upgrade",
5258
# This will upgrade a room to the same version, but that's fine.
5359
content={"new_version": DEFAULT_ROOM_VERSION},
5460
access_token=token or self.creator_token,
@@ -157,3 +163,56 @@ def test_power_levels_tombstone(self):
157163
tok=self.creator_token,
158164
)
159165
self.assertNotIn(self.other, power_levels["users"])
166+
167+
def test_space(self):
168+
"""Test upgrading a space."""
169+
170+
# Create a space.
171+
space_id = self.helper.create_room_as(
172+
self.creator,
173+
tok=self.creator_token,
174+
extra_content={
175+
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
176+
},
177+
)
178+
179+
# Add the room as a child room.
180+
self.helper.send_state(
181+
space_id,
182+
event_type=EventTypes.SpaceChild,
183+
body={"via": [self.hs.hostname]},
184+
tok=self.creator_token,
185+
state_key=self.room_id,
186+
)
187+
188+
# Also add a room that was removed.
189+
old_room_id = "!notaroom:" + self.hs.hostname
190+
self.helper.send_state(
191+
space_id,
192+
event_type=EventTypes.SpaceChild,
193+
body={},
194+
tok=self.creator_token,
195+
state_key=old_room_id,
196+
)
197+
198+
# Upgrade the room!
199+
channel = self._upgrade_room(room_id=space_id)
200+
self.assertEquals(200, channel.code, channel.result)
201+
self.assertIn("replacement_room", channel.json_body)
202+
203+
new_space_id = channel.json_body["replacement_room"]
204+
205+
state_ids = self.get_success(self.store.get_current_state_ids(new_space_id))
206+
207+
# Ensure the new room is still a space.
208+
create_event = self.get_success(
209+
self.store.get_event(state_ids[(EventTypes.Create, "")])
210+
)
211+
self.assertEqual(
212+
create_event.content.get(EventContentFields.ROOM_TYPE), RoomTypes.SPACE
213+
)
214+
215+
# The child link should have been copied over.
216+
self.assertIn((EventTypes.SpaceChild, self.room_id), state_ids)
217+
# The child that was removed should not be copied over.
218+
self.assertNotIn((EventTypes.SpaceChild, old_room_id), state_ids)

0 commit comments

Comments
 (0)