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>
This commit is contained in:
parent
073ce659df
commit
e82829d4a2
8 changed files with 56 additions and 94 deletions
1
changelogs/internal/newsfragments/1787.clarification
Normal file
1
changelogs/internal/newsfragments/1787.clarification
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Solve `allOf` recursively in OpenAPI and JSON Schemas.
|
|
@ -20,10 +20,6 @@
|
||||||
* The returned entries are based on the JSON schema definitions found by
|
* The returned entries are based on the JSON schema definitions found by
|
||||||
* recursing through the input `schema`, with the following differences:
|
* 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`
|
* * 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
|
* is given an `anchor`, which is a string suitable for using as an html
|
||||||
* anchor for that object schema.
|
* 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 }}
|
{{ errorf "Invalid call to partials/get-additional-objects: %s is not a map" $name .this_object }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
/* Although we expect resolve-allof to be called on the input, resolve-allof does not recurse into
|
{{ $res := partial "resolve-additional-types-inner" (dict "schema" .this_object "anchor_base" .anchor_base "name" $name) }}
|
||||||
* 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) }}
|
|
||||||
{{ range $res.objects }}
|
{{ range $res.objects }}
|
||||||
{{ $all_objects = $all_objects | append (partial "clean-object" .) }}
|
{{ $all_objects = $all_objects | append (partial "clean-object" .) }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{/*
|
{{/*
|
||||||
|
|
||||||
Resolves the `allOf` keyword (https://spec.openapis.org/oas/v3.1.0#composition-and-inheritance-polymorphism),
|
Resolves the `allOf` keyword (https://spec.openapis.org/oas/v3.1.0#composition-and-inheritance-polymorphism)
|
||||||
given a JSON schema object.
|
recursively, given a JSON schema object.
|
||||||
|
|
||||||
`allOf` is used to support a kind of inheritance for JSON schema objects.
|
`allOf` is used to support a kind of inheritance for JSON schema objects.
|
||||||
|
|
||||||
|
@ -11,36 +11,44 @@
|
||||||
|
|
||||||
Of course the parent can itself inherit from *its* parent, so we recurse to
|
Of course the parent can itself inherit from *its* parent, so we recurse to
|
||||||
handle that.
|
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 := . }}
|
{{ $ret := . }}
|
||||||
{{ $original := . }}
|
{{ $original := . }}
|
||||||
|
|
||||||
|
{{ if reflect.IsSlice $original }}
|
||||||
|
{{/*
|
||||||
|
If it's a slice, just recurse.
|
||||||
|
*/}}
|
||||||
|
{{ $ret = slice }}
|
||||||
|
|
||||||
|
{{ range $original }}
|
||||||
|
{{ $resolved := partial "json-schema/resolve-allof" . }}
|
||||||
|
{{ $ret = $ret | append $resolved }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else if reflect.IsMap $original }}
|
||||||
|
{{ $ret = dict }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
We special-case 'required', and accumulate the values from all the 'allOf'
|
We special-case 'required', and accumulate the values from all the 'allOf'
|
||||||
entries (rather than simply overriding them). Start the accumulation here.
|
entries (rather than simply overriding them). Start the accumulation here.
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $required := .required }}
|
|
||||||
{{ if not $required }}
|
|
||||||
{{ $required := slice }}
|
{{ $required := slice }}
|
||||||
|
{{ with $original.required }}
|
||||||
|
{{ $required = . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ with $ret.allOf }}
|
{{ with $original.allOf }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
construct a new dict, with each of the allOf entries merged into it in
|
Merge each of the allOf entries.
|
||||||
turn.
|
|
||||||
*/}}
|
*/}}
|
||||||
{{ $all_of_values := dict }}
|
|
||||||
{{ range . }}
|
{{ range . }}
|
||||||
{{ with .required }}
|
{{/*
|
||||||
|
First, resolve allOf in child.
|
||||||
|
*/}}
|
||||||
|
{{ $resolved := partial "json-schema/resolve-allof" . }}
|
||||||
|
|
||||||
|
{{ with $resolved.required }}
|
||||||
{{ $required = union $required . }}
|
{{ $required = union $required . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -51,52 +59,23 @@
|
||||||
Note also that `merge` does a *deep* merge - nested maps are also
|
Note also that `merge` does a *deep* merge - nested maps are also
|
||||||
merged. (Slices are replaced though.)
|
merged. (Slices are replaced though.)
|
||||||
*/}}
|
*/}}
|
||||||
{{ $all_of_values = merge $all_of_values . }}
|
{{ $ret = merge $ret $resolved }}
|
||||||
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
Finally, merge in the original, allowing the original to override allOf.
|
Finally, merge in the original, allowing the original to override allOf.
|
||||||
*/}}
|
*/}}
|
||||||
{{ $ret = merge $all_of_values $ret }}
|
{{ range $key, $value := $original }}
|
||||||
|
{{ if and (ne $key "allOf") (ne $key "required") }}
|
||||||
{{/*
|
{{ $resolved := partial "json-schema/resolve-allof" $value }}
|
||||||
Except that if allOf *itself* contains allOf (ie, the parent also
|
{{ $ret = merge $ret (dict $key $resolved) }}
|
||||||
inherits from a grandparent), then we replace allOf in the original
|
{{ end }}
|
||||||
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" . ) }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{/*
|
{{ with $required }}
|
||||||
special-case 'required': replace it with the union of all the
|
{{ $ret = merge $ret (dict "required" .) }}
|
||||||
'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 }}
|
|
||||||
{{ end }}
|
{{ 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 }}
|
{{ end }}
|
||||||
|
|
||||||
{{ return $ret }}
|
{{ return $ret }}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $this_object := partial "json-schema/resolve-allof" . }}
|
{{ $this_object := . }}
|
||||||
|
|
||||||
{{ $example := $this_object.example }}
|
{{ $example := $this_object.example }}
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,6 @@
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
{{ range $property_name, $property := $properties }}
|
{{ range $property_name, $property := $properties }}
|
||||||
|
|
||||||
{{ $property := partial "json-schema/resolve-allof" $property }}
|
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
Handle two ways of indicating "required", one for simple parameters,
|
Handle two ways of indicating "required", one for simple parameters,
|
||||||
the other for request and response body objects.
|
the other for request and response body objects.
|
||||||
|
@ -67,7 +64,7 @@
|
||||||
<th class="col-description">Description</th>
|
<th class="col-description">Description</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
{{ $property := partial "json-schema/resolve-allof" . }}
|
{{ $property := . }}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{{ partial "partials/property-type" $property }}</code></td>
|
<td><code>{{ partial "partials/property-type" $property }}</code></td>
|
||||||
|
@ -111,9 +108,6 @@
|
||||||
like `[type]`.
|
like `[type]`.
|
||||||
*/}}
|
*/}}
|
||||||
{{ $items := .items }}
|
{{ $items := .items }}
|
||||||
{{ if .items }}
|
|
||||||
{{ $items = partial "json-schema/resolve-allof" .items }}
|
|
||||||
{{ end }}
|
|
||||||
{{ $inner_type := partial "property-type" $items }}
|
{{ $inner_type := partial "property-type" $items }}
|
||||||
{{ $type = delimit (slice "[" $inner_type "]") "" }}
|
{{ $type = delimit (slice "[" $inner_type "]") "" }}
|
||||||
{{ else if or (reflect.IsSlice .type) .oneOf }}
|
{{ else if or (reflect.IsSlice .type) .oneOf }}
|
||||||
|
@ -180,8 +174,7 @@
|
||||||
If the property uses `additionalProperties` to describe its
|
If the property uses `additionalProperties` to describe its
|
||||||
internal structure, handle this with a bit of recursion
|
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 }}
|
{{ else if reflect.IsMap .patternProperties }}
|
||||||
{{/*
|
{{/*
|
||||||
If the property uses `patternProperties` to describe its
|
If the property uses `patternProperties` to describe its
|
||||||
|
@ -193,7 +186,6 @@
|
||||||
{{ $types := slice }}
|
{{ $types := slice }}
|
||||||
|
|
||||||
{{ range $pattern, $schema := .patternProperties}}
|
{{ range $pattern, $schema := .patternProperties}}
|
||||||
{{ $schema = partial "json-schema/resolve-allof" $schema }}
|
|
||||||
{{ $types = $types | append (partial "property-type" $schema) }}
|
{{ $types = $types | append (partial "property-type" $schema) }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
{{/*
|
{{/*
|
||||||
Display the JSON schemas
|
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) }}
|
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" $anchor_base) }}
|
||||||
{{ range $additional_types }}
|
{{ range $additional_types }}
|
||||||
|
@ -67,9 +67,7 @@
|
||||||
{{ $example := dict }}
|
{{ $example := dict }}
|
||||||
|
|
||||||
{{ if $body.schema }}
|
{{ if $body.schema }}
|
||||||
{{ $schema := partial "json-schema/resolve-allof" $body.schema }}
|
{{ $example = partial "json-schema/resolve-example" $body.schema }}
|
||||||
|
|
||||||
{{ $example = partial "json-schema/resolve-example" $schema }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if and (eq ($example | len) 0) $body.example }}
|
{{ if and (eq ($example | len) 0) $body.example }}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
Display the JSON schemas
|
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
|
All this is to work out how to express the content of the response
|
||||||
|
|
|
@ -24,5 +24,6 @@
|
||||||
{{ $path := delimit (slice "api" $spec $api) "/" }}
|
{{ $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-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) }}
|
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url) }}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue