{{/* Render a table listing the properties of an object, given: * `title`: optional caption for the table * `anchor`: optional HTML element id for the table * `properties`: optional dictionary of the properties to list, each given as: `property_name` : `property_data` * `additionalProperties`: a JSON Schema for additional properties on the object. * `patternProperties`: optional dictionary for properties with names adhering to a regex pattern. A map from regex pattern to JSON Schema. * `required`: optional array containing the names of required properties. In some cases (such as response body specifications) this isn't used, and instead properties have a `required` boolean attribute. We support this too. */}} {{ $title := .title }} {{ $properties := .properties}} {{ $required := .required}} {{ if $properties }} {{ with $title }} {{ . }} {{ end }} Name Type Description {{ range $property_name, $property := $properties }} {{/* Handle two ways of indicating "required", one for simple parameters, the other for request and response body objects. */}} {{ $required := cond (or (in $required $property_name) ( eq $property.required true )) true false }} {{ $property_name }} {{ partial "partials/property-type" $property | safeHTML }} {{ partial "partials/property-description" (dict "property" $property "required" $required) }} {{ end }} {{/* If the object has additional properties *as well as* regular properties, we add a special row to the table. Note that, per https://json-schema.org/draft/2020-12/json-schema-core#name-boolean-json-schemas, JSON schemas can be a simple "true" or "false" as well as the more normal object. `additionalProperties: true` is pretty much the default for Matrix (it means: "you're allowed to include random unspecced properties in your object"), so nothing to do here. `additionalProperties: false` means "you're not allowed to include any unspecced properties in your object". We may want to consider how to display that; for now we just ignore it. TODO: support `patternProperties` here. */}} {{ if reflect.IsMap .additionalProperties }} <Other properties> {{ partial "partials/property-type" .additionalProperties | safeHTML }} {{ partial "partials/property-description" (dict "property" .additionalProperties) }} {{ end }} {{ else if (or .additionalProperties .patternProperties) }} {{/* A special format of table for objects which only have additionalProperties or patternProperties. This is only ever used for top-level objects. Nested objects in this situation are just shown as rows within their parent object, and don't get their own table. (They are filtered out in resolve-additional-types.) */}} {{ with $title }} {{ . }} {{ end }} Type Description {{ $property := . }} {{ partial "partials/property-type" $property | safeHTML }} {{ partial "partials/property-description" (dict "property" $property) }} {{ end }} {{/* Computes the type to display for a property's schema, given: * `type`: optional string or array of strings for the type(s) of the property * `title`: optional string for the title of the property * `oneOf`: optional array of dictionaries describing the different formats that the property can have * `anyOf`: optional array of dictionaries describing the different formats that the property can have * `properties`: if the type is an object, optional dictionary for well-defined properties, each given as: `property_name` : `property_data` * `additionalProperties`: if the type is an object, optional dictionary for properties with undefined names * `patternProperties`: if the type is an object, optional dictionary for properties with names adhering to a regex pattern * `items`: if the type is an array, array of dictionaries describing the format of the array's items * `anchor`: optional HTML element id for the target type, which will be used to link to it. * `format`: optional string for the format of the type, used for strings. */}} {{ define "partials/property-type" }} {{ $type := "" }} {{ if eq .type "object" }} {{/* Resolve the type or title of the object */}} {{ $type = partial "object-type-or-title" . }} {{ else if eq .type "array"}} {{/* If the property is an array, indicate this with square brackets, like `[type]`. */}} {{ $items := .items }} {{ $inner_type := partial "property-type" $items }} {{ $type = delimit (slice "[" $inner_type "]") "" }} {{ else if eq .type "string" }} {{ $type = "string" }} {{/* If the string uses a known format, use it. */}} {{ with .format }} {{ with partial "string-format" . }} {{ $type = . }} {{ end }} {{ end }} {{ else if or (reflect.IsSlice .type) .oneOf .anyOf }} {{/* It's legal to specify an array of types. There are three ways to do that: - Use an array of strings. - Use oneOf, with items having a schema. - Use anyOf, with items having a schema. Join them together in that case, like `type|other_type`. */}} {{ $types := slice }} {{ if .oneOf }} {{ range .oneOf }} {{ $types = $types | append (partial "property-type" .) }} {{ end }} {{ else if .anyOf }} {{ range .anyOf }} {{ $types = $types | append (partial "property-type" .) }} {{ end }} {{ else }} {{ range .type }} {{ $types = $types | append (htmlEscape .) }} {{ end }} {{ end }} {{ $type = delimit $types "|" }} {{ else }} {{/* A simple type like integer or boolean */}} {{ $type = (htmlEscape .type) }} {{ end }} {{ return $type }} {{ end }} {{/* Computes the type to display for an object property's schema, given: * `type`: string equal to "object" * `title`: optional string for the title of the object property * `properties`: optional dictionary for well-defined properties, each given as: `property_name` : `property_data` * `additionalProperties`: optional dictionary for properties with undefined names * `patternProperties`: optional dictionary for properties with names adhering to a regex pattern * `anchor`: optional HTML element id for the target type, which will be used to link to it. */}} {{ define "partials/object-type-or-title" }} {{ $type := "object" }} {{ if .properties }} {{/* The object has its own (regular) properties, so we will make a separate table for it. Refer to it using its title, if it has one. */}} {{ if .title }} {{ $type = .title | htmlEscape }} {{ if .anchor }} {{ $type = printf "%s" (htmlEscape .anchor) $type }} {{ end }} {{ end }} {{ else if reflect.IsMap .additionalProperties }} {{/* If the property uses `additionalProperties` to describe its internal structure, handle this with a bit of recursion */}} {{ $type = delimit (slice "{string: " (partial "property-type" .additionalProperties) "}" ) "" }} {{ else if reflect.IsMap .patternProperties }} {{/* If the property uses `patternProperties` to describe its internal structure, handle this with a bit of recursion. 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 partial "string-format" $formatId }} {{ $formatKey = . }} {{ else }} {{ errorf "Unsupported value for `x-pattern-format`: %s" $formatId }} {{ end }} {{ end }} {{ $formatString := printf "{%s: %s}" $formatKey $formatType }} {{ $types = $types | append $formatString }} {{ end }} {{/* Then join all the formats. */}} {{ $type = delimit $types "|" }} {{ else if .title }} {{/* No properties, so there won't be a separate table. We use the title anyway, because showing the title (like `EventFilter`) is better than showing `object`. */}} {{ $type = .title | htmlEscape }} {{ end }} {{ return $type }} {{ end }} {{/* Computes the description to display for a property, given: * `required`: boolean indicating whether this property is required. * `property`: dictionary describing the property's data, with these fields: * `description`: string describing the property * `enum`: optional array indicating the accepted values for the property * `x-addedInMatrixVersion`: optional string indicating in which Matrix spec version this property was added. * `x-changedInMatrixVersion`: optional string indicating in which Matrix spec version this property was last changed. */}} {{ define "partials/property-description" -}} {{ $description := .property.description -}} {{ if .required -}} {{/* Prepend "Required:" to make it part of the first paragraph of the description. */}} {{- $description = printf "Required: %s" $description -}} {{ end -}} {{/* Force the rendering as a block so the description is always inside a paragraph. This allows to always keep the same spacing between paragraphs when adding added-in and changed-in paragraphs. */}} {{ $description | page.RenderString (dict "display" "block") -}} {{ if .property.enum }}

One of: [{{ delimit .property.enum ", " }}].

{{ end -}} {{ if (index .property "x-addedInMatrixVersion") }}{{ partial "added-in" (dict "v" (index .property "x-addedInMatrixVersion")) }}{{ end -}} {{ if (index .property "x-changedInMatrixVersion") }}{{ partial "changed-in" (dict "changes_dict" (index .property "x-changedInMatrixVersion")) }}{{ end -}} {{ end }} {{/* Computes the type to display for a string format, given the identifier of the format as a string. */}} {{ define "partials/string-format" }} {{ $stringFormat := "" }} {{ with index site.Data "string-formats" . }} {{ $stringFormat = printf "%s" (htmlEscape .url | relURL) (htmlEscape .title) }} {{ end }} {{ return $stringFormat }} {{ end }}