diff --git a/changelogs/internal/newsfragments/1796.clarification b/changelogs/internal/newsfragments/1796.clarification new file mode 100644 index 00000000..6a489a01 --- /dev/null +++ b/changelogs/internal/newsfragments/1796.clarification @@ -0,0 +1 @@ +Add support for pattern formats for `patternProperties`. diff --git a/data/custom-formats.yaml b/data/custom-formats.yaml new file mode 100644 index 00000000..f0001c80 --- /dev/null +++ b/data/custom-formats.yaml @@ -0,0 +1,33 @@ +# Copyright 2024 Commaille Kévin +# +# 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. + +# This file contains the list of custom formats supported for the `format` key +# and the `x-pattern-format` extension (see `openapi_extensions.md` for more +# details). +# +# Each entry must use the `mx-` prefix and have the form: +# +# mx-custom-key: +# title: The title rendered in the specification +# url: /url/to#definition + +mx-user-id: + title: User ID + url: /appendices#user-identifiers + # regex: "^@" + +mx-event-id: + title: Event ID + url: /appendices#event-ids + # regex: "^\\$" diff --git a/data/event-schemas/schema/m.ignored_user_list.yaml b/data/event-schemas/schema/m.ignored_user_list.yaml index 32cc6a46..af838490 100644 --- a/data/event-schemas/schema/m.ignored_user_list.yaml +++ b/data/event-schemas/schema/m.ignored_user_list.yaml @@ -17,7 +17,7 @@ properties: "^@": type: "object" description: "An empty object for future enhancement" - x-pattern: "$USER_ID" + x-pattern-format: "mx-user-id" required: - ignored_users type: diff --git a/data/event-schemas/schema/m.receipt.yaml b/data/event-schemas/schema/m.receipt.yaml index 702191d1..23a5d835 100644 --- a/data/event-schemas/schema/m.receipt.yaml +++ b/data/event-schemas/schema/m.receipt.yaml @@ -16,7 +16,7 @@ properties: patternProperties: "^\\$": type: object - x-pattern: "$EVENT_ID" + x-pattern-format: "mx-event-id" title: Event Receipts description: |- The collection of receipts for this event ID. @@ -34,7 +34,7 @@ properties: description: |- The mapping of user ID to receipt. The user ID is the entity who sent this receipt. - x-pattern: "$USER_ID" + x-pattern-format: "mx-user-id" properties: ts: type: integer diff --git a/layouts/partials/openapi/render-object-table.html b/layouts/partials/openapi/render-object-table.html index 6faa21d9..227e9e04 100644 --- a/layouts/partials/openapi/render-object-table.html +++ b/layouts/partials/openapi/render-object-table.html @@ -179,17 +179,45 @@ {{/* If the property uses `patternProperties` to describe its internal structure, handle this with a bit of recursion. - Note that we ignore the pattern as the current definitions + Types are grouped by pattern format. Note that we ignore + patterns without a format as the current definitions always have a single pattern, but we might need to handle them later to differentiate schemas according to patterns. */}} + + {{/* + Construct a map from format ID to the type string of the format. + */}} + {{ $formatMap := newScratch }} + + {{ range $pattern, $schema := .patternProperties }} + {{ $formatId := or (index $schema "x-pattern-format") "string" }} + + {{ if $formatMap.Get $formatId }} + {{ errorf "'%s' pattern format is defined more than once for the same property" $formatId }} + {{ end }} + + {{ $formatMap.Set $formatId (partial "property-type" $schema) }} + {{ end }} + + {{/* First generate the type string for each format. */}} {{ $types := slice }} + {{ range $formatId, $formatType := $formatMap.Values }} + {{ $formatKey := "string" }} + {{ if ne $formatId "string" }} + {{ with index site.Data "custom-formats" $formatId }} + {{ $formatKey = printf "%s" (htmlEscape .url) (htmlEscape .title) }} + {{ else }} + {{ errorf "Unsupported value for `x-pattern-format`: %s" $formatId }} + {{ end }} + {{ end }} - {{ range $pattern, $schema := .patternProperties}} - {{ $types = $types | append (partial "property-type" $schema) }} + {{ $formatString := printf "{%s: %s}" $formatKey $formatType }} + {{ $types = $types | append $formatString }} {{ end }} - {{ $type = delimit (slice "{string: " (delimit $types "|") "}" ) "" }} + {{/* Then join all the formats. */}} + {{ $type = delimit $types "|" }} {{ end }} {{ return $type }} diff --git a/openapi_extensions.md b/openapi_extensions.md index 8a7146a9..e8fd1002 100644 --- a/openapi_extensions.md +++ b/openapi_extensions.md @@ -31,3 +31,13 @@ particular Matrix specification versions. Although the OpenAPI/JSON Schema specs only allow to use `$ref` to reference a whole example, we use it to compose examples from other examples. + +## Custom `x-pattern-format` key and custom formats + +In JSON Schema, [`format`](https://json-schema.org/understanding-json-schema/reference/string#format) +is a property to convey semantic information about a schema. We define +`x-pattern-format` as a key on the schemas under `patternProperties` with the +same use as `format`, but that applies to the pattern of the property. We also +define custom values for formats with the `mx-` prefix in +`data/custom-formats.yaml`. Those values are recognized in the rendered +specification and link to the definition of the format.