Add MSC3030 experimental client and federation API endpoints to get the closest event to a given timestamp (#9445)

MSC3030: https://github.com/matrix-org/matrix-doc/pull/3030

Client API endpoint. This will also go and fetch from the federation API endpoint if unable to find an event locally or we found an extremity with possibly a closer event we don't know about.
```
GET /_matrix/client/unstable/org.matrix.msc3030/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
{
    "event_id": ...
    "origin_server_ts": ...
}
```

Federation API endpoint:
```
GET /_matrix/federation/unstable/org.matrix.msc3030/timestamp_to_event/<roomID>?ts=<timestamp>&dir=<direction>
{
    "event_id": ...
    "origin_server_ts": ...
}
```

Co-authored-by: Erik Johnston <erik@matrix.org>
This commit is contained in:
Eric Eastwood 2021-12-02 01:02:20 -06:00 committed by GitHub
parent 84dc50e160
commit a6f1a3abec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 674 additions and 31 deletions

View file

@ -1517,6 +1517,83 @@ class FederationClient(FederationBase):
self._get_room_hierarchy_cache[(room_id, suggested_only)] = result
return result
async def timestamp_to_event(
self, destination: str, room_id: str, timestamp: int, direction: str
) -> "TimestampToEventResponse":
"""
Calls a remote federating server at `destination` asking for their
closest event to the given timestamp in the given direction. Also
validates the response to always return the expected keys or raises an
error.
Args:
destination: Domain name of the remote homeserver
room_id: Room to fetch the event from
timestamp: The point in time (inclusive) we should navigate from in
the given direction to find the closest event.
direction: ["f"|"b"] to indicate whether we should navigate forward
or backward from the given timestamp to find the closest event.
Returns:
A parsed TimestampToEventResponse including the closest event_id
and origin_server_ts
Raises:
Various exceptions when the request fails
InvalidResponseError when the response does not have the correct
keys or wrong types
"""
remote_response = await self.transport_layer.timestamp_to_event(
destination, room_id, timestamp, direction
)
if not isinstance(remote_response, dict):
raise InvalidResponseError(
"Response must be a JSON dictionary but received %r" % remote_response
)
try:
return TimestampToEventResponse.from_json_dict(remote_response)
except ValueError as e:
raise InvalidResponseError(str(e))
@attr.s(frozen=True, slots=True, auto_attribs=True)
class TimestampToEventResponse:
"""Typed response dictionary for the federation /timestamp_to_event endpoint"""
event_id: str
origin_server_ts: int
# the raw data, including the above keys
data: JsonDict
@classmethod
def from_json_dict(cls, d: JsonDict) -> "TimestampToEventResponse":
"""Parsed response from the federation /timestamp_to_event endpoint
Args:
d: JSON object response to be parsed
Raises:
ValueError if d does not the correct keys or they are the wrong types
"""
event_id = d.get("event_id")
if not isinstance(event_id, str):
raise ValueError(
"Invalid response: 'event_id' must be a str but received %r" % event_id
)
origin_server_ts = d.get("origin_server_ts")
if not isinstance(origin_server_ts, int):
raise ValueError(
"Invalid response: 'origin_server_ts' must be a int but received %r"
% origin_server_ts
)
return cls(event_id, origin_server_ts, d)
@attr.s(frozen=True, slots=True, auto_attribs=True)
class FederationSpaceSummaryEventResult: