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

Commit c445611

Browse files
Add experimental support for MSC3391: deleting account data (#14714)
1 parent 044fa1a commit c445611

File tree

9 files changed

+547
-31
lines changed

9 files changed

+547
-31
lines changed

changelog.d/14714.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add experimental support for [MSC3391](https://github.com/matrix-org/matrix-spec-proposals/pull/3391) (removing account data).

docker/complement/conf/workers-shared-extra.yaml.j2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ experimental_features:
102102
{% endif %}
103103
# Filtering /messages by relation type.
104104
msc3874_enabled: true
105+
# Enable removing account data support
106+
msc3391_enabled: true
105107

106108
server_notices:
107109
system_mxid_localpart: _server

scripts-dev/complement.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ fi
190190

191191
extra_test_args=()
192192

193-
test_tags="synapse_blacklist,msc3787,msc3874"
193+
test_tags="synapse_blacklist,msc3787,msc3874,msc3391"
194194

195195
# All environment variables starting with PASS_ will be shared.
196196
# (The prefix is stripped off before reaching the container.)

synapse/config/experimental.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,6 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
136136
# Enable room version (and thus applicable push rules from MSC3931/3932)
137137
version_id = RoomVersions.MSC1767v10.identifier
138138
KNOWN_ROOM_VERSIONS[version_id] = RoomVersions.MSC1767v10
139+
140+
# MSC3391: Removing account data.
141+
self.msc3391_enabled = experimental.get("msc3391_enabled", False)

synapse/handlers/account_data.py

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
from typing import TYPE_CHECKING, Awaitable, Callable, Collection, List, Optional, Tuple
1818

1919
from synapse.replication.http.account_data import (
20+
ReplicationAddRoomAccountDataRestServlet,
2021
ReplicationAddTagRestServlet,
22+
ReplicationAddUserAccountDataRestServlet,
23+
ReplicationRemoveRoomAccountDataRestServlet,
2124
ReplicationRemoveTagRestServlet,
22-
ReplicationRoomAccountDataRestServlet,
23-
ReplicationUserAccountDataRestServlet,
25+
ReplicationRemoveUserAccountDataRestServlet,
2426
)
2527
from synapse.streams import EventSource
2628
from synapse.types import JsonDict, StreamKeyType, UserID
@@ -41,8 +43,18 @@ def __init__(self, hs: "HomeServer"):
4143
self._instance_name = hs.get_instance_name()
4244
self._notifier = hs.get_notifier()
4345

44-
self._user_data_client = ReplicationUserAccountDataRestServlet.make_client(hs)
45-
self._room_data_client = ReplicationRoomAccountDataRestServlet.make_client(hs)
46+
self._add_user_data_client = (
47+
ReplicationAddUserAccountDataRestServlet.make_client(hs)
48+
)
49+
self._remove_user_data_client = (
50+
ReplicationRemoveUserAccountDataRestServlet.make_client(hs)
51+
)
52+
self._add_room_data_client = (
53+
ReplicationAddRoomAccountDataRestServlet.make_client(hs)
54+
)
55+
self._remove_room_data_client = (
56+
ReplicationRemoveRoomAccountDataRestServlet.make_client(hs)
57+
)
4658
self._add_tag_client = ReplicationAddTagRestServlet.make_client(hs)
4759
self._remove_tag_client = ReplicationRemoveTagRestServlet.make_client(hs)
4860
self._account_data_writers = hs.config.worker.writers.account_data
@@ -112,7 +124,7 @@ async def add_account_data_to_room(
112124

113125
return max_stream_id
114126
else:
115-
response = await self._room_data_client(
127+
response = await self._add_room_data_client(
116128
instance_name=random.choice(self._account_data_writers),
117129
user_id=user_id,
118130
room_id=room_id,
@@ -121,15 +133,59 @@ async def add_account_data_to_room(
121133
)
122134
return response["max_stream_id"]
123135

136+
async def remove_account_data_for_room(
137+
self, user_id: str, room_id: str, account_data_type: str
138+
) -> Optional[int]:
139+
"""
140+
Deletes the room account data for the given user and account data type.
141+
142+
"Deleting" account data merely means setting the content of the account data
143+
to an empty JSON object: {}.
144+
145+
Args:
146+
user_id: The user ID to remove room account data for.
147+
room_id: The room ID to target.
148+
account_data_type: The account data type to remove.
149+
150+
Returns:
151+
The maximum stream ID, or None if the room account data item did not exist.
152+
"""
153+
if self._instance_name in self._account_data_writers:
154+
max_stream_id = await self._store.remove_account_data_for_room(
155+
user_id, room_id, account_data_type
156+
)
157+
if max_stream_id is None:
158+
# The referenced account data did not exist, so no delete occurred.
159+
return None
160+
161+
self._notifier.on_new_event(
162+
StreamKeyType.ACCOUNT_DATA, max_stream_id, users=[user_id]
163+
)
164+
165+
# Notify Synapse modules that the content of the type has changed to an
166+
# empty dictionary.
167+
await self._notify_modules(user_id, room_id, account_data_type, {})
168+
169+
return max_stream_id
170+
else:
171+
response = await self._remove_room_data_client(
172+
instance_name=random.choice(self._account_data_writers),
173+
user_id=user_id,
174+
room_id=room_id,
175+
account_data_type=account_data_type,
176+
content={},
177+
)
178+
return response["max_stream_id"]
179+
124180
async def add_account_data_for_user(
125181
self, user_id: str, account_data_type: str, content: JsonDict
126182
) -> int:
127183
"""Add some global account_data for a user.
128184
129185
Args:
130-
user_id: The user to add a tag for.
186+
user_id: The user to add some account data for.
131187
account_data_type: The type of account_data to add.
132-
content: A json object to associate with the tag.
188+
content: The content json dictionary.
133189
134190
Returns:
135191
The maximum stream ID.
@@ -148,14 +204,53 @@ async def add_account_data_for_user(
148204

149205
return max_stream_id
150206
else:
151-
response = await self._user_data_client(
207+
response = await self._add_user_data_client(
152208
instance_name=random.choice(self._account_data_writers),
153209
user_id=user_id,
154210
account_data_type=account_data_type,
155211
content=content,
156212
)
157213
return response["max_stream_id"]
158214

215+
async def remove_account_data_for_user(
216+
self, user_id: str, account_data_type: str
217+
) -> Optional[int]:
218+
"""Removes a piece of global account_data for a user.
219+
220+
Args:
221+
user_id: The user to remove account data for.
222+
account_data_type: The type of account_data to remove.
223+
224+
Returns:
225+
The maximum stream ID, or None if the room account data item did not exist.
226+
"""
227+
228+
if self._instance_name in self._account_data_writers:
229+
max_stream_id = await self._store.remove_account_data_for_user(
230+
user_id, account_data_type
231+
)
232+
if max_stream_id is None:
233+
# The referenced account data did not exist, so no delete occurred.
234+
return None
235+
236+
self._notifier.on_new_event(
237+
StreamKeyType.ACCOUNT_DATA, max_stream_id, users=[user_id]
238+
)
239+
240+
# Notify Synapse modules that the content of the type has changed to an
241+
# empty dictionary.
242+
await self._notify_modules(user_id, None, account_data_type, {})
243+
244+
return max_stream_id
245+
else:
246+
response = await self._remove_user_data_client(
247+
instance_name=random.choice(self._account_data_writers),
248+
user_id=user_id,
249+
account_data_type=account_data_type,
250+
content={},
251+
)
252+
return response["max_stream_id"]
253+
159254
async def add_tag_to_room(
160255
self, user_id: str, room_id: str, tag: str, content: JsonDict
161256
) -> int:

synapse/replication/http/account_data.py

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
logger = logging.getLogger(__name__)
2929

3030

31-
class ReplicationUserAccountDataRestServlet(ReplicationEndpoint):
31+
class ReplicationAddUserAccountDataRestServlet(ReplicationEndpoint):
3232
"""Add user account data on the appropriate account data worker.
3333
3434
Request format:
@@ -49,7 +49,6 @@ def __init__(self, hs: "HomeServer"):
4949
super().__init__(hs)
5050

5151
self.handler = hs.get_account_data_handler()
52-
self.clock = hs.get_clock()
5352

5453
@staticmethod
5554
async def _serialize_payload( # type: ignore[override]
@@ -73,7 +72,45 @@ async def _handle_request( # type: ignore[override]
7372
return 200, {"max_stream_id": max_stream_id}
7473

7574

76-
class ReplicationRoomAccountDataRestServlet(ReplicationEndpoint):
75+
class ReplicationRemoveUserAccountDataRestServlet(ReplicationEndpoint):
76+
"""Remove user account data on the appropriate account data worker.
77+
78+
Request format:
79+
80+
POST /_synapse/replication/remove_user_account_data/:user_id/:type
81+
82+
{
83+
"content": { ... },
84+
}
85+
86+
"""
87+
88+
NAME = "remove_user_account_data"
89+
PATH_ARGS = ("user_id", "account_data_type")
90+
CACHE = False
91+
92+
def __init__(self, hs: "HomeServer"):
93+
super().__init__(hs)
94+
95+
self.handler = hs.get_account_data_handler()
96+
97+
@staticmethod
98+
async def _serialize_payload( # type: ignore[override]
99+
user_id: str, account_data_type: str
100+
) -> JsonDict:
101+
return {}
102+
103+
async def _handle_request( # type: ignore[override]
104+
self, request: Request, user_id: str, account_data_type: str
105+
) -> Tuple[int, JsonDict]:
106+
max_stream_id = await self.handler.remove_account_data_for_user(
107+
user_id, account_data_type
108+
)
109+
110+
return 200, {"max_stream_id": max_stream_id}
111+
112+
113+
class ReplicationAddRoomAccountDataRestServlet(ReplicationEndpoint):
77114
"""Add room account data on the appropriate account data worker.
78115
79116
Request format:
@@ -94,7 +131,6 @@ def __init__(self, hs: "HomeServer"):
94131
super().__init__(hs)
95132

96133
self.handler = hs.get_account_data_handler()
97-
self.clock = hs.get_clock()
98134

99135
@staticmethod
100136
async def _serialize_payload( # type: ignore[override]
@@ -118,6 +154,44 @@ async def _handle_request( # type: ignore[override]
118154
return 200, {"max_stream_id": max_stream_id}
119155

120156

157+
class ReplicationRemoveRoomAccountDataRestServlet(ReplicationEndpoint):
158+
"""Remove room account data on the appropriate account data worker.
159+
160+
Request format:
161+
162+
POST /_synapse/replication/remove_room_account_data/:user_id/:room_id/:account_data_type
163+
164+
{
165+
"content": { ... },
166+
}
167+
168+
"""
169+
170+
NAME = "remove_room_account_data"
171+
PATH_ARGS = ("user_id", "room_id", "account_data_type")
172+
CACHE = False
173+
174+
def __init__(self, hs: "HomeServer"):
175+
super().__init__(hs)
176+
177+
self.handler = hs.get_account_data_handler()
178+
179+
@staticmethod
180+
async def _serialize_payload( # type: ignore[override]
181+
user_id: str, room_id: str, account_data_type: str, content: JsonDict
182+
) -> JsonDict:
183+
return {}
184+
185+
async def _handle_request( # type: ignore[override]
186+
self, request: Request, user_id: str, room_id: str, account_data_type: str
187+
) -> Tuple[int, JsonDict]:
188+
max_stream_id = await self.handler.remove_account_data_for_room(
189+
user_id, room_id, account_data_type
190+
)
191+
192+
return 200, {"max_stream_id": max_stream_id}
193+
194+
121195
class ReplicationAddTagRestServlet(ReplicationEndpoint):
122196
"""Add tag on the appropriate account data worker.
123197
@@ -139,7 +213,6 @@ def __init__(self, hs: "HomeServer"):
139213
super().__init__(hs)
140214

141215
self.handler = hs.get_account_data_handler()
142-
self.clock = hs.get_clock()
143216

144217
@staticmethod
145218
async def _serialize_payload( # type: ignore[override]
@@ -186,7 +259,6 @@ def __init__(self, hs: "HomeServer"):
186259
super().__init__(hs)
187260

188261
self.handler = hs.get_account_data_handler()
189-
self.clock = hs.get_clock()
190262

191263
@staticmethod
192264
async def _serialize_payload(user_id: str, room_id: str, tag: str) -> JsonDict: # type: ignore[override]
@@ -206,7 +278,11 @@ async def _handle_request( # type: ignore[override]
206278

207279

208280
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
209-
ReplicationUserAccountDataRestServlet(hs).register(http_server)
210-
ReplicationRoomAccountDataRestServlet(hs).register(http_server)
281+
ReplicationAddUserAccountDataRestServlet(hs).register(http_server)
282+
ReplicationAddRoomAccountDataRestServlet(hs).register(http_server)
211283
ReplicationAddTagRestServlet(hs).register(http_server)
212284
ReplicationRemoveTagRestServlet(hs).register(http_server)
285+
286+
if hs.config.experimental.msc3391_enabled:
287+
ReplicationRemoveUserAccountDataRestServlet(hs).register(http_server)
288+
ReplicationRemoveRoomAccountDataRestServlet(hs).register(http_server)

0 commit comments

Comments
 (0)