{{/* Finds and returns all nested objects, given a dict containing: * `schema`: a JSON schema object * `anchor_base`: a prefix to add to the HTML anchors generated for each object. If nil, no anchors are generated. * `name`: optionally, a name to use for this object in error/warning messages. If left unset, the object's `title` property is used (if present). This template finds all nested objects inside `schema`. Assumes that "resolve-refs" and "resolve-allof" has already been called on the input schema. Returns a pair [$updated_definition, $additional_objects], where: $updated_definition: Largely the same as the provided `schema`, except for the addition of an `anchor` property for any objects we decided to generate anchors for. For objects, this is the same as the first entry in `$additional_objects`. $additional_objects is an array of all the objects found. For each object, the following properties are returned: * title * properties * required * enum * anchor: a string suitable for using as an html anchor for this object (if `anchor_base` was set, and the object has a title) Note that the returned array may contain duplicate objects. */}} {{ $this_object := .schema }} {{ $anchor_base := .anchor_base }} {{ $additional_objects := slice }} {{ $name := .name | default $this_object.title | default "" }} {{ if eq $this_object.type "object" }} {{/* give this object an anchor, if it has a name */}} {{ if (and $anchor_base $this_object.title) }} {{ $this_object = merge $this_object (dict "anchor" (printf "%s_%s" $anchor_base (anchorize $this_object.title))) }} {{ end }} {{/* Add any nested objects referenced in this object's `additionalProperties` */}} {{ if $this_object.additionalProperties }} {{ if reflect.IsMap $this_object.additionalProperties }} {{ $res := partial "get-additional-objects" (dict "this_object" $this_object.additionalProperties "anchor_base" $anchor_base "name" (printf "%s.additional" $name) ) }} {{/* appending an empty slice messes up the types and confuses go, so only do the append if $more_objects is non-empty */}} {{ with $more_objects := index $res 1 }} {{ $additional_objects = $additional_objects | append $more_objects }} {{ end }} {{/* update the object definition with any updates from the additional properties */}} {{ $this_object = merge $this_object (dict "additionalProperties" (index $res 0)) }} {{ end }} {{ end }} {{/* Add any nested objects referenced in this object's `properties` */}} {{ $updated_properties := dict }} {{ range $key, $property := $this_object.properties}} {{ $res := partial "get-additional-objects" (dict "this_object" $property "anchor_base" $anchor_base "name" (printf "%s.%s" $name $key) ) }} {{ with $more_objects := index $res 1 }} {{ $additional_objects = $additional_objects | append $more_objects }} {{ end }} {{ $updated_properties = merge $updated_properties (dict $key (index $res 0)) }} {{ end }} {{/* update the object definition with any updates from the properties */}} {{ $this_object = merge $this_object (dict "properties" $updated_properties) }} {{/* Add the updated object to the front of the $additional_objects array */}} {{ $res := slice (partial "clean-object" $this_object) }} {{ if $additional_objects }} {{ $res = $res | append $additional_objects }} {{ end }} {{ $additional_objects = $res }} {{ end }} {{ if eq $this_object.type "array" }} {{/* Add any nested objects referenced in this object's `items` */}} {{ if reflect.IsSlice $this_object.items}} {{ range $idx, $item := $this_object.items }} {{ $res := partial "get-additional-objects" (dict "this_object" $item "anchor_base" $anchor_base "name" (printf "%s.items[%d]" $name $idx) ) }} {{ with $more_objects := index $res 1 }} {{ $additional_objects = $additional_objects | append $more_objects }} {{ end }} {{ end }} {{ else if reflect.IsMap $this_object.items}} {{ $res := partial "get-additional-objects" (dict "this_object" $this_object.items "anchor_base" $anchor_base "name" (printf "%s.items" $name) ) }} {{ with $more_objects := index $res 1 }} {{ $additional_objects = $additional_objects | append $more_objects }} {{ end }} {{ $this_object = merge $this_object (dict "items" (index $res 0)) }} {{ else }} {{ errorf "%s is defined as an 'array' but lacks a valid 'items'" $name }} {{ end }} {{ end }} {{ return slice $this_object $additional_objects }} {{/* This actually makes the recursive call, and returns the new objects */}} {{ define "partials/get-additional-objects" }} {{/* .name is the name of the object for logging purposes */}} {{ $name := .name }} {{ if not (reflect.IsMap .this_object) }} {{ 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 objects, so we have to call it again. */ {{ $this_object := partial "json-schema/resolve-allof" .this_object }} {{ $more_objects := partial "json-schema/resolve-additional-types" (dict "schema" $this_object "anchor_base" .anchor_base "name" $name) }} {{ return $more_objects }} {{ end }} {{/* Only copy the bits of the object that we actually care about. This is needed for uniqify to work - otherwise objects that are the same 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) }} {{ end }}