From 1435d06b711538883b6e23dbe70e14e3a6f8b61c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 17 May 2022 21:43:47 -0600 Subject: [PATCH] Define MSC2675 --- content/client-server-api/_index.md | 146 ++++++++++++++++++++++- data/api/client-server/relations.yaml | 165 ++++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 data/api/client-server/relations.yaml diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 2055f93e..f84a8b74 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1868,11 +1868,23 @@ An event with `m.relates_to` can relate to another event with `m.relates_to`, forming a sort of chain of events. {{% /boxes/note %}} +{{% boxes/note %}} +To allow the server to aggregate and find relations on events, the `m.relates_to` +key of an event must be included in the plaintext copy of the event. It cannot +be exclusively recorded in the encrypted payload of an event. +{{% /boxes/note %}} + +{{% boxes/warning %}} +If an encrypted event contains an `m.relates_to` in its payload, it should be +ignored and instead favour the plaintext `m.relates_to` copy (including when there +is no plaintext copy). +{{% /boxes/warning %}} + `m.relates_to` is described as follows: {{% definition path="api/client-server/definitions/m.relates_to" %}} -### Relationship types +#### Relationship types This specification describes the following relationship types: @@ -1887,6 +1899,138 @@ Future versions of this specification are expected to require certain behaviours or aggregation of related events. {{% /boxes/note %}} +#### Aggregations + +{{% added-in v="1.3" %}} + +Some relationships are "aggregated" by the server depending on their relationship +type. This can allow a set of related events to be summarised as a subset of values. + +For example, a relationship might define an extra `key` field which, when used with +the appropriate `rel_type`, would mean that the client receives a total count for +the number of times that `key` was used in a relationship. + +The actual aggregation format depends on the relationship type. + +{{% boxes/note %}} +This specification does not currently describe any relation types which require +aggregation, however [namespaced](/appendices#identifier-grammar) relationship +types might have aggregation behaviour. +{{% /boxes/note %}} + +When aggregations are summarised on an event, it is known as a "bundled aggregation" +or "bundle" for simplicity. The act of doing this is "bundling". + +The bundle for an event is found under the `unsigned` field of the event, when that +event is served to the client through the APIs listed below. The field, `m.relations`, +is an object with a key of the relationship type and value being the bundle itself. + +For example (unimportant fields not included): + +```json +{ + "event_id": "$my_event", + "unsigned": { + "m.relations": { + "org.example.possible_annotations": [ + { + "key": "👍", + "origin_server_ts": 1562763768320, + "count": 3 + }, + { + "key": "👎", + "origin_server_ts": 1562763768320, + "count": 1 + } + ], + "org.example.possible_thread": { + "current_server_participated": true, + "count": 7, + "latest_event": { + "event_id": "$another_event", + "content": { + "body": "Hello world" + } + } + } + } + } +} +``` + +Note how the `org.example.possible_annotations` bundle is an array compared to the +`org.example.possible_thread` bundle where the server is summarising the state of +the relationship in a single object. Both are valid bundles, and their exact types +depend on the relationship type. + +The endpoints where the server *should* include the `m.relations` unsigned field are: + +* [`GET /rooms/{roomId}/messages`](#get_matrixclientv3roomsroomidmessages) +* [`GET /rooms/{roomId}/context/{eventId}`](#get_matrixclientv3roomsroomidcontexteventid) +* [`GET /rooms/{roomId}/event/{eventId}`](#get_matrixclientv3roomsroomideventeventid) +* [`GET /rooms/{roomId}/relations`](#get_matrixclientv3roomsroomidrelations) +* [`GET /sync`](#get_matrixclientv3sync) when the relevant section has a `limited` value + of `true`. + +{{% boxes/note %}} +The server is **not** required to return bundles/aggregations on deprecated endpoints +such as `/initialSync`. +{{% /boxes/note %}} + +While this functionality allows the client to see what was known to the server at the +time of handling, the client should continue to aggregate locally if it is aware of +the relationship type's behaviour. For example, a client might increment a `count` +on a related event's bundle if it saw a new event which referenced that event. + +{{% boxes/warning %}} +The bundle provided by the server only includes events which were known at the time +the event was *received*. This can mean that in a single `/sync` response an event +will have a bundle and more events which qualify for that aggregation: in this case, +the client *should* aggregate the events which are "after" the event in question on +its own, as the server will not have considered them. +{{% /boxes/warning %}} + +{{% boxes/note %}} +Events from [ignored users](#ignoring-users) do not appear in the bundle or aggregation. +Clients will need to de-aggregate the events sent by ignored users to avoid them being +considered in counts. Similarly, servers must ensure they do not consider events from +ignored users when preparing a bundle for the client. +{{% /boxes/note %}} + +When an event is redacted, the relations attached to it remain. However, when an event +which uses a relation is redacted then the relation is broken. Thus, the server needs +to de-aggregate or disassociate an event from its parent when it is redacted. Clients +with local aggregation should do the same. + +It is suggested that clients perform local echo on aggregations. For instance, aggregating +the event into a bundle optimistically until the server returns a failure or the client +gives up on sending the event, at which point the event should be de-aggregated and an +error or similar shown. The client should be cautious to not aggregate an event twice if +it has already optimistically aggregated the event. Clients are encouraged to take this +a step further to additionally track related events which target unsent/pending events, +likely using the transaction ID as a temporary event ID until a proper event ID is known. + +{{% boxes/warning %}} +Due to history visibility restrictions, related events might not be visible to the user +if they are in a section of history the user cannot see. This can mean inaccurate bundles +for events that are "out of range". + +Additionally, if the server is missing portions of the room history then it may not be +able to accurately aggregate the events. +{{% /boxes/warning %}} + +#### API + +To retrieve relations for an event from the server, the client can call the following +endpoint with relevant information. The endpoint does not aggregate the events and is +instead paginated: clients can perform local aggregation if needed. + +This endpoint is particularly useful if the client has lost context on the bundle for +an event and needs to rebuild/verify it. + +{{% http-api spec="client-server" api="relations" %}} + ## Rooms ### Types diff --git a/data/api/client-server/relations.yaml b/data/api/client-server/relations.yaml new file mode 100644 index 00000000..0e8d6839 --- /dev/null +++ b/data/api/client-server/relations.yaml @@ -0,0 +1,165 @@ +# Copyright 2022 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server Relations API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}": + get: + summary: Get the related events for a given event, with optional filter. + description: |- + Retrieve all of the related events for a given event, optionally filtering + down by relationship type and related event type. + + Note that while this endpoint indicates `relType` and `eventType` being + required, they can be individually omitted to retrieve all matching events. + When omitting the path parameters, a trailing slash must not be included + (otherwise the search will be for an empty string). + operationId: getEventsRelatingToEvent + security: + - accessToken: [] + parameters: + - in: path + type: string + name: roomId + description: The ID of the room the supposed parent event is in. + required: true + x-example: "!636q39766251:matrix.org" + - in: path + type: string + name: eventId + description: The supposed parent event ID. + required: true + x-example: "$asfDuShaf7Gafaw" + - in: path + type: string + name: relType + description: |- + The [relationship type](/client-server-api/#relationship-types) to search + for, if any. Exclude to find all events which relate to the supposed parent + event with any `rel_type`. + required: true # we can't say false, annoyingly + x-example: "org.example.my_relation" + - in: path + type: string + name: eventType + description: |- + The event type of related events to search for, if any. Exclude to find all + events of any type which relate to the supposed parent event. + + Note that in encrypted rooms this will typically always be `m.room.encrypted` + regardless of the event type contained within the encrypted payload. + required: true # we can't say false, annoyingly + x-example: "m.room.message" + - in: query + type: string + name: from + description: |- + The pagination token to start returning results from. If not supplied, results + start at the earliest topological event known to the server. + + Can be a `next_batch` or `prev_batch` token from a previous call, or an equivalent + token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages) + or [`/sync`](/client-server-api/#get_matrixclientv3sync) to limit results to the + events returned by that section of timeline. + required: false + x-example: "page2_token" + - in: query + type: string + name: to + description: |- + The pagination token to stop returning results at. If not supplied, results + continue up to `limit` or until there are no more events. + + Like `from`, this can be a previous token from a prior call to this endpoint + or from `/messages` or `/sync` to limit to a section of timeline. + required: false + x-example: "page3_token" + - in: query + type: integer + name: limit + description: |- + The maximum number of results to return in a single `chunk`. The server can + and should apply a maximum value to this parameter to avoid large responses. + + Similarly, the server should apply a default value when not supplied. + required: false + x-example: 20 + responses: + # note: this endpoint deliberately does not support rate limiting, therefore a + # 429 error response is not included. + + 200: + description: |- + The paginated events which relate to the supposed parent. If no events are + related to the parent, an empty `chunk` is returned. + examples: + application/json: { + "chunk": [{ + "room_id": "!636q39766251:matrix.org", + "$ref": "../../event-schemas/examples/m.room.message$m.text.yaml", + "content": { + "m.relates_to": { + "rel_type": "org.example.my_relation", + "event_id": "$asfDuShaf7Gafaw" + } + } + }], + "next_batch": "page2_token", + "prev_batch": "page1_token" + } + schema: + type: object + properties: + chunk: + title: "RelatedEventsChunk" + type: array + description: |- + The events which relate to the parent event, after applicable filters. + items: + allOf: + - "$ref": "definitions/client_event.yaml" + next_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means there are no more results to fetch and the client should stop paginating. + prev_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means there are no prior results to fetch, i.e. this is the first batch. + 404: + description: The supposed parent event was not found or you do not have permission to read this event. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + schema: + "$ref": "definitions/errors/error.yaml" + tags: + - Event relationships