From 6634f73bff43c99165a807271802c569e1c16a0e Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 13 Mar 2024 16:19:25 +0100 Subject: [PATCH 1/6] Add missing 'in' in SSO specification (#1748) * Add missing 'in' in SSO specification Signed-off-by: Johannes Marbach * Use standard changelog entry for typos --------- Signed-off-by: Johannes Marbach Co-authored-by: Hubert Chathi --- changelogs/client_server/newsfragments/1748.clarification | 1 + content/client-server-api/modules/sso_login.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelogs/client_server/newsfragments/1748.clarification diff --git a/changelogs/client_server/newsfragments/1748.clarification b/changelogs/client_server/newsfragments/1748.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/1748.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/content/client-server-api/modules/sso_login.md b/content/client-server-api/modules/sso_login.md index 95a3e23a..1a46c84d 100644 --- a/content/client-server-api/modules/sso_login.md +++ b/content/client-server-api/modules/sso_login.md @@ -10,7 +10,7 @@ This module allows a Matrix homeserver to delegate user authentication to an external authentication server supporting one of these protocols. In this process, there are three systems involved: -- A Matrix client, using the APIs defined this specification, which +- A Matrix client, using the APIs defined in this specification, which is seeking to authenticate a user to a Matrix homeserver. - A Matrix homeserver, implementing the APIs defined in this specification, but which is delegating user authentication to the From 0b43b5a343f19ceea1b141fc0dd9be7227ba7454 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 13 Mar 2024 11:28:30 -0400 Subject: [PATCH 2/6] Add some clarifications around implementation requirements for MSCs (#1718) * clarification around implementation requirement, and mention new label * add changelog * fix typo * Fix typos Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../internal/newsfragments/1718.clarification | 1 + content/proposals.md | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 changelogs/internal/newsfragments/1718.clarification diff --git a/changelogs/internal/newsfragments/1718.clarification b/changelogs/internal/newsfragments/1718.clarification new file mode 100644 index 00000000..5b89875a --- /dev/null +++ b/changelogs/internal/newsfragments/1718.clarification @@ -0,0 +1 @@ +Add some clarifications around implementation requirements for MSCs diff --git a/content/proposals.md b/content/proposals.md index dac520c9..b58a98be 100644 --- a/content/proposals.md +++ b/content/proposals.md @@ -380,9 +380,18 @@ As part of the proposal process the Spec Core Team will require evidence of the MSC working in order for it to move into FCP. This can usually be a branch/pull request to whichever implementation of choice that proves the MSC works in practice, though in some cases the MSC itself will be -small enough to be considered proven. Where it's unclear if an MSC will -require an implementation proof, ask in -[\#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org). +small enough to be considered proven. Implementations do not need to be +merged or released, but must be of sufficient quality to show that the +MSC works. Where it's unclear if an MSC will require an implementation +proof, ask in [\#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org). +Proposals may require both server-side and client-side implementations. + +Proposals that have not yet been implemented will have the +`needs-implementation` label. After an implementation has been made, add a +comment in the GitHub issue indicating so. After an implementation has been +made, we will check it to verify that it implements the MSC. Proposals that +have implementations that have not yet been checked will have the +`implementation-needs-checking` label. ### Early release of an MSC/idea From 4d7e33ec26b232501c550d2dc11bf50742a5c4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= <76261501+zecakeh@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:50:49 +0100 Subject: [PATCH 3/6] Add support for `$ref` URIs containing fragments in OpenAPI definitions and JSON schemas (#1751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- .../internal/newsfragments/1751.clarification | 1 + layouts/partials/events/example.html | 11 ++- layouts/partials/events/render-event.html | 6 +- .../partials/json-schema/resolve-refs.html | 22 ++++- .../partials/openapi/render-parameters.html | 4 +- layouts/partials/openapi/render-request.html | 6 +- .../partials/openapi/render-responses.html | 5 +- layouts/shortcodes/definition.html | 4 - layouts/shortcodes/event.html | 2 +- layouts/shortcodes/http-api.html | 2 +- layouts/shortcodes/msgtypes.html | 2 +- scripts/check-event-schema-examples.py | 40 +-------- scripts/check-json-schemas.py | 15 +--- scripts/check-openapi-sources.py | 65 +------------- scripts/dump-openapi.py | 31 +------ scripts/helpers.py | 87 +++++++++++++++++++ 16 files changed, 140 insertions(+), 163 deletions(-) create mode 100644 changelogs/internal/newsfragments/1751.clarification create mode 100755 scripts/helpers.py diff --git a/changelogs/internal/newsfragments/1751.clarification b/changelogs/internal/newsfragments/1751.clarification new file mode 100644 index 00000000..50c50693 --- /dev/null +++ b/changelogs/internal/newsfragments/1751.clarification @@ -0,0 +1 @@ +Add support for `$ref` URIs containing fragments in OpenAPI definitions and JSON schemas. diff --git a/layouts/partials/events/example.html b/layouts/partials/events/example.html index 181de88f..90752fbd 100644 --- a/layouts/partials/events/example.html +++ b/layouts/partials/events/example.html @@ -1,13 +1,18 @@ {{/* - Renders an event example. Resolves `$ref`s, serializes as JSON, and ensures + Renders an event example. Resolves `$ref`s, serializes as JSON, and ensures that it can be included in HTML. - This partial is called with the example event object as its context. + Parameters: + + * `schema`: the schema of the example + * `name`: the name of the example */}} -{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" . "path" "event-schemas/examples") }} +{{ $path := delimit (slice "event-schemas/examples" .name) "/" }} + +{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" .schema "path" $path) }} {{ $example_json := jsonify (dict "indent" " ") $example_content }} {{ $example_json = replace $example_json "\\u003c" "<" }} {{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }} diff --git a/layouts/partials/events/render-event.html b/layouts/partials/events/render-event.html index 70752721..71ba60bd 100644 --- a/layouts/partials/events/render-event.html +++ b/layouts/partials/events/render-event.html @@ -77,7 +77,7 @@ */}} {{ if $desired_example_name }} {{ if eq $example_name $desired_example_name }} - {{ partial "events/example" $example }} + {{ partial "events/example" (dict "schema" $example "name" $example_name) }} {{ end }} {{/* If `$desired_example_name` is not given, we will include any @@ -86,7 +86,7 @@ the event name includes a "$". */}} {{ else if eq $event_name $example_name }} - {{ partial "events/example" $example }} + {{ partial "events/example" (dict "schema" $example "name" $example_name) }} {{/* If `$desired_example_name` is not given, we will include any examples whose first part (before "$") matches the event name @@ -96,7 +96,7 @@ {{ $pieces := split $example_name "$" }} {{ $example_base_name := index $pieces 0 }} {{ if eq $event_name $example_base_name }} - {{ partial "events/example" $example }} + {{ partial "events/example" (dict "schema" $example "name" $example_name) }} {{ end }} {{ end }} {{ end }} diff --git a/layouts/partials/json-schema/resolve-refs.html b/layouts/partials/json-schema/resolve-refs.html index 1d99201d..9a36e413 100644 --- a/layouts/partials/json-schema/resolve-refs.html +++ b/layouts/partials/json-schema/resolve-refs.html @@ -1,7 +1,10 @@ {{/* Resolves the `$ref` JSON schema keyword, by recursively replacing - it with the object it points to. + it with the object it points to, given: + + * `schema`: the schema where the references should be resolved + * `path`: the path of the file containing the schema This template uses [`Scratch`](https://gohugo.io/functions/scratch/) rather than a normal `dict` because with `dict` you can't replace key values: @@ -20,8 +23,12 @@ {{ $scratch.Set "result_map" dict }} {{ $ref_value := index $schema "$ref"}} - {{ if $ref_value}} - {{ $full_path := path.Join $path $ref_value }} + {{ if $ref_value }} + {{ $uri := urls.Parse $path }} + {{ $ref_uri := urls.Parse $ref_value }} + {{ $full_uri := $uri.ResolveReference $ref_uri }} + + {{ $full_path := strings.TrimPrefix "/" $full_uri.Path }} {{/* Apparently Hugo doesn't give us a nice way to split the extension off a filename. */}} @@ -30,11 +37,18 @@ {{ $ref := index site.Data $pieces }} + {{/* If there is a fragment, follow the JSON Pointer */}} + {{ if $full_uri.Fragment }} + {{ $fragment := strings.TrimPrefix "/" $full_uri.Fragment }} + {{ $pieces := split $fragment "/" }} + {{ $ref = index $ref $pieces }} + {{ end }} + {{ $new_path := (path.Split $full_path).Dir}} {{ $result_map := partial "json-schema/resolve-refs" (dict "schema" $ref "path" $new_path)}} {{ if $result_map}} {{ $scratch.Set "result_map" $result_map }} - {{end }} + {{ end }} {{ end }} diff --git a/layouts/partials/openapi/render-parameters.html b/layouts/partials/openapi/render-parameters.html index 925b0197..ecabfc05 100644 --- a/layouts/partials/openapi/render-parameters.html +++ b/layouts/partials/openapi/render-parameters.html @@ -5,6 +5,7 @@ * `parameters`: OpenAPI data specifying the parameters * `type`: the type of parameters to render: "header, ""path", "query" * `caption`: caption to use for the table + * `path`: the path where this definition was found, to enable us to resolve "$ref" This template renders a single table containing parameters of the given type. @@ -13,7 +14,9 @@ {{ $parameters := .parameters }} {{ $type := .type }} {{ $caption := .caption }} +{{ $path := .path }} +{{ $parameters = partial "json-schema/resolve-refs" (dict "schema" $parameters "path" $path) }} {{ $parameters_of_type := where $parameters "in" $type }} {{ with $parameters_of_type }} @@ -32,5 +35,4 @@ {{/* and render the parameters */}} {{ partial "openapi/render-object-table" (dict "title" $caption "properties" $param_dict) }} - {{ end }} diff --git a/layouts/partials/openapi/render-request.html b/layouts/partials/openapi/render-request.html index 3d4b0381..5ef55c64 100644 --- a/layouts/partials/openapi/render-request.html +++ b/layouts/partials/openapi/render-request.html @@ -26,9 +26,9 @@ {{ if $parameters }}

Request parameters

- {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "header" "caption" "header parameters") }} - {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "path" "caption" "path parameters") }} - {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "query" "caption" "query parameters") }} + {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "header" "caption" "header parameters" "path" .path) }} + {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "path" "caption" "path parameters" "path" .path) }} + {{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "query" "caption" "query parameters" "path" .path) }} {{ end }} diff --git a/layouts/partials/openapi/render-responses.html b/layouts/partials/openapi/render-responses.html index 99662ad0..82c2f954 100644 --- a/layouts/partials/openapi/render-responses.html +++ b/layouts/partials/openapi/render-responses.html @@ -26,6 +26,8 @@ Description +{{ $responses = partial "json-schema/resolve-refs" (dict "schema" $responses "path" $path) }} + {{ range $code, $response := $responses }} @@ -49,8 +51,7 @@ Display the JSON schemas */}} - {{ $schema := partial "json-schema/resolve-refs" (dict "schema" $json_body.schema "path" $path) }} - {{ $schema := partial "json-schema/resolve-allof" $schema }} + {{ $schema := partial "json-schema/resolve-allof" $json_body.schema }} {{/* All this is to work out how to express the content of the response diff --git a/layouts/shortcodes/definition.html b/layouts/shortcodes/definition.html index 0699ff7b..23461878 100644 --- a/layouts/shortcodes/definition.html +++ b/layouts/shortcodes/definition.html @@ -22,10 +22,6 @@ {{ errorf "site data %s not found" $path }} {{ end }} -{{/* The base path, which we use to resolve $ref, omits the last component */}} -{{ $pieces = first (sub (len $pieces) 1) $pieces}} -{{ $path = delimit $pieces "/" }} - {{/* Resolve $ref and allOf */}} {{ $definition = partial "json-schema/resolve-refs" (dict "schema" $definition "path" $path) }} {{ $definition = partial "json-schema/resolve-allof" $definition }} diff --git a/layouts/shortcodes/event.html b/layouts/shortcodes/event.html index a9838542..c671318a 100644 --- a/layouts/shortcodes/event.html +++ b/layouts/shortcodes/event.html @@ -25,7 +25,7 @@ */}} {{ $event_data := index .Site.Data "event-schemas" "schema" .Params.event }} -{{ $path := "event-schemas/schema" }} +{{ $path := delimit (slice "event-schemas/schema" .Params.event) "/" }} {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }} {{ $event_data := partial "json-schema/resolve-allof" $event_data }} diff --git a/layouts/shortcodes/http-api.html b/layouts/shortcodes/http-api.html index a3b706db..43d08b9e 100644 --- a/layouts/shortcodes/http-api.html +++ b/layouts/shortcodes/http-api.html @@ -21,6 +21,6 @@ {{ $api_data := index .Site.Data.api .Params.spec .Params.api }} {{ $base_url := (index $api_data.servers 0).variables.basePath.default }} -{{ $path := delimit (slice "api" $spec) "/" }} +{{ $path := delimit (slice "api" $spec $api) "/" }} {{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "path" $path) }} diff --git a/layouts/shortcodes/msgtypes.html b/layouts/shortcodes/msgtypes.html index ba731111..1ab28aae 100644 --- a/layouts/shortcodes/msgtypes.html +++ b/layouts/shortcodes/msgtypes.html @@ -6,7 +6,6 @@ */}} -{{ $path := "event-schemas/schema" }} {{ $compact := false }} {{/* @@ -40,6 +39,7 @@ {{ range $msgtypes }} {{ $event_data := index $site_data "event-schemas" "schema" . }} + {{ $path := delimit (slice "event-schemas/schema" .) "/" }} {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }} {{ $event_data := partial "json-schema/resolve-allof" $event_data }} diff --git a/scripts/check-event-schema-examples.py b/scripts/check-event-schema-examples.py index b258ca2e..9058ff4e 100755 --- a/scripts/check-event-schema-examples.py +++ b/scripts/check-event-schema-examples.py @@ -18,6 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import helpers import sys import json import os @@ -48,51 +49,16 @@ except ImportError as e: raise -def load_file(path): - print("Loading reference: %s" % path) - if not path.startswith("file://"): - raise Exception("Bad ref: %s" % (path,)) - path = path[len("file://"):] - with open(path, "r") as f: - if path.endswith(".json"): - return json.load(f) - else: - # We have to assume it's YAML because some of the YAML examples - # do not have file extensions. - return yaml.safe_load(f) - - -def resolve_references(path, schema): - if isinstance(schema, dict): - # do $ref first - if '$ref' in schema: - value = schema['$ref'] - path = os.path.abspath(os.path.join(os.path.dirname(path), value)) - ref = load_file("file://" + path) - result = resolve_references(path, ref) - del schema['$ref'] - else: - result = {} - - for key, value in schema.items(): - result[key] = resolve_references(path, value) - return result - elif isinstance(schema, list): - return [resolve_references(path, value) for value in schema] - else: - return schema - - def check_example_file(examplepath, schemapath): with open(examplepath) as f: - example = resolve_references(examplepath, json.load(f)) + example = helpers.resolve_references(examplepath, json.load(f)) with open(schemapath) as f: schema = yaml.safe_load(f) fileurl = "file://" + os.path.abspath(schemapath) schema["id"] = fileurl - resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": load_file}) + resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri}) print ("Checking schema for: %r %r" % (examplepath, schemapath)) try: diff --git a/scripts/check-json-schemas.py b/scripts/check-json-schemas.py index 3901300f..06b24106 100755 --- a/scripts/check-json-schemas.py +++ b/scripts/check-json-schemas.py @@ -18,6 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import helpers import sys import json import os @@ -67,23 +68,11 @@ class SchemaDirReport: def add(self, other_report): self.files += other_report.files self.errors += other_report.errors - -def load_file(path): - if not path.startswith("file://"): - raise Exception(f"Bad ref: {path}") - path = path[len("file://"):] - with open(path, "r") as f: - if path.endswith(".json"): - return json.load(f) - else: - # We have to assume it's YAML because some of the YAML examples - # do not have file extensions. - return yaml.safe_load(f) def check_example(path, schema, example): # URI with scheme is necessary to make RefResolver work. fileurl = "file://" + os.path.abspath(path) - resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": load_file}) + resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri}) validator = jsonschema.Draft202012Validator(schema, resolver) validator.validate(example) diff --git a/scripts/check-openapi-sources.py b/scripts/check-openapi-sources.py index 467e8091..7f28d860 100755 --- a/scripts/check-openapi-sources.py +++ b/scripts/check-openapi-sources.py @@ -19,6 +19,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import helpers import sys import json import os @@ -49,9 +50,7 @@ except ImportError as e: def check_schema(filepath, example, schema): - example = resolve_references(filepath, example) - schema = resolve_references(filepath, schema) - resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file}) + resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": helpers.load_file_from_uri}) validator = jsonschema.Draft202012Validator(schema, resolver) validator.validate(example) @@ -120,6 +119,8 @@ def check_openapi_file(filepath): with open(filepath) as f: openapi = yaml.safe_load(f) + openapi = helpers.resolve_references(filepath, openapi) + openapi_version = openapi.get('openapi') if not openapi_version: # This is not an OpenAPI file, skip. @@ -149,64 +150,6 @@ def check_openapi_file(filepath): check_response(filepath, request, code, json_response) -def resolve_references(path, schema): - """Recurse through a given schema until we find a $ref key. Upon doing so, - check that the referenced file exists, then load it up and check all of the - references in that file. Continue on until we've hit all dead ends. - - $ref values are deleted from schemas as they are validated, to prevent - duplicate work. - """ - if isinstance(schema, dict): - # do $ref first - if '$ref' in schema: - # Pull the referenced filepath from the schema - referenced_file = schema['$ref'] - - # Referenced filepaths are relative, so take the current path's - # directory and append the relative, referenced path to it. - inner_path = os.path.join(os.path.dirname(path), referenced_file) - - # Then convert the path (which may contiain '../') into a - # normalised, absolute path - inner_path = os.path.abspath(inner_path) - - # Load the referenced file - ref = load_file("file://" + inner_path) - - # Check that the references in *this* file are valid - result = resolve_references(inner_path, ref) - - # They were valid, and so were the sub-references. Delete - # the reference here to ensure we don't pass over it again - # when checking other files - del schema['$ref'] - else: - result = {} - - for key, value in schema.items(): - result[key] = resolve_references(path, value) - return result - elif isinstance(schema, list): - return [resolve_references(path, value) for value in schema] - else: - return schema - - -def load_file(path): - print("Loading reference: %s" % path) - if not path.startswith("file://"): - raise Exception("Bad ref: %s" % (path,)) - path = path[len("file://"):] - with open(path, "r") as f: - if path.endswith(".json"): - return json.load(f) - else: - # We have to assume it's YAML because some of the YAML examples - # do not have file extensions. - return yaml.safe_load(f) - - if __name__ == '__main__': # Get the directory that this script is residing in script_directory = os.path.dirname(os.path.realpath(__file__)) diff --git a/scripts/dump-openapi.py b/scripts/dump-openapi.py index 1cc2279c..490ac9bf 100755 --- a/scripts/dump-openapi.py +++ b/scripts/dump-openapi.py @@ -20,6 +20,7 @@ import argparse import errno +import helpers import json import logging import os.path @@ -31,34 +32,6 @@ import yaml scripts_dir = os.path.dirname(os.path.abspath(__file__)) api_dir = os.path.join(os.path.dirname(scripts_dir), "data", "api") -def resolve_references(path, schema): - if isinstance(schema, dict): - # do $ref first - if '$ref' in schema: - value = schema['$ref'] - previous_path = path - path = os.path.join(os.path.dirname(path), value) - try: - with open(path, encoding="utf-8") as f: - ref = yaml.safe_load(f) - result = resolve_references(path, ref) - del schema['$ref'] - path = previous_path - except FileNotFoundError: - print("Resolving {}".format(schema)) - print("File not found: {}".format(path)) - result = {} - else: - result = {} - - for key, value in schema.items(): - result[key] = resolve_references(path, value) - return result - elif isinstance(schema, list): - return [resolve_references(path, value) for value in schema] - else: - return schema - def prefix_absolute_path_references(text, base_url): """Adds base_url to absolute-path references. @@ -176,7 +149,7 @@ for filename in os.listdir(selected_api_dir): print("Reading OpenAPI: %s" % filepath) with open(filepath, "r") as f: api = yaml.safe_load(f.read()) - api = resolve_references(filepath, api) + api = helpers.resolve_references(filepath, api) basePath = api['servers'][0]['variables']['basePath']['default'] for path, methods in api["paths"].items(): diff --git a/scripts/helpers.py b/scripts/helpers.py new file mode 100755 index 00000000..c35e8e2a --- /dev/null +++ b/scripts/helpers.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Helpers to resolve $ref recursively in OpenAPI and JSON schemas. + +# Copyright 2016 OpenMarket Ltd +# +# 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. + +import json +import os +import os.path +import urllib.parse +import yaml + +def resolve_references(path, schema): + """Recurse through a given schema until we find a $ref key. Upon doing so, + check that the referenced file exists, then load it up and check all of the + references in that file. Continue on until we've hit all dead ends. + + $ref values are deleted from schemas as they are validated, to prevent + duplicate work. + """ + if isinstance(schema, dict): + # do $ref first + if '$ref' in schema: + # Pull the referenced URI from the schema + ref_uri = schema['$ref'] + + # Join the referenced URI with the URI of the file, to resolve + # relative URIs + full_ref_uri = urllib.parse.urljoin("file://" + path, ref_uri) + + # Separate the fragment. + (full_ref_uri, fragment) = urllib.parse.urldefrag(full_ref_uri) + + # Load the referenced file + ref = load_file_from_uri(full_ref_uri) + + if fragment: + # The fragment should be a JSON Pointer + keys = fragment.strip('/').split('/') + for key in keys: + ref = ref[key] + + # Check that the references in *this* file are valid + result = resolve_references(urllib.parse.urlsplit(full_ref_uri).path, ref) + + # They were valid, and so were the sub-references. Delete + # the reference here to ensure we don't pass over it again + # when checking other files + del schema['$ref'] + else: + result = {} + + for key, value in schema.items(): + result[key] = resolve_references(path, value) + return result + elif isinstance(schema, list): + return [resolve_references(path, value) for value in schema] + else: + return schema + + +def load_file_from_uri(path): + """Load a JSON or YAML file from a file:// URI. + """ + print("Loading reference: %s" % path) + if not path.startswith("file://"): + raise Exception("Bad ref: %s" % (path,)) + path = path[len("file://"):] + with open(path, "r") as f: + if path.endswith(".json"): + return json.load(f) + else: + # We have to assume it's YAML because some of the YAML examples + # do not have file extensions. + return yaml.safe_load(f) \ No newline at end of file From 5c96f455569b69ba841f1a456b3eab660bde5b9f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 19 Mar 2024 16:59:00 +0200 Subject: [PATCH 4/6] Specify that appservice login and register fail on incorrect as_tokens (#1744) Signed-off-by: Tulir Asokan --- .../application_service/newsfragments/1744.clarification | 1 + content/application-service-api.md | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 changelogs/application_service/newsfragments/1744.clarification diff --git a/changelogs/application_service/newsfragments/1744.clarification b/changelogs/application_service/newsfragments/1744.clarification new file mode 100644 index 00000000..908c48ab --- /dev/null +++ b/changelogs/application_service/newsfragments/1744.clarification @@ -0,0 +1 @@ +Clarify that the `/login` and `/register` endpoints should fail when using the `m.login.application_service` login type without a valid `as_token`. diff --git a/content/application-service-api.md b/content/application-service-api.md index f3db06cf..a7526e64 100644 --- a/content/application-service-api.md +++ b/content/application-service-api.md @@ -436,6 +436,12 @@ an application service-defined namespace will receive the same `M_EXCLUSIVE` error code, but only if the application service has defined the namespace as `exclusive`. +If `/register` or `/login` is called with the `m.login.application_service` +login type, but without a valid `as_token`, the endpoints will return an error +with the `M_MISSING_TOKEN` or `M_UNKNOWN_TOKEN` error code and 401 as the HTTP +status code. This is the same behavior as invalid auth in the client-server API +(see [Using access tokens](/client-server-api/#using-access-tokens)). + #### Pinging {{% added-in v="1.7" %}} From 38796de79a0c5f0b49b3d1a65806d8bd4e50a607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= <76261501+zecakeh@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:02:35 +0100 Subject: [PATCH 5/6] Add support for multi-stream VoIP (#1735) As per MSC3077. --- .../client_server/newsfragments/1735.feature | 1 + .../client-server-api/modules/voip_events.md | 33 +++++++++++++------ .../event-schemas/examples/m.call.answer.yaml | 8 +++++ .../event-schemas/examples/m.call.invite.yaml | 8 +++++ .../examples/m.call.negotiate.yaml | 8 +++++ .../components/sdp_stream_metadata.yaml | 27 +++++++++++++++ data/event-schemas/schema/m.call.answer.yaml | 3 ++ data/event-schemas/schema/m.call.invite.yaml | 5 ++- .../schema/m.call.negotiate.yaml | 2 ++ 9 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1735.feature create mode 100644 data/event-schemas/schema/components/sdp_stream_metadata.yaml diff --git a/changelogs/client_server/newsfragments/1735.feature b/changelogs/client_server/newsfragments/1735.feature new file mode 100644 index 00000000..1d764142 --- /dev/null +++ b/changelogs/client_server/newsfragments/1735.feature @@ -0,0 +1 @@ +Add support for multi-stream VoIP, as per [MSC3077](https://github.com/matrix-org/matrix-spec-proposals/pull/3077). \ No newline at end of file diff --git a/content/client-server-api/modules/voip_events.md b/content/client-server-api/modules/voip_events.md index 102e3dcd..4df0ba13 100644 --- a/content/client-server-api/modules/voip_events.md +++ b/content/client-server-api/modules/voip_events.md @@ -171,18 +171,31 @@ In response to an incoming invite, a client may do one of several things: ##### Streams -Clients are expected to send one stream with one track of kind `audio` (creating a -voice call). They can optionally send a second track in the same stream of kind -`video` (creating a video call). +Clients may send more than one stream in a VoIP call. The streams should be +differentiated by including metadata in the [`m.call.invite`](/client-server-api/#mcallinvite), +[`m.call.answer`](/client-server-api/#mcallanswer) and [`m.call.negotiate`](/client-server-api/#mcallnegotiate) +events, using the `sdp_stream_metadata` property. -Clients implementing this specification use the first stream and will ignore -any streamless tracks. Note that in the JavaScript WebRTC API, this means -`addTrack()` must be passed two parameters: a track and a stream, not just a -track, and in a video call the stream must be the same for both audio and video -track. +`sdp_stream_metadata` maps from the `id` of a stream in the session description, +to metadata about that stream. Currently only one property is defined for the +metadata. This is `purpose`, which should be a string indicating the purpose of +the stream. The following `purpose`s are defined: -A client may send other streams and tracks but the behaviour of the other party -with respect to presenting such streams and tracks is undefined. +* `m.usermedia` - stream that contains the webcam and/or microphone tracks +* `m.screenshare` - stream with the screen-sharing tracks + +If `sdp_stream_metadata` is present and an incoming stream is not listed in it, +the stream should be ignored. If a stream has a `purpose` of an unknown type, it +should also be ignored. + +For backwards compatibility, if `sdp_stream_metadata` is not present in the +initial [`m.call.invite`](/client-server-api/#mcallinvite) or [`m.call.answer`](/client-server-api/#mcallanswer) +event sent by the other party, the client should assume that this property is +not supported by the other party. It means that multiple streams cannot be +differentiated: the client should only use the first incoming stream and +shouldn't send more than one stream. + +Clients implementing this specification should ignore any streamless tracks. ##### Invitees The `invitee` field should be added whenever the call is intended for one diff --git a/data/event-schemas/examples/m.call.answer.yaml b/data/event-schemas/examples/m.call.answer.yaml index 78b48878..8a627360 100644 --- a/data/event-schemas/examples/m.call.answer.yaml +++ b/data/event-schemas/examples/m.call.answer.yaml @@ -8,6 +8,14 @@ "answer": { "type" : "answer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" + }, + "sdp_stream_metadata": { + "271828182845": { + "purpose": "m.screenshare" + }, + "314159265358": { + "purpose": "m.usermedia" + } } } } diff --git a/data/event-schemas/examples/m.call.invite.yaml b/data/event-schemas/examples/m.call.invite.yaml index 45600001..9547854b 100644 --- a/data/event-schemas/examples/m.call.invite.yaml +++ b/data/event-schemas/examples/m.call.invite.yaml @@ -9,6 +9,14 @@ "offer": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" + }, + "sdp_stream_metadata": { + "271828182845": { + "purpose": "m.screenshare" + }, + "314159265358": { + "purpose": "m.usermedia" + } } } } diff --git a/data/event-schemas/examples/m.call.negotiate.yaml b/data/event-schemas/examples/m.call.negotiate.yaml index fabb6add..aaf9daf2 100644 --- a/data/event-schemas/examples/m.call.negotiate.yaml +++ b/data/event-schemas/examples/m.call.negotiate.yaml @@ -9,6 +9,14 @@ "description": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" + }, + "sdp_stream_metadata": { + "271828182845": { + "purpose": "m.screenshare" + }, + "314159265358": { + "purpose": "m.usermedia" + } } } } diff --git a/data/event-schemas/schema/components/sdp_stream_metadata.yaml b/data/event-schemas/schema/components/sdp_stream_metadata.yaml new file mode 100644 index 00000000..f16b4cbd --- /dev/null +++ b/data/event-schemas/schema/components/sdp_stream_metadata.yaml @@ -0,0 +1,27 @@ +type: object +x-addedInMatrixVersion: "1.10" +description: |- + Metadata describing the [streams](/client-server-api/#streams) that will be + sent. + + This is a map of stream ID to metadata about the stream. +additionalProperties: + type: object + title: StreamMetadata + description: Metadata describing a stream. + properties: + purpose: + type: string + enum: + - m.usermedia + - m.screenshare + description: |- + The purpose of the stream. + + The possible values are: + + * `m.usermedia`: Stream that contains the webcam and/or microphone + tracks. + * `m.screenshare`: Stream with the screen-sharing tracks. + required: + - purpose diff --git a/data/event-schemas/schema/m.call.answer.yaml b/data/event-schemas/schema/m.call.answer.yaml index 163690be..15e07202 100644 --- a/data/event-schemas/schema/m.call.answer.yaml +++ b/data/event-schemas/schema/m.call.answer.yaml @@ -27,6 +27,9 @@ } }, "required": ["type", "sdp"] + }, + "sdp_stream_metadata": { + "$ref": "components/sdp_stream_metadata.yaml" } }, "required": ["answer"] diff --git a/data/event-schemas/schema/m.call.invite.yaml b/data/event-schemas/schema/m.call.invite.yaml index 72020b26..c688d7b3 100644 --- a/data/event-schemas/schema/m.call.invite.yaml +++ b/data/event-schemas/schema/m.call.invite.yaml @@ -35,7 +35,10 @@ "invitee": { "type": "string", "description": "The ID of the user being called. If omitted, any user in the room can answer.", - "x-addedInMatrixVersion": "1.7", + "x-addedInMatrixVersion": "1.7" + }, + "sdp_stream_metadata": { + "$ref": "components/sdp_stream_metadata.yaml" } }, "required": ["offer", "lifetime"] diff --git a/data/event-schemas/schema/m.call.negotiate.yaml b/data/event-schemas/schema/m.call.negotiate.yaml index b2b47c1d..e1a14f6f 100644 --- a/data/event-schemas/schema/m.call.negotiate.yaml +++ b/data/event-schemas/schema/m.call.negotiate.yaml @@ -63,6 +63,8 @@ properties: type: integer description: The time in milliseconds that the negotiation is valid for. Once the negotiation age exceeds this value, clients should discard it. + sdp_stream_metadata: + $ref: components/sdp_stream_metadata.yaml required: - description - lifetime From bb4003afa8eaad8c6aab247785ddc1da35b1c268 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 19 Mar 2024 17:03:44 +0000 Subject: [PATCH 6/6] Factor out all the common parameters of the various /relations apis (#1745) Contributed by @zecakeh https://github.com/zecakeh/matrix-spec/commit/bd5478105213036b7bf313754d05a32fc97db648. Co-authored-by: Richard van der Hoff --- .../internal/newsfragments/1745.clarification | 1 + data/api/client-server/relations.yaml | 557 ++++++------------ 2 files changed, 196 insertions(+), 362 deletions(-) create mode 100644 changelogs/internal/newsfragments/1745.clarification diff --git a/changelogs/internal/newsfragments/1745.clarification b/changelogs/internal/newsfragments/1745.clarification new file mode 100644 index 00000000..acd85892 --- /dev/null +++ b/changelogs/internal/newsfragments/1745.clarification @@ -0,0 +1 @@ +Factor out all the common parameters of the various `/relations` apis. diff --git a/data/api/client-server/relations.yaml b/data/api/client-server/relations.yaml index 65c3491a..fc35bf5d 100644 --- a/data/api/client-server/relations.yaml +++ b/data/api/client-server/relations.yaml @@ -33,69 +33,12 @@ paths: security: - accessToken: [] parameters: - - in: path - name: roomId - description: The ID of the room containing the parent event. - required: true - example: "!636q39766251:matrix.org" - schema: - type: string - - in: path - name: eventId - description: The ID of the parent event whose child events are to be returned. - required: true - example: $asfDuShaf7Gafaw - schema: - type: string - - in: query - name: from - description: |- - The pagination token to start returning results from. If not supplied, results - start at the most recent topological event known to the server. - - Can be a `next_batch` or `prev_batch` token from a previous call, or a returned - `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), - or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). - required: false - example: page2_token - schema: - type: string - - in: query - 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`. - required: false - example: page3_token - schema: - type: string - - in: query - 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 - example: 20 - schema: - type: integer - - in: query - name: dir - x-addedInMatrixVersion: "1.4" - description: |- - Optional (default `b`) direction to return events from. If this is set to `f`, events - will be returned in chronological order starting at `from`. If it - is set to `b`, events will be returned in *reverse* chronological - order, again starting at `from`. - schema: - type: string - enum: - - b - - f + - $ref: '#/components/parameters/roomId' + - $ref: '#/components/parameters/eventId' + - $ref: '#/components/parameters/from' + - $ref: '#/components/parameters/to' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/dir' responses: # note: this endpoint deliberately does not support rate limiting, therefore a # 429 error response is not included. @@ -107,60 +50,24 @@ paths: content: application/json: schema: - type: object - properties: - chunk: - title: ChildEventsChunk - type: array - description: The child events of the requested event, ordered topologically - most-recent first. - 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 this is the start of the result set, i.e. this is the first batch/page. - required: - - chunk + allOf: + - $ref: '#/components/schemas/response' + - type: object + properties: + chunk: + title: ChildEventsChunk + type: array + description: The child events of the requested event, ordered topologically + most-recent first. + items: + $ref: definitions/client_event.yaml + required: + - chunk examples: response: - value: { - "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" - } + $ref: '#/components/examples/response' "404": - description: |- - The parent event was not found or the user does not have permission to read - this event (it might be contained in history that is not accessible to the user). - content: - application/json: - schema: - $ref: definitions/errors/error.yaml - examples: - response: - value: { - "errcode": "M_NOT_FOUND", - "error": "Event not found." - } + $ref: '#/components/responses/404' tags: - Event relationships # The same as above, with added `/{relType}` @@ -182,77 +89,13 @@ paths: security: - accessToken: [] parameters: - - in: path - name: roomId - description: The ID of the room containing the parent event. - required: true - example: "!636q39766251:matrix.org" - schema: - type: string - - in: path - name: eventId - description: The ID of the parent event whose child events are to be returned. - required: true - example: $asfDuShaf7Gafaw - schema: - type: string - - in: path - name: relType - description: The [relationship type](/client-server-api/#relationship-types) to - search for. - required: true - example: org.example.my_relation - schema: - type: string - - in: query - name: from - description: |- - The pagination token to start returning results from. If not supplied, results - start at the most recent topological event known to the server. - - Can be a `next_batch` or `prev_batch` token from a previous call, or a returned - `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), - or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). - required: false - example: page2_token - schema: - type: string - - in: query - 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`. - required: false - example: page3_token - schema: - type: string - - in: query - 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 - example: 20 - schema: - type: integer - - in: query - name: dir - x-addedInMatrixVersion: "1.4" - description: |- - Optional (default `b`) direction to return events from. If this is set to `f`, events - will be returned in chronological order starting at `from`. If it - is set to `b`, events will be returned in *reverse* chronological - order, again starting at `from`. - schema: - type: string - enum: - - b - - f + - $ref: '#/components/parameters/roomId' + - $ref: '#/components/parameters/eventId' + - $ref: '#/components/parameters/relType' + - $ref: '#/components/parameters/from' + - $ref: '#/components/parameters/to' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/dir' responses: # note: this endpoint deliberately does not support rate limiting, therefore a # 429 error response is not included. @@ -264,62 +107,26 @@ paths: content: application/json: schema: - type: object - properties: - chunk: - title: ChildEventsChunk - type: array - description: |- - The child events of the requested event, ordered topologically - most-recent first. The events returned will match the `relType` - supplied in the URL. - 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 this is the start of the result set, i.e. this is the first batch/page. - required: - - chunk + allOf: + - $ref: '#/components/schemas/response' + - type: object + properties: + chunk: + title: ChildEventsChunk + type: array + description: |- + The child events of the requested event, ordered topologically + most-recent first. The events returned will match the `relType` + supplied in the URL. + items: + $ref: definitions/client_event.yaml + required: + - chunk examples: response: - value: { - "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" - } + $ref: '#/components/examples/response' "404": - description: |- - The parent event was not found or the user does not have permission to read - this event (it might be contained in history that is not accessible to the user). - content: - application/json: - schema: - $ref: definitions/errors/error.yaml - examples: - response: - value: { - "errcode": "M_NOT_FOUND", - "error": "Event not found." - } + $ref: '#/components/responses/404' tags: - Event relationships # The same as above, with added `/{eventType}` @@ -342,28 +149,9 @@ paths: security: - accessToken: [] parameters: - - in: path - name: roomId - description: The ID of the room containing the parent event. - required: true - example: "!636q39766251:matrix.org" - schema: - type: string - - in: path - name: eventId - description: The ID of the parent event whose child events are to be returned. - required: true - example: $asfDuShaf7Gafaw - schema: - type: string - - in: path - name: relType - description: The [relationship type](/client-server-api/#relationship-types) to - search for. - required: true - example: org.example.my_relation - schema: - type: string + - $ref: '#/components/parameters/roomId' + - $ref: '#/components/parameters/eventId' + - $ref: '#/components/parameters/relType' - in: path name: eventType description: |- @@ -375,55 +163,10 @@ paths: example: m.room.message schema: type: string - - in: query - name: from - description: |- - The pagination token to start returning results from. If not supplied, results - start at the most recent topological event known to the server. - - Can be a `next_batch` or `prev_batch` token from a previous call, or a returned - `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), - or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). - required: false - example: page2_token - schema: - type: string - - in: query - 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`. - required: false - example: page3_token - schema: - type: string - - in: query - 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 - example: 20 - schema: - type: integer - - in: query - name: dir - x-addedInMatrixVersion: "1.4" - description: |- - Optional (default `b`) direction to return events from. If this is set to `f`, events - will be returned in chronological order starting at `from`. If it - is set to `b`, events will be returned in *reverse* chronological - order, again starting at `from`. - schema: - type: string - enum: - - b - - f + - $ref: '#/components/parameters/from' + - $ref: '#/components/parameters/to' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/dir' responses: # note: this endpoint deliberately does not support rate limiting, therefore a # 429 error response is not included. @@ -435,62 +178,26 @@ paths: content: application/json: schema: - type: object - properties: - chunk: - title: ChildEventsChunk - type: array - description: |- - The child events of the requested event, ordered topologically most-recent - first. The events returned will match the `relType` and `eventType` supplied - in the URL. - 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 this is the start of the result set, i.e. this is the first batch/page. - required: - - chunk + allOf: + - $ref: '#/components/schemas/response' + - type: object + properties: + chunk: + title: ChildEventsChunk + type: array + description: |- + The child events of the requested event, ordered topologically most-recent + first. The events returned will match the `relType` and `eventType` supplied + in the URL. + items: + $ref: definitions/client_event.yaml + required: + - chunk examples: response: - value: { - "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" - } + $ref: '#/components/examples/response' "404": - description: |- - The parent event was not found or the user does not have permission to read - this event (it might be contained in history that is not accessible to the user). - content: - application/json: - schema: - $ref: definitions/errors/error.yaml - examples: - response: - value: { - "errcode": "M_NOT_FOUND", - "error": "Event not found." - } + $ref: '#/components/responses/404' tags: - Event relationships servers: @@ -508,3 +215,129 @@ servers: components: securitySchemes: $ref: definitions/security.yaml + parameters: + roomId: + in: path + name: roomId + description: The ID of the room containing the parent event. + required: true + example: "!636q39766251:matrix.org" + schema: + type: string + eventId: + in: path + name: eventId + description: The ID of the parent event whose child events are to be returned. + required: true + example: $asfDuShaf7Gafaw + schema: + type: string + from: + in: query + name: from + description: |- + The pagination token to start returning results from. If not supplied, results + start at the most recent topological event known to the server. + + Can be a `next_batch` or `prev_batch` token from a previous call, or a returned + `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), + or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). + required: false + example: page2_token + schema: + type: string + to: + in: query + 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`. + required: false + example: page3_token + schema: + type: string + limit: + in: query + 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 + example: 20 + schema: + type: integer + dir: + in: query + name: dir + x-addedInMatrixVersion: "1.4" + description: |- + Optional (default `b`) direction to return events from. If this is set to `f`, events + will be returned in chronological order starting at `from`. If it + is set to `b`, events will be returned in *reverse* chronological + order, again starting at `from`. + schema: + type: string + enum: + - b + - f + relType: + in: path + name: relType + description: The [relationship type](/client-server-api/#relationship-types) to + search for. + required: true + example: org.example.my_relation + schema: + type: string + schemas: + response: + type: object + properties: + 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 this is the start of the result set, i.e. this is the first batch/page. + responses: + "404": + description: |- + The parent event was not found or the user does not have permission to read + this event (it might be contained in history that is not accessible to the user). + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + examples: + response: + value: { + "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" + }