|
18 | 18 | # [This file includes modifications made by New Vector Limited]
|
19 | 19 | #
|
20 | 20 | #
|
21 |
| -from typing import TYPE_CHECKING, Dict, Optional |
| 21 | +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union |
22 | 22 |
|
23 | 23 | from synapse._pydantic_compat import HAS_PYDANTIC_V2
|
24 | 24 |
|
25 | 25 | if TYPE_CHECKING or HAS_PYDANTIC_V2:
|
26 |
| - from pydantic.v1 import Extra, StrictInt, StrictStr, constr, validator |
| 26 | + from pydantic.v1 import ( |
| 27 | + Extra, |
| 28 | + StrictBool, |
| 29 | + StrictInt, |
| 30 | + StrictStr, |
| 31 | + conint, |
| 32 | + constr, |
| 33 | + validator, |
| 34 | + ) |
27 | 35 | else:
|
28 |
| - from pydantic import Extra, StrictInt, StrictStr, constr, validator |
| 36 | + from pydantic import ( |
| 37 | + Extra, |
| 38 | + StrictBool, |
| 39 | + StrictInt, |
| 40 | + StrictStr, |
| 41 | + conint, |
| 42 | + constr, |
| 43 | + validator, |
| 44 | + ) |
29 | 45 |
|
30 | 46 | from synapse.rest.models import RequestBodyModel
|
31 | 47 | from synapse.util.threepids import validate_email
|
@@ -97,3 +113,172 @@ class EmailRequestTokenBody(ThreepidRequestTokenBody):
|
97 | 113 | class MsisdnRequestTokenBody(ThreepidRequestTokenBody):
|
98 | 114 | country: ISO3116_1_Alpha_2
|
99 | 115 | phone_number: StrictStr
|
| 116 | + |
| 117 | + |
| 118 | +class SlidingSyncBody(RequestBodyModel): |
| 119 | + """ |
| 120 | + Sliding Sync API request body. |
| 121 | +
|
| 122 | + Attributes: |
| 123 | + lists: Sliding window API. A map of list key to list information |
| 124 | + (:class:`SlidingSyncList`). Max lists: 100. The list keys should be |
| 125 | + arbitrary strings which the client is using to refer to the list. Keep this |
| 126 | + small as it needs to be sent a lot. Max length: 64 bytes. |
| 127 | + room_subscriptions: Room subscription API. A map of room ID to room subscription |
| 128 | + information. Used to subscribe to a specific room. Sometimes clients know |
| 129 | + exactly which room they want to get information about e.g by following a |
| 130 | + permalink or by refreshing a webapp currently viewing a specific room. The |
| 131 | + sliding window API alone is insufficient for this use case because there's |
| 132 | + no way to say "please track this room explicitly". |
| 133 | + extensions: Extensions API. A map of extension key to extension config. |
| 134 | + """ |
| 135 | + |
| 136 | + class CommonRoomParameters(RequestBodyModel): |
| 137 | + """ |
| 138 | + Common parameters shared between the sliding window and room subscription APIs. |
| 139 | +
|
| 140 | + Attributes: |
| 141 | + required_state: Required state for each room returned. An array of event |
| 142 | + type and state key tuples. Elements in this array are ORd together to |
| 143 | + produce the final set of state events to return. One unique exception is |
| 144 | + when you request all state events via `["*", "*"]`. When used, all state |
| 145 | + events are returned by default, and additional entries FILTER OUT the |
| 146 | + returned set of state events. These additional entries cannot use `*` |
| 147 | + themselves. For example, `["*", "*"], ["m.room.member", |
| 148 | + "@alice:example.com"]` will *exclude* every `m.room.member` event |
| 149 | + *except* for `@alice:example.com`, and include every other state event. |
| 150 | + In addition, `["*", "*"], ["m.space.child", "*"]` is an error, the |
| 151 | + `m.space.child` filter is not required as it would have been returned |
| 152 | + anyway. |
| 153 | + timeline_limit: The maximum number of timeline events to return per response. |
| 154 | + (Max 1000 messages) |
| 155 | + include_old_rooms: Determines if `predecessor` rooms are included in the |
| 156 | + `rooms` response. The user MUST be joined to old rooms for them to show up |
| 157 | + in the response. |
| 158 | + """ |
| 159 | + |
| 160 | + class IncludeOldRooms(RequestBodyModel): |
| 161 | + timeline_limit: StrictInt |
| 162 | + required_state: List[Tuple[StrictStr, StrictStr]] |
| 163 | + |
| 164 | + required_state: List[Tuple[StrictStr, StrictStr]] |
| 165 | + # mypy workaround via https://github.com/pydantic/pydantic/issues/156#issuecomment-1130883884 |
| 166 | + if TYPE_CHECKING: |
| 167 | + timeline_limit: int |
| 168 | + else: |
| 169 | + timeline_limit: conint(le=1000, strict=True) # type: ignore[valid-type] |
| 170 | + include_old_rooms: Optional[IncludeOldRooms] = None |
| 171 | + |
| 172 | + class SlidingSyncList(CommonRoomParameters): |
| 173 | + """ |
| 174 | + Attributes: |
| 175 | + ranges: Sliding window ranges. If this field is missing, no sliding window |
| 176 | + is used and all rooms are returned in this list. Integers are |
| 177 | + *inclusive*. |
| 178 | + sort: How the list should be sorted on the server. The first value is |
| 179 | + applied first, then tiebreaks are performed with each subsequent sort |
| 180 | + listed. |
| 181 | +
|
| 182 | + FIXME: Furthermore, it's not currently defined how servers should behave |
| 183 | + if they encounter a filter or sort operation they do not recognise. If |
| 184 | + the server rejects the request with an HTTP 400 then that will break |
| 185 | + backwards compatibility with new clients vs old servers. However, the |
| 186 | + client would be otherwise unaware that only some of the sort/filter |
| 187 | + operations have taken effect. We may need to include a "warnings" |
| 188 | + section to indicate which sort/filter operations are unrecognised, |
| 189 | + allowing for some form of graceful degradation of service. |
| 190 | + -- https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#filter-and-sort-extensions |
| 191 | +
|
| 192 | + slow_get_all_rooms: Just get all rooms (for clients that don't want to deal with |
| 193 | + sliding windows). When true, the `ranges` and `sort` fields are ignored. |
| 194 | + required_state: Required state for each room returned. An array of event |
| 195 | + type and state key tuples. Elements in this array are ORd together to |
| 196 | + produce the final set of state events to return. |
| 197 | +
|
| 198 | + One unique exception is when you request all state events via `["*", |
| 199 | + "*"]`. When used, all state events are returned by default, and |
| 200 | + additional entries FILTER OUT the returned set of state events. These |
| 201 | + additional entries cannot use `*` themselves. For example, `["*", "*"], |
| 202 | + ["m.room.member", "@alice:example.com"]` will *exclude* every |
| 203 | + `m.room.member` event *except* for `@alice:example.com`, and include |
| 204 | + every other state event. In addition, `["*", "*"], ["m.space.child", |
| 205 | + "*"]` is an error, the `m.space.child` filter is not required as it |
| 206 | + would have been returned anyway. |
| 207 | +
|
| 208 | + Room members can be lazily-loaded by using the special `$LAZY` state key |
| 209 | + (`["m.room.member", "$LAZY"]`). Typically, when you view a room, you |
| 210 | + want to retrieve all state events except for m.room.member events which |
| 211 | + you want to lazily load. To get this behaviour, clients can send the |
| 212 | + following:: |
| 213 | +
|
| 214 | + { |
| 215 | + "required_state": [ |
| 216 | + // activate lazy loading |
| 217 | + ["m.room.member", "$LAZY"], |
| 218 | + // request all state events _except_ for m.room.member |
| 219 | + events which are lazily loaded |
| 220 | + ["*", "*"] |
| 221 | + ] |
| 222 | + } |
| 223 | +
|
| 224 | + timeline_limit: The maximum number of timeline events to return per response. |
| 225 | + include_old_rooms: Determines if `predecessor` rooms are included in the |
| 226 | + `rooms` response. The user MUST be joined to old rooms for them to show up |
| 227 | + in the response. |
| 228 | + include_heroes: Return a stripped variant of membership events (containing |
| 229 | + `user_id` and optionally `avatar_url` and `displayname`) for the users used |
| 230 | + to calculate the room name. |
| 231 | + filters: Filters to apply to the list before sorting. |
| 232 | + bump_event_types: Allowlist of event types which should be considered recent activity |
| 233 | + when sorting `by_recency`. By omitting event types from this field, |
| 234 | + clients can ensure that uninteresting events (e.g. a profile rename) do |
| 235 | + not cause a room to jump to the top of its list(s). Empty or omitted |
| 236 | + `bump_event_types` have no effect—all events in a room will be |
| 237 | + considered recent activity. |
| 238 | + """ |
| 239 | + |
| 240 | + class Filters(RequestBodyModel): |
| 241 | + is_dm: Optional[StrictBool] = None |
| 242 | + spaces: Optional[List[StrictStr]] = None |
| 243 | + is_encrypted: Optional[StrictBool] = None |
| 244 | + is_invite: Optional[StrictBool] = None |
| 245 | + room_types: Optional[List[Union[StrictStr, None]]] = None |
| 246 | + not_room_types: Optional[List[StrictStr]] = None |
| 247 | + room_name_like: Optional[StrictStr] = None |
| 248 | + tags: Optional[List[StrictStr]] = None |
| 249 | + not_tags: Optional[List[StrictStr]] = None |
| 250 | + |
| 251 | + # mypy workaround via https://github.com/pydantic/pydantic/issues/156#issuecomment-1130883884 |
| 252 | + if TYPE_CHECKING: |
| 253 | + ranges: Optional[List[Tuple[int, int]]] = None |
| 254 | + else: |
| 255 | + ranges: Optional[List[Tuple[conint(ge=0, strict=True), conint(ge=0, strict=True)]]] = None # type: ignore[valid-type] |
| 256 | + sort: Optional[List[StrictStr]] = None |
| 257 | + slow_get_all_rooms: Optional[StrictBool] = False |
| 258 | + include_heroes: Optional[StrictBool] = False |
| 259 | + filters: Optional[Filters] = None |
| 260 | + bump_event_types: Optional[List[StrictStr]] = None |
| 261 | + |
| 262 | + class RoomSubscription(CommonRoomParameters): |
| 263 | + pass |
| 264 | + |
| 265 | + class Extension(RequestBodyModel): |
| 266 | + enabled: Optional[StrictBool] = False |
| 267 | + lists: Optional[List[StrictStr]] = None |
| 268 | + rooms: Optional[List[StrictStr]] = None |
| 269 | + |
| 270 | + # mypy workaround via https://github.com/pydantic/pydantic/issues/156#issuecomment-1130883884 |
| 271 | + if TYPE_CHECKING: |
| 272 | + lists: Optional[Dict[str, SlidingSyncList]] = None |
| 273 | + else: |
| 274 | + lists: Optional[Dict[constr(max_length=64, strict=True), SlidingSyncList]] = None # type: ignore[valid-type] |
| 275 | + room_subscriptions: Optional[Dict[StrictStr, RoomSubscription]] = None |
| 276 | + extensions: Optional[Dict[StrictStr, Extension]] = None |
| 277 | + |
| 278 | + @validator("lists") |
| 279 | + def lists_length_check( |
| 280 | + cls, value: Optional[Dict[str, SlidingSyncList]] |
| 281 | + ) -> Optional[Dict[str, SlidingSyncList]]: |
| 282 | + if value is not None: |
| 283 | + assert len(value) <= 100, f"Max lists: 100 but saw {len(value)}" |
| 284 | + return value |
0 commit comments