diff --git a/changelogs/internal/newsfragments/1798.clarification b/changelogs/internal/newsfragments/1798.clarification new file mode 100644 index 00000000..5bd28a93 --- /dev/null +++ b/changelogs/internal/newsfragments/1798.clarification @@ -0,0 +1 @@ +Show information about "Additional Properties" in object tables. diff --git a/data/api/identity/v2_terms.yaml b/data/api/identity/v2_terms.yaml index d9eea90e..d7c231d4 100644 --- a/data/api/identity/v2_terms.yaml +++ b/data/api/identity/v2_terms.yaml @@ -52,7 +52,6 @@ paths: might be and could be "alpha", semantically versioned, or arbitrary. required: - version - # TODO: TravisR - Make this render additionalProperties: type: object title: Internationalised Policy diff --git a/layouts/partials/json-schema/resolve-additional-types.html b/layouts/partials/json-schema/resolve-additional-types.html index cf5e9953..f6f09646 100644 --- a/layouts/partials/json-schema/resolve-additional-types.html +++ b/layouts/partials/json-schema/resolve-additional-types.html @@ -29,6 +29,8 @@ * * * title * * properties + * * additionalProperties + * * patternProperties * * required * * enum * * anchor @@ -45,27 +47,31 @@ "schema" .schema "anchor_base" .anchor_base "name" (.name | default .schema.title | default "") + "top_level" true ) }} {{ return $res.objects }} /* * A helper for the resolve-additional-types partial. * - * Takes the same inputs as resolve-additional-types itself, except that `name` is mandatory. + * Takes the same inputs as resolve-additional-types itself, except: + * * `name` is mandatory. + * * `top_level`, if set, indicates that this is the top-level object. * * Returns a dict containing: * * * `objects`: The array of object schema definitions. * - * * `schema`: An updated copy of the `schema` input (eg, nested `allOf` - * entries are expanded, and an `anchor` may be added). If the input - * `schema` was itself an object, this will be the same as the first entry - * in `objects`. + * * `schema`: An updated copy of the `schema` input (eg, an `anchor` may be added). + * If the input `schema` was itself an object that we should create a table for, + * this will be the same as the first entry in `objects`. */ {{ define "partials/resolve-additional-types-inner" }} {{ $this_object := .schema }} {{ $anchor_base := .anchor_base }} {{ $name := .name }} + {{ $top_level := .top_level }} + {{ $all_objects := slice }} {{ if eq $this_object.type "object" }} @@ -126,12 +132,24 @@ {{ $this_object = merge $this_object (dict "properties" $updated_properties) }} {{ end }} - /* Finally, prepend the updated schema for the top-level object onto the $all_objects array */ - {{ $tmp := slice $this_object }} - {{ if $all_objects }} - {{ $tmp = $tmp | append $all_objects }} + /* We'll want to create a table for `$this_object` itself if either: + * + * - The object has some regular properties (not just patternProperties or additionalProperties), or: + * - It is the top-level object. (We use a special format of table for top-level objects that + * only have patternProperties or additionalProperties) + * + * In those cases, prepend the updated schema for the top-level object onto the $all_objects array. + * + * We think about this last so that we can take advantage of any updates to the schema that happened + * above (in particular, addition of `anchor` attributes). + */ + {{ if or $this_object.properties $top_level }} + {{ $tmp := slice $this_object }} + {{ if $all_objects }} + {{ $tmp = $tmp | append $all_objects }} + {{ end }} + {{ $all_objects = $tmp }} {{ end }} - {{ $all_objects = $tmp }} {{ end }} {{ if eq $this_object.type "array" }} @@ -198,8 +216,7 @@ * * Returns a dict containing: * * `objects`: The array of object schema definitions. - * * `schema`: An updated copy of the `schema` input (eg, nested `allOf` - * entries are expanded, and an `anchor` may be added). + * * `schema`: An updated copy of the `schema` input (eg, an `anchor` may be added). */ {{ define "partials/get-additional-objects" }} /* .name is the name of the object for logging purposes */ @@ -211,7 +228,7 @@ {{ errorf "Invalid call to partials/get-additional-objects: %s is not a map" $name .this_object }} {{ end }} - {{ $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 "top_level" false) }} {{ range $res.objects }} {{ $all_objects = $all_objects | append (partial "clean-object" .) }} {{ end }} @@ -226,5 +243,5 @@ * but with (for example) different examples will be considered different. */ {{ define "partials/clean-object" }} - {{ return (dict "title" .title "properties" .properties "required" .required "enum" .enum "anchor" .anchor) }} + {{ return (dict "title" .title "properties" .properties "additionalProperties" .additionalProperties "patternProperties" .patternProperties "required" .required "enum" .enum "anchor" .anchor) }} {{ end }} diff --git a/layouts/partials/openapi/render-object-table.html b/layouts/partials/openapi/render-object-table.html index 21d77c89..72ea5a8a 100644 --- a/layouts/partials/openapi/render-object-table.html +++ b/layouts/partials/openapi/render-object-table.html @@ -9,11 +9,11 @@ * `properties`: optional dictionary of the properties to list, each given as: `property_name` : `property_data` - * `additionalProperties`: optional dictionary for properties with undefined - names, in the same format as `property_data` + * `additionalProperties`: a JSON Schema for additional properties on the + object. * `patternProperties`: optional dictionary for properties with names adhering - to a regex pattern, in the same format as `property_data` + 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 @@ -26,7 +26,6 @@ {{ $required := .required}} {{ if $properties }} - {{ with $title }} {{ . }} @@ -52,9 +51,40 @@ {{ 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 }} {{ . }} @@ -116,7 +146,7 @@ {{ else if or (reflect.IsSlice .type) .oneOf }} {{/* It's legal to specify an array of types. - + There are two ways to do that: - Use an array of strings. - Use oneOf, with items having a schema.