Compare commits

...

3 Commits

Author SHA1 Message Date
Richard van der Hoff 2b5f990f60
Factor out common definition of `Tag` type (#1793)
... and remove spurious `additionalProperties: true`
1 month ago
Kévin Commaille e82829d4a2
Make resolve-allof partial recursive (#1787)
Makes it easier to use, like resolve-refs. It just needs to be called once.

Fixes an issue with m.call.* events not displaying the common fields

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
1 month ago
Richard van der Hoff 073ce659df
Define opaque identifier grammar (#1791)
Since we already have three of these, and I'm about to add a fourth, let's pull
it out to a common definition.

We could, of course, keep defining the grammar each time it's used, but
defining it in an appendix helps us be consistent for future API design.
1 month ago

@ -0,0 +1 @@
Define 'Opaque Identifier Grammar'.

@ -0,0 +1 @@
Solve `allOf` recursively in OpenAPI and JSON Schemas.

@ -0,0 +1 @@
Factor out common definition of `Tag` type.

@ -921,6 +921,25 @@ unique servers based on the following criteria:
specify the servers it can. For example, a room with only 2 users in
it would result in maximum 2 `via` parameters.
### Opaque Identifiers
The specification defines some identifiers to use the *Opaque Identifier
Grammar*. This is a common grammar intended for non-user-visible identifiers
which do not require parsing or interpretation (other than as a unique
identifier).
The grammar is defined as:
* Identifiers must be entirely composed of the characters `[0-9]`, `[A-Z]`,
`[a-z]`, `-`, `.`, `_`, and `~`.
* Unless otherwise specified, identifiers must be at least one character and at
most 255 characters in length.
{{% boxes/note %}}
The acceptable character set matches the unreserved character set in [RFC
3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
{{% /boxes/note %}}
## 3PID Types
Third-party Identifiers (3PIDs) represent identifiers on other

@ -943,11 +943,12 @@ or completely closed registration (where the homeserver administrators create
and distribute accounts).
The token required for this authentication type is shared out of band from
Matrix and is an opaque string with maximum length of 64 characters in the
range `[A-Za-z0-9._~-]`. The server can keep any number of tokens for any
length of time/validity. Such cases might be a token limited to 100 uses or
for the next 2 hours - after the tokens expire, they can no longer be used
to create accounts.
Matrix and is an opaque string using the [Opaque Identifier
Grammar](/appendices#opaque-identifiers), with maximum length of 64
characters. The server can keep any number of tokens for any length of
time/validity. Such cases might be a token limited to 100 uses or for the next
2 hours - after the tokens expire, they can no longer be used to create
accounts.
To use this authentication type, clients should submit an auth dict with just
the type, token, and session:
@ -1201,7 +1202,7 @@ is complete, the client will need to submit a `/login` request matching
`m.login.token`.
{{< added-in v="1.7" >}} Already-authenticated clients can additionally generate
a token for their user ID if supported by the homeserver using
a token for their user ID if supported by the homeserver using
[`POST /login/get_token`](/client-server-api/#post_matrixclientv1loginget_token).
{{% http-api spec="client-server" api="login" %}}

@ -96,13 +96,8 @@ Matrix clients can send DTMF as specified by WebRTC. The WebRTC standard as of A
in the RTP payload.
#### Grammar for VoIP IDs
`call_id`s and `party_id` are explicitly defined to be between 1 and 255 characters long, consisting
of the characters `[0-9a-zA-Z._~-]`.
(Note that this matches the grammar of 'opaque IDs' from
[MSC1597](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/proposals/id_grammar/proposals/1597-id-grammar.md#opaque-ids),
and that of the `id` property of the
[`m.login.sso` flow schema](#definition-mloginsso-flow-schema).)
`call_id`s and `party_id` must follow the [Opaque Identifier Grammar](/appendices#opaque-identifiers).
#### Behaviour on Room Leave
If the client sees the user it is in a call with leave the room, the client should treat this

@ -40,10 +40,7 @@ properties:
description: |-
Opaque string chosen by the homeserver, uniquely identifying
the IdP from other IdPs the homeserver might support. Should
be between 1 and 255 characters in length, containing unreserved
characters under [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt)
(`ALPHA DIGIT "-" / "." / "_" / "~"`). Clients are not intended
to parse or infer meaning from opaque strings.
use the [Opaque identifier Grammar](/appendices#opaque-identifiers).
example: "com.example.idp.github"
name:
type: string

@ -0,0 +1,24 @@
# Copyright 2020-2024 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.
title: Tag
type: object
properties:
order:
type: number
format: float
description: |-
A number in a range `[0,1]` describing a relative
position of the room under the given tag.
example: 0.25

@ -53,16 +53,7 @@ paths:
tags:
type: object
additionalProperties:
title: Tag
type: object
properties:
order:
type: number
format: float
description: |-
A number in a range `[0,1]` describing a relative
position of the room under the given tag.
additionalProperties: true
$ref: definitions/tag.yaml
examples:
response:
value: {
@ -114,18 +105,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
order:
type: number
format: float
description: |-
A number in a range `[0,1]` describing a relative
position of the room under the given tag.
additionalProperties: true
example: {
"order": 0.25
}
$ref: definitions/tag.yaml
description: Extra data for the tag, e.g. ordering.
required: true
responses:

@ -17,16 +17,7 @@
"type": "object",
"description": "The tags on the room and their contents.",
"additionalProperties": {
"title": "Tag",
"type": "object",
"properties": {
"order": {
"type": "number",
"format": "float",
"description":
"A number in a range `[0,1]` describing a relative position of the room under the given tag."
}
}
"$ref": "../../api/client-server/definitions/tag.yaml"
}
}
}

@ -20,10 +20,6 @@
* The returned entries are based on the JSON schema definitions found by
* recursing through the input `schema`, with the following differences:
*
* * `allOf` references are expanded. (Although this partial requires that
* `resolve-allof` is called on the top-level `schema` beforehand,
* `resolve-allof` doesn't recurse down to subschemas).
*
* * If `anchor_base` is set, each object with a `title` and `properties`
* is given an `anchor`, which is a string suitable for using as an html
* anchor for that object schema.
@ -210,12 +206,7 @@
{{ errorf "Invalid call to partials/get-additional-objects: %s is not a map" $name .this_object }}
{{ end }}
/* Although we expect resolve-allof to be called on the input, resolve-allof does not recurse into
* nested schemas, so we have to call it again.
*/
{{ $this_object := partial "json-schema/resolve-allof" .this_object }}
{{ $res := partial "resolve-additional-types-inner" (dict "schema" $this_object "anchor_base" .anchor_base "name" $name) }}
{{ $res := partial "resolve-additional-types-inner" (dict "schema" .this_object "anchor_base" .anchor_base "name" $name) }}
{{ range $res.objects }}
{{ $all_objects = $all_objects | append (partial "clean-object" .) }}
{{ end }}

@ -1,7 +1,7 @@
{{/*
Resolves the `allOf` keyword (https://spec.openapis.org/oas/v3.1.0#composition-and-inheritance-polymorphism),
given a JSON schema object.
Resolves the `allOf` keyword (https://spec.openapis.org/oas/v3.1.0#composition-and-inheritance-polymorphism)
recursively, given a JSON schema object.
`allOf` is used to support a kind of inheritance for JSON schema objects.
@ -11,92 +11,71 @@
Of course the parent can itself inherit from *its* parent, so we recurse to
handle that.
Note that `allOf` is only resolved at the top level of the schema object. For
example, if you call this on an API definition which defines a `parameter`
which has an allOf schema, it will not be resolved. To handle this, the
openapi templates call resolve-allof for every schema object that they
process.
*/}}
{{ $ret := . }}
{{ $original := . }}
{{/*
We special-case 'required', and accumulate the values from all the 'allOf'
entries (rather than simply overriding them). Start the accumulation here.
*/}}
{{ $required := .required }}
{{ if not $required }}
{{ $required := slice }}
{{ end }}
{{ if reflect.IsSlice $original }}
{{/*
If it's a slice, just recurse.
*/}}
{{ $ret = slice }}
{{ with $ret.allOf }}
{{ range $original }}
{{ $resolved := partial "json-schema/resolve-allof" . }}
{{ $ret = $ret | append $resolved }}
{{ end }}
{{ else if reflect.IsMap $original }}
{{ $ret = dict }}
{{/*
construct a new dict, with each of the allOf entries merged into it in
turn.
We special-case 'required', and accumulate the values from all the 'allOf'
entries (rather than simply overriding them). Start the accumulation here.
*/}}
{{ $all_of_values := dict }}
{{ range . }}
{{ with .required }}
{{ $required = union $required . }}
{{ end }}
{{ $required := slice }}
{{ with $original.required }}
{{ $required = . }}
{{ end }}
{{ with $original.allOf }}
{{/*
With merge, values from the second argument override those from the first argument.
So this order will accumulate values from allOf items, allowing later ones to override earlier
Note also that `merge` does a *deep* merge - nested maps are also
merged. (Slices are replaced though.)
Merge each of the allOf entries.
*/}}
{{ $all_of_values = merge $all_of_values . }}
{{ range . }}
{{/*
First, resolve allOf in child.
*/}}
{{ $resolved := partial "json-schema/resolve-allof" . }}
{{ with $resolved.required }}
{{ $required = union $required . }}
{{ end }}
{{/*
With merge, values from the second argument override those from the first argument.
So this order will accumulate values from allOf items, allowing later ones to override earlier
Note also that `merge` does a *deep* merge - nested maps are also
merged. (Slices are replaced though.)
*/}}
{{ $ret = merge $ret $resolved }}
{{ end }}
{{ end }}
{{/*
Finally, merge in the original, allowing the original to override allOf.
*/}}
{{ $ret = merge $all_of_values $ret }}
{{/*
Except that if allOf *itself* contains allOf (ie, the parent also
inherits from a grandparent), then we replace allOf in the original
with that in the parent. Below, we see that this has happened, and
recurse.
TODO: surely it would be better to simply do the recursion as we iterate
though the allOf list above - not least because we might have multiple
parents with different grandparents, and by doing this we only get one
set of grandparents.
*/}}
{{ with $all_of_values.allOf }}
{{ $ret = merge $ret (dict "allOf" . ) }}
{{ range $key, $value := $original }}
{{ if and (ne $key "allOf") (ne $key "required") }}
{{ $resolved := partial "json-schema/resolve-allof" $value }}
{{ $ret = merge $ret (dict $key $resolved) }}
{{ end }}
{{ end }}
{{/*
special-case 'required': replace it with the union of all the
'required' arrays from the original and allOf values.
XXX: but first we merge in the original 'required', again? why
do we do that? it should already have been done at the start.
*/}}
{{ with $ret.required }}
{{ $required = union $required $ret.required }}
{{ with $required }}
{{ $ret = merge $ret (dict "required" .) }}
{{ end }}
{{ $ret = merge $ret (dict "required" $required) }}
{{ end }}
{{/*
If we replaced the 'allOf' dict with one from a grandparent, we now
need to recurse.
*/}}
{{ if ne $ret.allOf $original.allOf }}
{{ $resolved := partial "json-schema/resolve-allof" $ret }}
{{ $ret = merge $ret $resolved }}
{{ end }}
{{ return $ret }}

@ -9,7 +9,7 @@
*/}}
{{ $this_object := partial "json-schema/resolve-allof" . }}
{{ $this_object := . }}
{{ $example := $this_object.example }}

@ -38,9 +38,6 @@
</thead>
{{ range $property_name, $property := $properties }}
{{ $property := partial "json-schema/resolve-allof" $property }}
{{/*
Handle two ways of indicating "required", one for simple parameters,
the other for request and response body objects.
@ -67,7 +64,7 @@
<th class="col-description">Description</th>
</thead>
{{ $property := partial "json-schema/resolve-allof" . }}
{{ $property := . }}
<tr>
<td><code>{{ partial "partials/property-type" $property }}</code></td>
@ -111,9 +108,6 @@
like `[type]`.
*/}}
{{ $items := .items }}
{{ if .items }}
{{ $items = partial "json-schema/resolve-allof" .items }}
{{ end }}
{{ $inner_type := partial "property-type" $items }}
{{ $type = delimit (slice "[" $inner_type "]") "" }}
{{ else if or (reflect.IsSlice .type) .oneOf }}
@ -180,8 +174,7 @@
If the property uses `additionalProperties` to describe its
internal structure, handle this with a bit of recursion
*/}}
{{ $additionalProperties := partial "json-schema/resolve-allof" .additionalProperties }}
{{ $type = delimit (slice "{string: " (partial "property-type" $additionalProperties) "}" ) "" }}
{{ $type = delimit (slice "{string: " (partial "property-type" .additionalProperties) "}" ) "" }}
{{ else if reflect.IsMap .patternProperties }}
{{/*
If the property uses `patternProperties` to describe its
@ -193,7 +186,6 @@
{{ $types := slice }}
{{ range $pattern, $schema := .patternProperties}}
{{ $schema = partial "json-schema/resolve-allof" $schema }}
{{ $types = $types | append (partial "property-type" $schema) }}
{{ end }}

@ -40,7 +40,7 @@
{{/*
Display the JSON schemas
*/}}
{{ $schema := partial "json-schema/resolve-allof" $json_body.schema }}
{{ $schema := $json_body.schema }}
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" $anchor_base) }}
{{ range $additional_types }}
@ -67,9 +67,7 @@
{{ $example := dict }}
{{ if $body.schema }}
{{ $schema := partial "json-schema/resolve-allof" $body.schema }}
{{ $example = partial "json-schema/resolve-example" $schema }}
{{ $example = partial "json-schema/resolve-example" $body.schema }}
{{ end }}
{{ if and (eq ($example | len) 0) $body.example }}

@ -47,7 +47,7 @@
Display the JSON schemas
*/}}
{{ $schema := partial "json-schema/resolve-allof" $json_body.schema }}
{{ $schema := $json_body.schema }}
{{/*
All this is to work out how to express the content of the response

@ -24,5 +24,6 @@
{{ $path := delimit (slice "api" $spec $api) "/" }}
{{ $api_data = partial "json-schema/resolve-refs" (dict "schema" $api_data "path" $path) }}
{{ $api_data = partial "json-schema/resolve-allof" $api_data }}
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url) }}

Loading…
Cancel
Save