Compare commits

..

1 commit

Author SHA1 Message Date
Will Hunt 35d68cbbf8
Merge 695a97728d into 844dd6f8ae 2025-03-04 17:18:33 +00:00
26 changed files with 378 additions and 326 deletions

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Deduplicate the definition of `RoomKeysUpdateResponse`.

View file

@ -1 +0,0 @@
Deduplicate the definitions of `Invite3pid`.

View file

@ -1 +0,0 @@
Support more locations for examples in OpenAPI definitions and JSON schemas.

View file

@ -1 +0,0 @@
Add link to the git commit for the unstable changelog.

View file

@ -1 +0,0 @@
Remove an erroneous `room_id` field in a few examples.

View file

@ -131,7 +131,32 @@ paths:
A list of objects representing third-party IDs to invite into
the room.
items:
$ref: definitions/invite_3pid.yaml
type: object
title: Invite3pid
properties:
id_server:
type: string
description: The hostname+port of the identity server which should be used for
third-party identifier lookups.
id_access_token:
type: string
description: |-
An access token previously registered with the identity server. Servers
can treat this as optional to distinguish between r0.5-compatible clients
and this specification version.
medium:
type: string
description: |-
The kind of address being passed in the address field, for example `email`
(see [the list of recognised values](/appendices/#3pid-types)).
address:
type: string
description: The invitee's third-party identifier.
required:
- id_server
- id_access_token
- medium
- address
room_version:
type: string
description: |-

View file

@ -1,45 +0,0 @@
# Copyright 2025 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.
type: object
title: Invite3pid
properties:
id_server:
type: string
description: The hostname+port of the identity server which should be used for
third-party identifier lookups.
id_access_token:
type: string
description: |-
An access token previously registered with the identity server. Servers
can treat this as optional to distinguish between r0.5-compatible clients
and this specification version.
medium:
type: string
description: |-
The kind of address being passed in the address field, for example `email`
(see [the list of recognised values](/appendices/#3pid-types)).
address:
type: string
description: The invitee's third-party identifier.
required:
- id_server
- id_access_token
- medium
- address
example: {
"id_server": "matrix.org",
"id_access_token": "abc123_OpaqueString",
"medium": "email",
"address": "cheeky@monkey.com"
}

View file

@ -438,7 +438,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"403":
description: |-
The version specified does not match the current backup version.
@ -556,7 +571,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"404":
description: The backup was not found.
content:
@ -614,7 +644,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"403":
description: |-
The version specified does not match the current backup version.
@ -733,7 +778,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"404":
description: The backup was not found.
content:
@ -806,7 +866,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"403":
description: |-
The version specified does not match the current backup version.
@ -932,7 +1007,22 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/RoomKeysUpdateResponse"
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See `GET /room_keys/version/{version}` for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
"404":
description: The backup was not found.
content:
@ -966,26 +1056,6 @@ servers:
basePath:
default: /_matrix/client/v3
components:
schemas:
RoomKeysUpdateResponse:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See [`GET /room_keys/version/{version}`](client-server-api/#get_matrixclientv3room_keysversionversion)
for more details.
type: string
example: abcdefg
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
securitySchemes:
accessTokenQuery:
$ref: definitions/security.yaml#/accessTokenQuery

View file

@ -214,7 +214,7 @@ paths:
- public
description: |-
Whether this room is visible to the `/publicRooms` API
or not.
or not."
account_data:
type: array
description: |-

View file

@ -96,7 +96,7 @@ paths:
- public
description: |-
Whether this room is visible to the `/publicRooms` API
or not.
or not."
account_data:
type: array
description: The private data that this user has attached to this room.

View file

@ -76,7 +76,37 @@ paths:
content:
application/json:
schema:
$ref: definitions/invite_3pid.yaml
type: object
example: {
"id_server": "matrix.org",
"id_access_token": "abc123_OpaqueString",
"medium": "email",
"address": "cheeky@monkey.com"
}
properties:
id_server:
type: string
description: The hostname+port of the identity server which should be used for
third-party identifier lookups.
id_access_token:
type: string
description: |-
An access token previously registered with the identity server. Servers
can treat this as optional to distinguish between r0.5-compatible clients
and this specification version.
medium:
type: string
description: |-
The kind of address being passed in the address field, for example
`email` (see [the list of recognised values](/appendices/#3pid-types)).
address:
type: string
description: The invitee's third-party identifier.
required:
- id_server
- id_access_token
- medium
- address
required: true
responses:
"200":

View file

@ -75,6 +75,22 @@ paths:
example:
$ref: ../../event-schemas/examples/invite_room_state.json
type: object
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "invite"
},
"signatures": {
"example.com": {
"ed25519:key_version": "SomeSignatureHere"
},
}
}
required: true
responses:
"200":

View file

@ -78,6 +78,25 @@ paths:
required:
- room_version
- event
example: {
"room_version": "2",
"event": {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "invite"
},
"signatures": {
"example.com": {
"ed25519:key_version": "SomeSignatureHere"
},
}
}
}
required: true
responses:
"200":

View file

@ -140,6 +140,7 @@ paths:
- type
- content
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",

View file

@ -270,6 +270,7 @@ paths:
- type
- content
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",

View file

@ -31,17 +31,9 @@
<h1>{{ .Title }}</h1>
<table class="release-info">
{{ $rev := $version }}
{{ if eq $version "unstable" -}}
{{- /*
Extract the git SHA from the frontmatter of the changelog, where
it was stashed by `generate-changelog.sh`.
*/ -}}
{{ $rev = .Params.commit -}}
{{ end -}}
{{ $commitLink := printf "https://github.com/matrix-org/matrix-spec/tree/%s" $rev -}}
{{ if ne $version "unstable" -}}
{{ $commitLink := printf "https://github.com/matrix-org/matrix-spec/tree/%s" $version -}}
<tr><th>Git commit</th><td><a href="{{ $commitLink }}">{{ $commitLink }}</a></td>
{{ if ne $version "unstable" }}
<tr><th>Release date</th><td>{{ .Date | time.Format ":date_long" }}</td>
{{ end -}}
{{ $checklist := .OutputFormats.Get "checklist" -}}

View file

@ -13,4 +13,10 @@
{{ $path := delimit (slice "event-schemas/examples" .name) "/" }}
{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" .schema "path" $path) }}
{{ partial "render-example" (dict "example" $example_content) }}
{{ $example_json := jsonify (dict "indent" " ") $example_content }}
{{ $example_json = replace $example_json "\\u003c" "<" }}
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
```json
{{ $example_json }}
```

View file

@ -0,0 +1,46 @@
{{/*
For complex objects, example content is sometimes attached to the
object's individual properties (and subproperties...), so to get
a complete example for the whole object we need to iterate through
its properties (and subproperties...) and assemble them.
That's what this template does.
*/}}
{{ $this_object := . }}
{{ $example := $this_object.example }}
{{ if not $example }}
{{ if eq $this_object.type "object" }}
{{ $example = dict }}
{{ range $key, $property := $this_object.properties}}
{{ $this_property_example := partial "json-schema/resolve-example" $property }}
{{ if $this_property_example }}
{{ $example = merge (dict $key $this_property_example) $example }}
{{ end }}
{{ end }}
{{ else if eq $this_object.type "array" }}
{{/* the "items" within an array can either be an object (where we have a
list of items which match the schema), or a list (for tuple
validation, where each item has a different schema).
TODO: support tuple validation here.
*/}}
{{ if reflect.IsMap $this_object.items }}
{{ $items_example := partial "json-schema/resolve-example" $this_object.items }}
{{ if $items_example }}
{{ $example = slice $items_example }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ return $example }}

View file

@ -1,71 +0,0 @@
{{/*
Find examples in the given JSON schema.
Tries to find examples in the `examples` and `example` keys of the schema
first.
If it doesn't succeed, iterates through the properties and subproperties to
collect their examples, to merge them into a complete example for the whole
object.
Parameter: the JSON schema to extract the examples from.
*/}}
{{ $this_object := . }}
{{ $examples := slice }}
{{ if $this_object.examples }}
{{/* This is an array of examples, we can just use it */}}
{{ $examples = $this_object.examples }}
{{ else if $this_object.example }}
{{ $examples = slice $this_object.example }}
{{ else }}
{{/* Resolve the nested examples */}}
{{ if eq $this_object.type "object" }}
{{ $example := dict }}
{{ range $key, $property := $this_object.properties}}
{{ $this_property_examples := partial "json-schema/resolve-examples" $property }}
{{/*
It would be too complex to handle several nested examples,
just use the first one.
*/}}
{{ with index $this_property_examples 0 }}
{{ $example = merge (dict $key .) $example }}
{{ end }}
{{ end }}
{{/*
Add the assembled example to the list if either (a) the example is
non-empty, or (b) the object itself is meant to be empty (so an
empty example is correct).
*/}}
{{ if (or $example (not $this_object.properties)) }}
{{ $examples = slice $example }}
{{ end }}
{{ else if eq $this_object.type "array" }}
{{/* the "items" within an array can either be an object (where we have a
list of items which match the schema), or a list (for tuple
validation, where each item has a different schema).
TODO: support tuple validation here.
*/}}
{{ if reflect.IsMap $this_object.items }}
{{ $items_examples := partial "json-schema/resolve-examples" $this_object.items }}
{{/*
It would be too complex to handle several nested examples,
just use the first one.
*/}}
{{ with index $items_examples 0 }}
{{ $examples = slice (slice .) }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ return $examples }}

View file

@ -1,104 +0,0 @@
{{/*
Render a map of [Media Type Objects](https://spec.openapis.org/oas/v3.1.1#media-type-object).
This map can be found as the `content` field of requests and responses.
Parameters:
* `content`: A map of MIME type to Media Type Object
* `kind`: the kind of object that is rendered, either `request` or `response`.
* `anchor_base`: a prefix to add to the HTML anchors generated for each object
This template renders:
* Object tables if the map contains an `application/json` MIME type, or a
table with the MIME types and the corresponding description of the object.
* The examples of the Media Type Objects.
*/}}
{{/*
Request and response bodies can have several content types. If there is a
JSON type, we display that; otherwise we just show the content types and
descriptions.
*/}}
{{ $json_body := index .content "application/json" }}
{{ if $json_body }}
{{/*
Display the JSON schemas
*/}}
{{ $schema := $json_body.schema }}
{{/*
All this is to work out how to express the content of the response
in the case where it is an array.
*/}}
{{ if eq $schema.type "array" }}
{{ $type_of := "" }}
{{ if $schema.items.anyOf }}
{{ $types := slice }}
{{ range $schema.items.anyOf }}
{{ if .title }}
{{ $types = $types | append .title}}
{{ else }}
{{ $types = $types | append .type }}
{{ end }}
{{ end }}
{{ $type_of = delimit $types ", "}}
{{ else }}
{{ $type_of = $schema.items.title }}
{{ end }}
<p>Array of <code>{{ $type_of }}</code>.</p>
{{ end }}
{{/*
Render object tables for any objects referenced in the request/response.
This will be a no-op for request/response body types which aren't
objects or arrays.
*/}}
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" .anchor_base) }}
{{ range $additional_types }}
{{ partial "openapi/render-object-table" . }}
{{ end }}
{{ else }}
{{/*
Show the content types and description.
*/}}
{{ partial "openapi/render-content-type" (dict "content_types" .content) }}
{{ end }}
{{/*
Show all the examples.
*/}}
{{ if eq .kind "request" }}
<h3>Request body example</h3>
{{ end }}
{{ range $mime, $body := .content }}
{{ $examples := slice }}
{{/*
Find the examples to display, with the following priority:
1. The `examples` field
2. The `example` field
3. The examples in the `schema` field
*/}}
{{ if $body.examples }}
{{/* This is a map of string to Example Object */}}
{{ range $key, $example := $body.examples }}
{{/* The example is in the `value` field of the object */}}
{{ $examples = $examples | append (slice $example.value) }}
{{ end }}
{{ else if $body.example }}
{{ $examples = slice $body.example }}
{{ else if $body.schema }}
{{ $examples = partial "json-schema/resolve-examples" $body.schema }}
{{ end }}
{{ range $examples }}
{{ partial "render-example" (dict "example" . "mime" $mime) }}
{{ end }}
{{ end }}

View file

@ -33,8 +33,65 @@
{{ if $request_body }}
<h3>Request body</h3>
{{/*
A request can have several content types.
*/}}
{{ $json_body := index $request_body.content "application/json" }}
{{ if $json_body }}
{{/*
Display the JSON schemas
*/}}
{{ $schema := $json_body.schema }}
{{ partial "openapi/render-media-type-objects" (dict "content" $request_body.content "kind" "request" "anchor_base" $anchor) }}
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" $anchor) }}
{{ range $additional_types }}
{{ partial "openapi/render-object-table" . }}
{{ end }}
{{ else }}
{{/*
Show the content types and description.
*/}}
{{ partial "openapi/render-content-type" (dict "content_types" $request_body.content) }}
{{ end }}
<h3>Request body example</h3>
{{/*
Show all the examples.
*/}}
{{ range $mime, $body := $request_body.content }}
{{ $example := dict }}
{{ if $body.schema }}
{{ $example = partial "json-schema/resolve-example" $body.schema }}
{{ end }}
{{ if and (eq ($example | len) 0) $body.example }}
{{/*
If no example was generated from the schema, fallback to the
main example.
*/}}
{{ $example = $body.example }}
{{ end }}
{{ if eq $mime "application/json" }}
{{ $example_json := jsonify (dict "indent" " ") $example }}
{{ $example_json = replace $example_json "\\u003c" "<" }}
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
```json
{{ $example_json }}
```
{{ else }}
{{ $example = $example | safeHTML }}
{{/*
We need to set a language for the code otherwise the styling
is different than other examples.
*/}}
```json
{{ $example }}
```
{{ end }}
{{ end }}
{{ end }}
{{ else }}

View file

@ -59,6 +59,72 @@
{{ partial "openapi/render-object-table" (dict "title" "Headers" "properties" $headers_dict) }}
{{ end }}
{{ partial "openapi/render-media-type-objects" (dict "content" $response.content "kind" "response" "anchor_base" $anchor) }}
{{/*
A response can have several content types.
*/}}
{{ $json_body := index $response.content "application/json" }}
{{ if $json_body }}
{{/*
Display the JSON schemas
*/}}
{{ $schema := $json_body.schema }}
{{/*
All this is to work out how to express the content of the response
in the case where it is an array.
*/}}
{{ if eq $schema.type "array" }}
{{ $type_of := "" }}
{{ if $schema.items.anyOf }}
{{ $types := slice }}
{{ range $schema.items.anyOf }}
{{ if .title }}
{{ $types = $types | append .title}}
{{ else }}
{{ $types = $types | append .type }}
{{ end }}
{{ end }}
{{ $type_of = delimit $types ", "}}
{{ else }}
{{ $type_of = $schema.items.title }}
{{ end }}
<p>Array of <code>{{ $type_of }}</code>.</p>
{{ end }}
{{/*
render object tables for any objects referenced in the
response. (This will be a no-op for response types which aren't
objects or arrays.)
*/}}
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" $anchor) }}
{{ range $additional_types }}
{{ partial "openapi/render-object-table" . }}
{{ end }}
{{/*
render an example. currently this is only supported for arrays and objects.
*/}}
{{ if or (eq $schema.type "object") (eq $schema.type "array") }}
{{ $example := partial "json-schema/resolve-example" $schema }}
{{ if $json_body.examples }}
{{ $example = $json_body.examples.response.value }}
{{ end }}
{{ $example_json := jsonify (dict "indent" " ") $example }}
{{ $example_json = replace $example_json "\\u003c" "<" }}
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
```json
{{ $example_json }}
```
{{ end }}
{{ else }}
{{/*
Show the content types and description.
*/}}
{{ partial "openapi/render-content-type" (dict "content_types" $response.content) }}
{{ end }}
{{ end }}
{{ end }}

View file

@ -1,40 +0,0 @@
{{/*
Renders an example to be included in HTML, with support for pretty-printing
JSON.
Parameters:
* `example`: the example
* `mime`: the mime type of the example. Used to pretty-print JSON and for
syntax highlighting. If it is not provided, it is assumed to be
`application/json`.
*/}}
{{ $example := .example }}
{{/*
We need to convert the mime type to a recognized language.
For simplicity we only support JSON, which is also the default. Other mime
types are not highlighted.
*/}}
{{ $language := "json" }}
{{ if (and .mime (ne .mime "application/json")) }}
{{/*
`no-highlight` treats the value as plain text, but still styles the code
block like the ones with proper syntax highlighting.
*/}}
{{ $language = "no-highlight" }}
{{ end }}
{{ if eq $language "json" }}
{{ $example = jsonify (dict "indent" " ") $example }}
{{ $example = replace $example "\\u003c" "<" }}
{{ $example = replace $example "\\u003e" ">" }}
{{ end }}
```{{ $language }}
{{ $example | safeHTML }}
```

View file

@ -58,14 +58,13 @@
{{ partial "openapi/render-object-table" . }}
{{end}}
{{ $examples := partial "json-schema/resolve-examples" $definition }}
{{ if $examples }}
<h2>Examples</h2>
{{ range $examples }}
{{ partial "render-example" (dict "example" .) }}
{{ end }}
{{ end }}
{{ $example := partial "json-schema/resolve-example" $definition }}
```json
{{ jsonify (dict "indent" " ") $example }}
```
</details>

View file

@ -44,18 +44,8 @@ outputs:
- html
- checklist
date: $(date -Idate)
---
EOF
# Add the commit hash for the unstable versions. It is used to generate a
# link to the commit on the repository.
if [ "$VERSION" == "vUNSTABLE" ]; then
echo "params:"
echo " commit: $(git rev-parse --short HEAD)"
fi
# Close the frontmatter.
echo "---"
# Remove trailing whitespace (such as our intentionally blank RST headings)
sed -e "s/[ ]*$//" rendered.md
} > ../content/changelog/$FILENAME