mirror of
https://github.com/element-hq/synapse
synced 2024-07-20 10:39:13 +00:00
Add Pydantic model for the Sliding Sync API
This commit is contained in:
parent
f9c9d44360
commit
654e8f69ee
|
@ -23,6 +23,28 @@ import logging
|
||||||
import re
|
import re
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
|
from synapse._pydantic_compat import HAS_PYDANTIC_V2
|
||||||
|
|
||||||
|
if TYPE_CHECKING or HAS_PYDANTIC_V2:
|
||||||
|
from pydantic.v1 import (
|
||||||
|
StrictBool,
|
||||||
|
StrictInt,
|
||||||
|
StrictStr,
|
||||||
|
StringConstraints,
|
||||||
|
constr,
|
||||||
|
validator,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
from pydantic import (
|
||||||
|
StrictBool,
|
||||||
|
StrictInt,
|
||||||
|
StrictStr,
|
||||||
|
StringConstraints,
|
||||||
|
constr,
|
||||||
|
validator,
|
||||||
|
)
|
||||||
|
|
||||||
from synapse.api.constants import AccountDataTypes, EduTypes, Membership, PresenceState
|
from synapse.api.constants import AccountDataTypes, EduTypes, Membership, PresenceState
|
||||||
from synapse.api.errors import Codes, StoreError, SynapseError
|
from synapse.api.errors import Codes, StoreError, SynapseError
|
||||||
|
@ -44,9 +66,16 @@ from synapse.handlers.sync import (
|
||||||
SyncVersion,
|
SyncVersion,
|
||||||
)
|
)
|
||||||
from synapse.http.server import HttpServer
|
from synapse.http.server import HttpServer
|
||||||
from synapse.http.servlet import RestServlet, parse_boolean, parse_integer, parse_string
|
from synapse.http.servlet import (
|
||||||
|
RestServlet,
|
||||||
|
parse_and_validate_json_object_from_request,
|
||||||
|
parse_boolean,
|
||||||
|
parse_integer,
|
||||||
|
parse_string,
|
||||||
|
)
|
||||||
from synapse.http.site import SynapseRequest
|
from synapse.http.site import SynapseRequest
|
||||||
from synapse.logging.opentracing import trace_with_opname
|
from synapse.logging.opentracing import trace_with_opname
|
||||||
|
from synapse.rest.models import RequestBodyModel
|
||||||
from synapse.types import JsonDict, Requester, StreamToken
|
from synapse.types import JsonDict, Requester, StreamToken
|
||||||
from synapse.util import json_decoder
|
from synapse.util import json_decoder
|
||||||
|
|
||||||
|
@ -682,6 +711,136 @@ class SlidingSyncE2eeRestServlet(RestServlet):
|
||||||
return 200, response
|
return 200, response
|
||||||
|
|
||||||
|
|
||||||
|
class SlidingSyncBody(RequestBodyModel):
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
lists: Sliding window API. A map of list key to list information
|
||||||
|
(:class:`SlidingSyncList`). Max lists: 100. The list keys should be
|
||||||
|
arbitrary strings which the client is using to refer to the list. Keep this
|
||||||
|
small as it needs to be sent a lot. Max length: 64 bytes.
|
||||||
|
room_subscriptions: Room subscription API. A map of room ID to room subscription
|
||||||
|
information. Used to subscribe to a specific room. Sometimes clients know
|
||||||
|
exactly which room they want to get information about e.g by following a
|
||||||
|
permalink or by refreshing a webapp currently viewing a specific room. The
|
||||||
|
sliding window API alone is insufficient for this use case because there's
|
||||||
|
no way to say "please track this room explicitly".
|
||||||
|
extensions: TODO
|
||||||
|
"""
|
||||||
|
|
||||||
|
class CommonRoomParameters(RequestBodyModel):
|
||||||
|
"""
|
||||||
|
Common parameters shared between the sliding window and room subscription APIs.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
required_state: Required state for each room returned. An array of event
|
||||||
|
type and state key tuples. Elements in this array are ORd together to
|
||||||
|
produce the final set of state events to return. One unique exception is
|
||||||
|
when you request all state events via `["*", "*"]`. When used, all state
|
||||||
|
events are returned by default, and additional entries FILTER OUT the
|
||||||
|
returned set of state events. These additional entries cannot use `*`
|
||||||
|
themselves. For example, `["*", "*"], ["m.room.member",
|
||||||
|
"@alice:example.com"]` will *exclude* every `m.room.member` event
|
||||||
|
*except* for `@alice:example.com`, and include every other state event.
|
||||||
|
In addition, `["*", "*"], ["m.space.child", "*"]` is an error, the
|
||||||
|
`m.space.child` filter is not required as it would have been returned
|
||||||
|
anyway.
|
||||||
|
timeline_limit: The maximum number of timeline events to return per response.
|
||||||
|
include_old_rooms: Determines if `predecessor` rooms are included in the
|
||||||
|
`rooms` response. The user MUST be joined to old rooms for them to show up
|
||||||
|
in the response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class IncludeOldRooms(RequestBodyModel):
|
||||||
|
timeline_limit: StrictInt
|
||||||
|
required_state: List[Tuple[StrictStr, StrictStr]]
|
||||||
|
|
||||||
|
required_state: List[Tuple[StrictStr, StrictStr]]
|
||||||
|
timeline_limit: StrictInt
|
||||||
|
include_old_rooms: Optional[IncludeOldRooms]
|
||||||
|
|
||||||
|
class SlidingSyncList(CommonRoomParameters):
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
ranges: Sliding window ranges. If this field is missing, no sliding window
|
||||||
|
is used and all rooms are returned in this list. Integers are
|
||||||
|
*inclusive*.
|
||||||
|
sort: How the list should be sorted on the server. The first value is
|
||||||
|
applied first, then tiebreaks are performed with each subsequent sort
|
||||||
|
listed.
|
||||||
|
|
||||||
|
FIXME: Furthermore, it's not currently defined how servers should behave
|
||||||
|
if they encounter a filter or sort operation they do not recognise. If
|
||||||
|
the server rejects the request with an HTTP 400 then that will break
|
||||||
|
backwards compatibility with new clients vs old servers. However, the
|
||||||
|
client would be otherwise unaware that only some of the sort/filter
|
||||||
|
operations have taken effect. We may need to include a "warnings"
|
||||||
|
section to indicate which sort/filter operations are unrecognised,
|
||||||
|
allowing for some form of graceful degradation of service.
|
||||||
|
-- https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#filter-and-sort-extensions
|
||||||
|
|
||||||
|
required_state: Required state for each room returned. An array of event
|
||||||
|
type and state key tuples. Elements in this array are ORd together to
|
||||||
|
produce the final set of state events to return. One unique exception is
|
||||||
|
when you request all state events via `["*", "*"]`. When used, all state
|
||||||
|
events are returned by default, and additional entries FILTER OUT the
|
||||||
|
returned set of state events. These additional entries cannot use `*`
|
||||||
|
themselves. For example, `["*", "*"], ["m.room.member",
|
||||||
|
"@alice:example.com"]` will *exclude* every `m.room.member` event
|
||||||
|
*except* for `@alice:example.com`, and include every other state event.
|
||||||
|
In addition, `["*", "*"], ["m.space.child", "*"]` is an error, the
|
||||||
|
`m.space.child` filter is not required as it would have been returned
|
||||||
|
anyway.
|
||||||
|
timeline_limit: The maximum number of timeline events to return per response.
|
||||||
|
include_old_rooms: Determines if `predecessor` rooms are included in the
|
||||||
|
`rooms` response. The user MUST be joined to old rooms for them to show up
|
||||||
|
in the response.
|
||||||
|
include_heroes: Return a stripped variant of membership events (containing
|
||||||
|
`user_id` and optionally `avatar_url` and `displayname`) for the users used
|
||||||
|
to calculate the room name.
|
||||||
|
filters: Filters to apply to the list before sorting.
|
||||||
|
bump_event_types: Allowlist of event types which should be considered recent activity
|
||||||
|
when sorting `by_recency`. By omitting event types from this field,
|
||||||
|
clients can ensure that uninteresting events (e.g. a profile rename) do
|
||||||
|
not cause a room to jump to the top of its list(s). Empty or omitted
|
||||||
|
`bump_event_types` have no effect—all events in a room will be
|
||||||
|
considered recent activity.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Filters(RequestBodyModel):
|
||||||
|
is_dm: Optional[StrictBool]
|
||||||
|
spaces: Optional[List[StrictStr]]
|
||||||
|
is_encrypted: Optional[StrictBool]
|
||||||
|
is_invite: Optional[StrictBool]
|
||||||
|
room_types: Optional[List[Union[StrictStr, None]]]
|
||||||
|
not_room_types: Optional[List[StrictStr]]
|
||||||
|
room_name_like: Optional[StrictStr]
|
||||||
|
tags: Optional[List[StrictStr]]
|
||||||
|
not_tags: Optional[List[StrictStr]]
|
||||||
|
|
||||||
|
ranges: Optional[List[Tuple[StrictInt, StrictInt]]]
|
||||||
|
sort: Optional[List[StrictStr]]
|
||||||
|
include_heroes: Optional[StrictBool] = False
|
||||||
|
filters: Optional[Filters]
|
||||||
|
bump_event_types: Optional[List[StrictStr]]
|
||||||
|
|
||||||
|
class RoomSubscription(CommonRoomParameters):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Extension(RequestBodyModel):
|
||||||
|
enabled: Optional[StrictBool] = False
|
||||||
|
lists: Optional[List[StrictStr]]
|
||||||
|
rooms: Optional[List[StrictStr]]
|
||||||
|
|
||||||
|
lists: Dict[Annotated[StrictStr, StringConstraints(max_length=64)], SlidingSyncList]
|
||||||
|
room_subscriptions: Dict[StrictStr, RoomSubscription]
|
||||||
|
extensions: Dict[StrictStr, Extension]
|
||||||
|
|
||||||
|
@validator("lists")
|
||||||
|
def lists_length_check(cls, v):
|
||||||
|
assert len(v) <= 100, f"Max lists: 100 but saw {len(v)}"
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class SlidingSyncRestServlet(RestServlet):
|
class SlidingSyncRestServlet(RestServlet):
|
||||||
"""
|
"""
|
||||||
API endpoint for MSC3575 Sliding Sync `/sync`. TODO
|
API endpoint for MSC3575 Sliding Sync `/sync`. TODO
|
||||||
|
@ -710,6 +869,12 @@ class SlidingSyncRestServlet(RestServlet):
|
||||||
user = requester.user
|
user = requester.user
|
||||||
device_id = requester.device_id
|
device_id = requester.device_id
|
||||||
|
|
||||||
|
# TODO: We currently don't know whether we're going to use sticky params or
|
||||||
|
# maybe some filters like sync v2 where they are built up once and referenced
|
||||||
|
# by filter ID. For now, we will just prototype with always passing everything
|
||||||
|
# in.
|
||||||
|
body = parse_and_validate_json_object_from_request(request, SlidingSyncBody)
|
||||||
|
|
||||||
return 200, {"foo": "bar"}
|
return 200, {"foo": "bar"}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue