339 lines
14 KiB
Cheetah
339 lines
14 KiB
Cheetah
{{- /* Determine if Path for requested GVR is at /api or /apis based on emptiness of group */ -}}
|
|
{{- $prefix := (ternary "/api" (join "" "/apis/" $.GVR.Group) (not $.GVR.Group)) -}}
|
|
|
|
{{- /* Search both cluster-scoped and namespaced-scoped paths for the GVR to find its GVK */ -}}
|
|
{{- /* Also search for paths with {name} component in case the list path is missing */ -}}
|
|
{{- /* Looks for the following paths: */ -}}
|
|
{{- /* /apis/<group>/<version>/<resource> */ -}}
|
|
{{- /* /apis/<group>/<version>/<resource>/{name} */ -}}
|
|
{{- /* /apis/<group>/<version>/namespaces/{namespace}/<resource> */ -}}
|
|
{{- /* /apis/<group>/<version>/namespaces/{namespace}/<resource>/{name} */ -}}
|
|
{{- /* Also search for get verb paths in case list verb is missing */ -}}
|
|
{{- $clusterScopedSearchPath := join "/" $prefix $.GVR.Version $.GVR.Resource -}}
|
|
{{- $clusterScopedNameSearchPath := join "/" $prefix $.GVR.Version $.GVR.Resource "{name}" -}}
|
|
{{- $namespaceScopedSearchPath := join "/" $prefix $.GVR.Version "namespaces" "{namespace}" $.GVR.Resource -}}
|
|
{{- $namespaceScopedNameSearchPath := join "/" $prefix $.GVR.Version "namespaces" "{namespace}" $.GVR.Resource "{name}" -}}
|
|
{{- $gvk := "" -}}
|
|
|
|
{{- /* Pull GVK from operation */ -}}
|
|
{{- range $index, $searchPath := (list $clusterScopedSearchPath $clusterScopedNameSearchPath $namespaceScopedSearchPath $namespaceScopedNameSearchPath) -}}
|
|
{{- with $resourcePathElement := index $.Document "paths" $searchPath -}}
|
|
{{- range $methodIndex, $method := (list "get" "post" "put" "patch" "delete") -}}
|
|
{{- with $resourceMethodPathElement := index $resourcePathElement $method -}}
|
|
{{- with $gvk = index $resourceMethodPathElement "x-kubernetes-group-version-kind" -}}
|
|
{{- break -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- with $gvk -}}
|
|
{{- if $gvk.group -}}
|
|
GROUP: {{ $gvk.group }}{{"\n" -}}
|
|
{{- end -}}
|
|
KIND: {{ $gvk.kind}}{{"\n" -}}
|
|
VERSION: {{ $gvk.version }}{{"\n" -}}
|
|
{{- "\n" -}}
|
|
|
|
{{- with include "schema" (dict "gvk" $gvk "Document" $.Document "FieldPath" $.FieldPath "Recursive" $.Recursive) -}}
|
|
{{- . -}}
|
|
{{- else -}}
|
|
{{- throw "error: GVK %v not found in OpenAPI schema" $gvk -}}
|
|
{{- end -}}
|
|
{{- else -}}
|
|
{{- throw "error: GVR (%v) not found in OpenAPI schema" $.GVR.String -}}
|
|
{{- end -}}
|
|
{{- "\n" -}}
|
|
|
|
{{- /*
|
|
Finds a schema with the given GVK and prints its explain output or empty string
|
|
if GVK was not found
|
|
|
|
Takes dictionary as argument with keys:
|
|
gvk: openapiv3 JSON schema
|
|
Document: entire doc
|
|
FieldPath: field path to follow
|
|
Recursive: print recursive
|
|
*/ -}}
|
|
{{- define "schema" -}}
|
|
{{- /* Find definition with this GVK by filtering out the components/schema with the given x-kubernetes-group-version-kind */ -}}
|
|
{{- range index $.Document "components" "schemas" -}}
|
|
{{- if contains (index . "x-kubernetes-group-version-kind") $.gvk -}}
|
|
{{- with include "output" (set $ "schema" .) -}}
|
|
{{- . -}}
|
|
{{- else -}}
|
|
{{- $fieldName := (index $.FieldPath (sub (len $.FieldPath) 1)) -}}
|
|
{{- throw "error: field \"%v\" does not exist" $fieldName}}
|
|
{{- end -}}
|
|
{{- break -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- /*
|
|
Follows FieldPath until the FieldPath is empty. Then prints field name and field
|
|
list of resultant schema. If field path is not found. Prints nothing.
|
|
Example output:
|
|
|
|
FIELD: spec
|
|
|
|
DESCRIPTION:
|
|
<template "description">
|
|
|
|
FIELDS:
|
|
<template "fieldList">
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema
|
|
history: map[string]int
|
|
Document: entire doc
|
|
FieldPath: field path to follow
|
|
Recursive: print recursive
|
|
*/ -}}
|
|
{{- define "output" -}}
|
|
{{- $refString := or (index $.schema "$ref") "" -}}
|
|
{{- $nextContext := set $ "history" (set $.history $refString 1) -}}
|
|
{{- $resolved := or (resolveRef $refString $.Document) $.schema -}}
|
|
{{- if not $.FieldPath -}}
|
|
DESCRIPTION:{{- "\n" -}}
|
|
{{- or (include "description" (dict "schema" $resolved "Document" $.Document)) "<empty>" | wrap 76 | indent 4 -}}{{- "\n" -}}
|
|
{{- with include "fieldList" (dict "schema" $resolved "level" 1 "Document" $.Document "Recursive" $.Recursive) -}}
|
|
FIELDS:{{- "\n" -}}
|
|
{{- . -}}
|
|
{{- end -}}
|
|
{{- else if and $refString (index $.history $refString) -}}
|
|
{{- /* Stop and do nothing. Hit a cycle */ -}}
|
|
{{- else if and $resolved.properties (index $resolved.properties (first $.FieldPath)) -}}
|
|
{{- /* Schema has this property directly. Traverse to next schema */ -}}
|
|
{{- $subschema := index $resolved.properties (first $.FieldPath) -}}
|
|
{{- if eq 1 (len $.FieldPath) -}}
|
|
{{- /* TODO: The original explain would say RESOURCE instead of FIELD here under some circumstances */ -}}
|
|
FIELD: {{first $.FieldPath}} <{{ template "typeGuess" (dict "schema" $subschema "Document" $.Document) }}>{{"\n"}}
|
|
{{- template "extractEnum" (dict "schema" $subschema "Document" $.Document "isLongView" true "limit" -1) -}}{{"\n"}}
|
|
{{- "\n" -}}
|
|
{{- end -}}
|
|
|
|
{{- template "output" (set $nextContext "history" (dict) "FieldPath" (slice $.FieldPath 1) "schema" $subschema ) -}}
|
|
{{- else if $resolved.items -}}
|
|
{{- /* If the schema is an array then traverse the array item fields */ -}}
|
|
{{- template "output" (set $nextContext "schema" $resolved.items) -}}
|
|
{{- else if $resolved.additionalProperties -}}
|
|
{{- /* If the schema is a map then traverse the map item fields */ -}}
|
|
{{- template "output" (set $nextContext "schema" $resolved.additionalProperties) -}}
|
|
{{- else -}}
|
|
{{- /* Last thing to try is all the alternatives in the allOf case */ -}}
|
|
{{- /* Stop when one of the alternatives has an output at all */ -}}
|
|
{{- range $index, $subschema := $resolved.allOf -}}
|
|
{{- with include "output" (set $nextContext "schema" $subschema) -}}
|
|
{{- . -}}
|
|
{{- break -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- /*
|
|
Prints list of fields of a given open api schema in following form:
|
|
|
|
field1 <type> -required-
|
|
DESCRIPTION
|
|
|
|
field2 <type> -required-
|
|
DESCRIPTION
|
|
|
|
or if Recursive is enabled:
|
|
field1 <type> -required-
|
|
subfield1 <type>
|
|
subsubfield1 <type>
|
|
subsubfield2 <type>
|
|
subfield2 <type>
|
|
field2 <type> -required-
|
|
subfield1 <type>
|
|
subfield2 <type>
|
|
|
|
Follows refs for field traversal. If there are cycles in the schema, they are
|
|
detected and traversal ends.
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema
|
|
level: indentation level
|
|
history: map[string]int containing all ref names so far
|
|
Document: entire doc
|
|
Recursive: print recursive
|
|
*/ -}}
|
|
{{- define "fieldList" -}}
|
|
{{- /* Resolve schema if it is a ref */}}
|
|
{{- /* If this is a ref seen before, then ignore it */}}
|
|
{{- $refString := or (index $.schema "$ref") "" }}
|
|
{{- if and $refString (index (or $.history (dict)) $refString) -}}
|
|
{{- /* Do nothing for cycle */}}
|
|
{{- else -}}
|
|
{{- $nextContext := set $ "history" (set $.history $refString 1) -}}
|
|
{{- $resolved := or (resolveRef $refString $.Document) $.schema -}}
|
|
{{- range $k, $v := $resolved.properties -}}
|
|
{{- template "fieldDetail" (dict "name" $k "schema" $resolved "short" $.Recursive "level" $.level "Document" $.Document) -}}
|
|
{{- if $.Recursive -}}
|
|
{{- /* Check if we already know about this element */}}
|
|
{{- template "fieldList" (set $nextContext "schema" $v "level" (add $.level 1)) -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- range $resolved.allOf -}}
|
|
{{- template "fieldList" (set $nextContext "schema" .)}}
|
|
{{- end -}}
|
|
{{- if $resolved.items}}{{- template "fieldList" (set $nextContext "schema" $resolved.items)}}{{end}}
|
|
{{- if $resolved.additionalProperties}}{{- template "fieldList" (set $nextContext "schema" $resolved.additionalProperties)}}{{end}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
|
|
{{- /*
|
|
|
|
Prints a single field of the given schema
|
|
Optionally prints in a one-line style
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema which contains the field
|
|
name: name of field
|
|
short: limit printing to a single-line summary
|
|
level: indentation amount
|
|
Document: openapi document
|
|
*/ -}}
|
|
{{- define "fieldDetail" -}}
|
|
{{- $level := or $.level 0 -}}
|
|
{{- $indentAmount := mul $level 2 -}}
|
|
{{- $fieldSchema := index $.schema.properties $.name -}}
|
|
{{- $.name | indent $indentAmount -}}{{"\t"}}<{{ template "typeGuess" (dict "schema" $fieldSchema "Document" $.Document) }}>
|
|
{{- if contains $.schema.required $.name}} -required-{{- end -}}
|
|
{{- template "extractEnum" (dict "schema" $fieldSchema "Document" $.Document "isLongView" false "limit" 4 "indentAmount" $indentAmount) -}}
|
|
{{- "\n" -}}
|
|
{{- if not $.short -}}
|
|
{{- or $fieldSchema.description "<no description>" | wrap (sub 78 $indentAmount) | indent (add $indentAmount 2) -}}{{- "\n" -}}
|
|
{{- "\n" -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- /*
|
|
|
|
Prints the description of the given OpenAPI v3 schema. Also walks through all
|
|
sibling schemas to the provided schema and prints the description of those schemas
|
|
too
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema
|
|
Document: document to resolve refs within
|
|
*/ -}}
|
|
{{- define "description" -}}
|
|
{{- with or (resolveRef (index $.schema "$ref") $.Document) $.schema -}}
|
|
{{- if .description -}}
|
|
{{- .description -}}
|
|
{{- "\n" -}}
|
|
{{- end -}}
|
|
{{- range .allOf -}}
|
|
{{- template "description" (set $ "schema" .)}}
|
|
{{- end -}}
|
|
{{- if .items -}}
|
|
{{- template "description" (set $ "schema" .items) -}}
|
|
{{- end -}}
|
|
{{- if .additionalProperties -}}
|
|
{{- template "description" (set $ "schema" .additionalProperties) -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- /* Renders a shortstring representing an interpretation of what is the "type"
|
|
of a subschema e.g.:
|
|
|
|
`string` `number`, `Object`, `[]Object`, `map[string]string`, etc.
|
|
|
|
Serves as a more helpful type hint than raw typical openapi `type` field
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema
|
|
Document: openapi document
|
|
*/ -}}
|
|
{{- define "typeGuess" -}}
|
|
{{- with $.schema -}}
|
|
{{- if .items -}}
|
|
[]{{template "typeGuess" (set $ "schema" .items)}}
|
|
{{- else if .additionalProperties -}}
|
|
map[string]{{template "typeGuess" (set $ "schema" .additionalProperties)}}
|
|
{{- else if and .allOf (not .properties) (eq (len .allOf) 1) -}}
|
|
{{- /* If allOf has a single element and there are no direct
|
|
properties on the schema, defer to the allOf */ -}}
|
|
{{- template "typeGuess" (set $ "schema" (first .allOf)) -}}
|
|
{{- else if index . "$ref"}}
|
|
{{- /* Parse the #!/components/schemas/io.k8s.Kind string into just the Kind name */ -}}
|
|
{{- $ref := index . "$ref" -}}
|
|
{{- /* Look up ref schema to see primitive type. Only put the ref type name if it is an object. */ -}}
|
|
{{- $refSchema := resolveRef $ref $.Document -}}
|
|
{{- if (or (not $refSchema) (or (not $refSchema.type) (eq $refSchema.type "object"))) -}}
|
|
{{- $name := split $ref "/" | last -}}
|
|
{{- or (split $name "." | last) "Object" -}}
|
|
{{- else if $refSchema.type -}}
|
|
{{- or $refSchema.type "Object" -}}
|
|
{{- else -}}
|
|
{{- or .type "Object" -}}
|
|
{{- end -}}
|
|
{{- else -}}
|
|
{{/* Old explain used capitalized "Object". Just follow suit */}}
|
|
{{- if eq .type "object" -}}Object
|
|
{{- else -}}{{- or .type "Object" -}}{{- end -}}
|
|
{{- end -}}
|
|
{{- else -}}
|
|
{{- fail "expected schema argument to subtemplate 'typeguess'" -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
|
|
{{- /* Check if there is any enum returns it in this format e.g.:
|
|
|
|
ENUM: "", BlockDevice, CharDevice, Directory
|
|
enum: "", BlockDevice, CharDevice, Directory
|
|
|
|
Can change the style of enum in future by modifying this function
|
|
|
|
Takes dictionary as argument with keys:
|
|
schema: openapiv3 JSON schema
|
|
Document: openapi document
|
|
isLongView: (boolean) Simple view: long list of all fields. Long view: all details of one field
|
|
limit: (int) truncate the amount of enums that can be printed in simple view, -1 means all items
|
|
indentAmount: intent of the beginning enum line in longform view
|
|
*/ -}}
|
|
{{- define "extractEnum" -}}
|
|
{{- with $.schema -}}
|
|
{{- if .enum -}}
|
|
{{- $enumLen := len .enum -}}
|
|
{{- $limit := or $.limit -1 -}}
|
|
{{- if eq $.isLongView false -}}
|
|
{{- "\n" -}}
|
|
{{- "" | indent $.indentAmount -}}
|
|
{{- "enum: " -}}
|
|
{{- else -}}
|
|
{{- "ENUM:" -}}
|
|
{{- end -}}
|
|
{{- range $index, $element := .enum -}}
|
|
{{- /* Prints , .... and return the range when it goes over the limit */ -}}
|
|
{{- if and (gt $limit -1) (ge $index $limit) -}}
|
|
{{- ", ...." -}}
|
|
{{- break -}}
|
|
{{- end -}}
|
|
{{- /* Use to reflect "" when we see empty string */ -}}
|
|
{{- $elementType := printf "%T" $element -}}
|
|
{{- /* Print out either `, ` or `\n ` based of the view */ -}}
|
|
{{- /* Simple view */ -}}
|
|
{{- if and (gt $index 0) (eq $.isLongView false) -}}
|
|
{{- ", " -}}
|
|
{{- /* Long view */ -}}
|
|
{{- else if eq $.isLongView true -}}
|
|
{{- "\n" -}}{{- "" | indent 4 -}}
|
|
{{- end -}}
|
|
{{- /* Convert empty string to `""` for more clarification */ -}}
|
|
{{- if and (eq "string" $elementType) (eq $element "") -}}
|
|
{{- `""` -}}
|
|
{{- else -}}
|
|
{{- $element -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}} |