Introduce experimental OpenAPI shortcode

This commit is contained in:
Alex Shpak
2026-05-22 14:06:48 +02:00
parent 118c5354bb
commit 1d38cd0183
8 changed files with 683 additions and 35 deletions
+261
View File
@@ -0,0 +1,261 @@
{
"openapi": "3.1.0",
"info": {
"title": "Tic Tac Toe",
"description": "This API allows writing down marks on a Tic Tac Toe board\nand requesting the state of the board or of individual squares.\n",
"version": "1.0.0"
},
"tags": [
{
"name": "Gameplay"
}
],
"paths": {
"/board": {
"get": {
"summary": "Get the whole board",
"description": "Retrieves the current state of the board and the winner.",
"tags": ["Gameplay"],
"operationId": "get-board",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/status"
}
}
}
}
},
"security": [
{
"defaultApiKey": []
},
{
"app2AppOauth": ["board:read"]
}
]
}
},
"/board/{row}/{column}": {
"parameters": [
{
"$ref": "#/components/parameters/rowParam"
},
{
"$ref": "#/components/parameters/columnParam"
}
],
"get": {
"summary": "Get a single board square",
"description": "Retrieves the requested square.",
"tags": ["Gameplay"],
"operationId": "get-square",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/mark"
}
}
}
},
"400": {
"description": "The provided parameters are incorrect",
"content": {
"text/html": {
"schema": {
"$ref": "#/components/schemas/errorMessage"
},
"example": "Illegal coordinates"
}
}
}
},
"security": [
{
"bearerHttpAuthentication": []
},
{
"user2AppOauth": ["board:read"]
}
]
},
"put": {
"summary": "Set a single board square",
"description": "Places a mark on the board and retrieves the whole board and the winner (if any).",
"tags": ["Gameplay"],
"operationId": "put-square",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/mark"
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/status"
}
}
}
},
"400": {
"description": "The provided parameters are incorrect",
"content": {
"text/html": {
"schema": {
"$ref": "#/components/schemas/errorMessage"
},
"examples": {
"illegalCoordinates": {
"value": "Illegal coordinates."
},
"notEmpty": {
"value": "Square is not empty."
},
"invalidMark": {
"value": "Invalid Mark (X or O)."
}
}
}
}
}
},
"security": [
{
"bearerHttpAuthentication": []
},
{
"user2AppOauth": ["board:write"]
}
]
}
}
},
"components": {
"parameters": {
"rowParam": {
"description": "Board row (vertical coordinate)",
"name": "row",
"in": "path",
"required": true,
"schema": {
"$ref": "#/components/schemas/coordinate"
}
},
"columnParam": {
"description": "Board column (horizontal coordinate)",
"name": "column",
"in": "path",
"required": true,
"schema": {
"$ref": "#/components/schemas/coordinate"
}
}
},
"schemas": {
"errorMessage": {
"type": "string",
"maxLength": 256,
"description": "A text message describing an error"
},
"coordinate": {
"type": "integer",
"minimum": 1,
"maximum": 3,
"example": 1
},
"mark": {
"type": "string",
"enum": [".", "X", "O"],
"description": "Possible values for a board square. `.` means empty square.",
"example": "."
},
"board": {
"type": "array",
"maxItems": 3,
"minItems": 3,
"items": {
"type": "array",
"maxItems": 3,
"minItems": 3,
"items": {
"$ref": "#/components/schemas/mark"
}
}
},
"winner": {
"type": "string",
"enum": [".", "X", "O"],
"description": "Winner of the game. `.` means nobody has won yet.",
"example": "."
},
"status": {
"type": "object",
"properties": {
"winner": {
"$ref": "#/components/schemas/winner"
},
"board": {
"$ref": "#/components/schemas/board"
}
}
}
},
"securitySchemes": {
"defaultApiKey": {
"description": "API key provided in console",
"type": "apiKey",
"name": "api-key",
"in": "header"
},
"basicHttpAuthentication": {
"description": "Basic HTTP Authentication",
"type": "http",
"scheme": "Basic"
},
"bearerHttpAuthentication": {
"description": "Bearer token using a JWT",
"type": "http",
"scheme": "Bearer",
"bearerFormat": "JWT"
},
"app2AppOauth": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "https://learn.openapis.org/oauth/2.0/token",
"scopes": {
"board:read": "Read the board"
}
}
}
},
"user2AppOauth": {
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://learn.openapis.org/oauth/2.0/auth",
"tokenUrl": "https://learn.openapis.org/oauth/2.0/token",
"scopes": {
"board:read": "Read the board",
"board:write": "Write to the board"
}
}
}
}
}
}
}
+11 -31
View File
@@ -5,62 +5,42 @@ layout: landing
<br />
# Hugo Book {anchor=false}
{{% columns %}}
- Documentation theme for [Hugo](https://gohugo.io) as simple as plain book.
Documentation theme for [Hugo](https://gohugo.io) as simple as plain book.
Hugo-Book theme is designed to be easy to read, write, navigate and maintain.
It is an attempt to create a sustainable web project.
{{<button href="/docs/getting-started/introduction">}}Explore{{</button>}}
- ```shell
# TL;DR
git clone https://github.com/alex-shpak/hugo-book-starter documentation
cd documentation
git submodule update --init --remote
hugo server --minify
```
{{% /columns %}}
<br />
<br />
{{% columns %}}
- {{< card >}}
### Probably fast {anchor=false}
- ### Probably fast {anchor=false}
Built on Hugo static site generator. "The worlds fastest framework for building websites".
{{< /card >}}
- {{< card >}}
### 50% JS free {anchor=false}
- ### 50% JS free {anchor=false}
All important features are working even with JavaScript disabled in the browser, including interactive shortcodes.
{{< /card >}}
- {{< card >}}
### Minimalistic {anchor=false}
- ### Minimalistic {anchor=false}
For real, it is very minimalistic, black on white. No extra tools are needed to build the site, only Hugo. No heavy JS or CSS frameworks included.
{{< /card >}}
{{% /columns %}}
{{% columns %}}
- {{< card >}}
### Shortcodes {anchor=false}
- ### Shortcodes {anchor=false}
Pretty good shortcodes are included to enhance content:
[Columns](/docs/content/shortcodes/columns/),
[Cards](/docs/content/shortcodes/cards/),
[Cards](/docs/content/shortcodes/experimental/cards/),
[Tabs](/docs/content/shortcodes/tabs/),
[Images](/docs/content/shortcodes/images/),
[Images](/docs/content/shortcodes/experimental/images/),
[Asciinema](/docs/content/shortcodes/asciinema/),
[KaTex](/docs/content/shortcodes/katex/),
[Mermaid](/docs/content/shortcodes/mermaid/) and others.
{{< /card >}}
- {{< card >}}
### Even more {anchor=false}
- ### Even more {anchor=false}
Do people actually read these? I thought it was just a visual filler. But there is full-text search and multi-language support.
{{< /card >}}
- {{< card >}}
### Made to be extendable {anchor=false}
- ### Made to be extendable {anchor=false}
There are multiple points to inject own styles and templates to make it your own.
{{< /card >}}
{{% /columns %}}
@@ -0,0 +1,3 @@
---
bookCollapseSection: true
---
@@ -1,6 +1,6 @@
# Cards
Content blocks with optional images and links. Often used with [Columns](/docs/content/shortcodes/columns) for grid layouts.
Content blocks with optional images and links. Often used with [Columns](/docs/content/shortcodes/columns/) for grid layouts.
## Syntax
@@ -18,7 +18,7 @@ Markdown content
Cards can display an image above the content.
{{< /card >}}
- {{< card href="/docs/content/shortcodes/cards" >}}
- {{< card href="/docs/content/shortcodes/experimental/cards/" >}}
### With Link {anchor=false}
When `href` is set, the entire card becomes clickable.
{{< /card >}}
@@ -0,0 +1,19 @@
---
title: OpenAPI
---
# OpenAPI
Renders an OpenAPI 3.x specification as a documentation page. Operations are grouped by their first tag.
## Syntax
```tpl
{{%/* openapi src="spec.json" */%}}
```
The spec file is loaded via `resources.Get`, so the path is relative to the site's `assets/` directory. JSON and YAML are both supported.
## Example
{{% openapi src="tictactoe.json" %}}
+385
View File
@@ -0,0 +1,385 @@
{{- $source := or (.Get "src") (.Get 0) -}}
{{- with resources.Get $source -}}
{{- $raw := . -}}
{{- if or (strings.HasSuffix $source ".yaml") (strings.HasSuffix $source ".yml") -}}
{{- $raw = resources.FromString (printf "%s.json" $source) (. | transform.Unmarshal | jsonify) -}}
{{- end -}}
{{- $spec := openapi3.Unmarshal $raw -}}
{{ with $spec.Info }}{{ template "openapi-info" (dict "info" . "externalDocs" $spec.ExternalDocs) }}{{ end }}
{{ with $spec.Servers }}{{ template "openapi-servers" (dict "servers" .) }}{{ end }}
{{ with $spec.Components }}{{ with .SecuritySchemes }}{{ template "openapi-security" (dict "schemes" .) }}{{ end }}{{ end }}
{{ template "openapi-operations" (dict "spec" $spec) }}
{{ with $spec.Components }}{{ with .Schemas }}{{ template "openapi-models" (dict "schemas" .) }}{{ end }}{{ end }}
{{- else -}}
{{- errorf "openapi shortcode: resource %q not found" $source -}}
{{- end -}}
{{- /* ========== Info ========== */ -}}
{{- define "openapi-info" -}}
{{- $info := .info -}}
{{- $externalDocs := .externalDocs }}
{{ with $info.Description }}{{ . }}{{ end }}
{{ $meta := slice -}}
{{- with $info.Version }}{{ $meta = $meta | append (printf "`v%s`" .) }}{{ end -}}
{{- with $info.License }}{{ if .URL }}{{ $meta = $meta | append (printf "[%s](%s)" .Name .URL) }}{{ else }}{{ $meta = $meta | append .Name }}{{ end }}{{ end -}}
{{- with $info.Contact }}{{ with .Email }}{{ $meta = $meta | append (printf "[%s](mailto:%s)" . .) }}{{ end }}{{ end -}}
{{- with $info.TermsOfService }}{{ $meta = $meta | append (printf "[Terms of service](%s)" .) }}{{ end -}}
{{- with $externalDocs }}{{ $meta = $meta | append (printf "[%s](%s)" (or .Description .URL) .URL) }}{{ end }}
{{ delimit $meta " \n" | safeHTML }}
{{- end -}}
{{- /* ========== Servers ========== */ -}}
{{- define "openapi-servers" }}
## Servers
{{ range .servers }}
{{ with .Description }}{{ . | chomp | replaceRE "\n" " " }}{{ else }}Server{{ end }}
: [`{{ .URL }}`]({{ .URL }})
{{- range $name, $var := .Variables }}
: `{{ $name }}`: default `{{ $var.Default }}`{{ with $var.Enum }}, enum: {{ delimit . ", " }}{{ end }}{{ with $var.Description }}, {{ . | chomp | replaceRE "\n" " " }}{{ end }}
{{- end }}
{{ end -}}
{{- end -}}
{{- /* ========== Security ========== */ -}}
{{- define "openapi-security" -}}
{{- $schemes := .schemes }}
## Security
{{ range $name, $schemeRef := $schemes }}
{{- with $schemeRef.Value }}
{{ with .Description }}{{ . | chomp | replaceRE "\n" " " }}
: `{{ $name }}`
{{ else }}`{{ $name }}`
{{ end -}}
: `{{ .Type }}`{{ with .Scheme }} `{{ . }}`{{ end }}{{ with .In }} in `{{ . }}`{{ end }}{{ with .Name }} (`{{ . }}`){{ end }}{{ with .BearerFormat }} `{{ . }}`{{ end }}{{ with .OpenIdConnectUrl }}, discovery: [{{ . }}]({{ . }}){{ end }}
{{- end }}
{{ end -}}
{{- end -}}
{{- /* ========== Operations (grouped by tag) ========== */ -}}
{{- define "openapi-operations" -}}
{{- $spec := .spec -}}
{{- $methods := slice "GET" "POST" "PUT" "PATCH" "DELETE" "HEAD" "OPTIONS" "TRACE" -}}
{{- $locations := slice
(dict "in" "path" "label" "Path parameters")
(dict "in" "query" "label" "Query parameters")
(dict "in" "header" "label" "Header parameters")
(dict "in" "cookie" "label" "Cookie parameters") -}}
{{- /* Bucket each operation under every tag it declares */ -}}
{{- $byTag := dict -}}
{{- range $path, $pathItem := $spec.Paths.Map -}}
{{- range $method := $methods -}}
{{- with index $pathItem.Operations $method -}}
{{- $tags := .Tags -}}
{{- if not $tags }}{{ $tags = slice "API" }}{{ end -}}
{{- $entry := dict "path" $path "method" $method "operation" . "pathItem" $pathItem -}}
{{- range $tag := $tags -}}
{{- $bucket := index $byTag $tag | default slice -}}
{{- $byTag = merge $byTag (dict $tag ($bucket | append $entry)) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end }}
{{ range $tag, $entries := $byTag }}
## {{ $tag | title }}
{{ range $entry := $entries }}
{{ template "openapi-operation" (dict "spec" $spec "path" $entry.path "method" $entry.method "operation" $entry.operation "pathItem" $entry.pathItem "locations" $locations) }}
{{- end -}}
{{- end }}
{{- end -}}
{{- /* ========== Operation ========== */ -}}
{{- define "openapi-operation" -}}
{{- $spec := .spec -}}
{{- $path := .path -}}
{{- $method := .method -}}
{{- $operation := .operation -}}
{{- $pathItem := .pathItem -}}
{{- $locations := .locations }}
{{- $title := or $operation.Summary (printf "`%s %s`" (upper $method) $path) -}}
{{- if $operation.Deprecated }}{{ $title = printf "~~%s~~" $title }}{{ end }}
### {{ $title }}{{ with $operation.OperationID }} {id="op-{{ anchorize . }}"}{{ end }}
{{ with $operation.Description }}{{ . }}
{{ end -}}
{{- with $operation.ExternalDocs }}[{{ with .Description }}{{ . | chomp | replaceRE "\n" " " }}{{ else }}{{ .URL }}{{ end }}]({{ .URL }})
{{ end -}}
{{- $security := $operation.Security -}}
{{- if not $security }}{{ $security = $spec.Security }}{{ end }}
{{ $requirementStrings := slice -}}
{{- range $requirement := $security -}}
{{- $schemeLinks := slice -}}
{{- range $schemeName, $_ := $requirement -}}
{{- $schemeLinks = $schemeLinks | append (printf "[%s](#security)" $schemeName) -}}
{{- end -}}
{{- if $schemeLinks }}{{ $requirementStrings = $requirementStrings | append (delimit $schemeLinks " and ") }}{{ end -}}
{{- end }}
#### Request{{ if $requirementStrings }} (requires {{ delimit $requirementStrings ", " }}){{ end }}
```tpl
{{ upper $method }} {{ $path }}
```
{{- /* Dedup: operation-level overrides path-level by name+in */ -}}
{{- $opKeys := dict -}}
{{- range $operation.Parameters }}{{ with .Value }}{{ $opKeys = merge $opKeys (dict (printf "%s:%s" .In .Name) true) }}{{ end }}{{ end -}}
{{- $allParams := slice -}}
{{- range $pp := $pathItem.Parameters }}{{ with $pp.Value }}{{ if not (isset $opKeys (printf "%s:%s" .In .Name)) }}{{ $allParams = $allParams | append $pp }}{{ end }}{{ end }}{{ end -}}
{{- $allParams = append $allParams $operation.Parameters -}}
{{ template "openapi-parameters" (dict "allParams" $allParams "locations" $locations) }}
{{- with $operation.RequestBody }}{{ with .Value -}}
{{ template "openapi-request-body" (dict "requestBody" .) }}
{{- end }}{{ end }}
{{- with $operation.Responses -}}
{{ template "openapi-responses" (dict "responses" .) }}
{{- end -}}
{{- end -}}
{{- /* ========== Parameters ========== */ -}}
{{- define "openapi-parameters" -}}
{{- $allParams := .allParams -}}
{{- range .locations -}}
{{- $location := .in -}}
{{- $params := slice -}}
{{- range $allParams -}}
{{- with .Value }}{{ if eq .In $location }}{{ $params = $params | append . }}{{ end }}{{ end -}}
{{- end }}
{{- if $params }}
#### {{ .label }}
| Parameter name | Value | Description | Additional |
|----------------|-------|-------------|------------|
{{- range $params }}
| `{{ .Name }}` | {{ with .Schema }}{{ template "openapi-type" . }}{{ end }} | {{ with .Description }}{{ . | chomp | replaceRE "\n" " " }}{{ end }} | {{ template "openapi-param-extras" . }} |
{{- end }}
{{- end -}}
{{- end -}}
{{- end -}}
{{- define "openapi-param-extras" -}}
{{- $bits := slice -}}
{{- if .Required }}{{ $bits = $bits | append "Required" }}{{ end -}}
{{- if .Deprecated }}{{ $bits = $bits | append "Deprecated" }}{{ end -}}
{{- $example := .Example -}}
{{- $default := "" -}}
{{- with .Schema }}{{ with .Value -}}
{{- with .Default }}{{ $default = . }}{{ end -}}
{{- if not $example }}{{ with .Example }}{{ $example = . }}{{ end }}{{ end -}}
{{- end }}{{ end -}}
{{- with $default }}{{ $bits = $bits | append (printf "Default: `%v`" .) }}{{ end -}}
{{- with $example }}{{ $bits = $bits | append (printf "Example: `%v`" .) }}{{ end -}}
{{- delimit $bits ", " -}}
{{- end -}}
{{- /* ========== Request body ========== */ -}}
{{- define "openapi-request-body" -}}
{{- with .requestBody -}}
{{- if or .Description .Content }}
#### Request body{{ if .Required }} (required){{ end }}
{{ with .Description }}{{ . }}{{ end }}
{{ range $mediaType, $obj := .Content }}
{{ template "openapi-media-examples" (dict "mediaType" $mediaType "examples" $obj.Examples "example" $obj.Example) }}
{{- end -}}
{{ template "openapi-schema-summary" (dict "content" .Content) }}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* ========== Responses ========== */ -}}
{{- define "openapi-responses" -}}
{{- $responses := .responses -}}
{{- with $responses }}
#### Responses
{{ range $code, $codeRef := .Map -}}
{{- with $codeRef.Value }}
`{{ $code }}`{{ with .Description }}: {{ . | chomp | replaceRE "\n" " " }}{{ end }}
{{ with .Content }}{{ range $mediaType, $obj := . }}
{{ template "openapi-media-examples" (dict "mediaType" $mediaType "examples" $obj.Examples "example" $obj.Example) }}
{{- end -}}
{{ template "openapi-schema-summary" (dict "content" .) }}
{{ end -}}
{{ template "openapi-headers-detail" (dict "code" $code "headers" .Headers) }}
{{- end }}
{{ end -}}
{{- end -}}
{{- end -}}
{{- define "openapi-headers-detail" -}}
{{- $code := .code -}}
{{- with .headers }}
`{{ $code }}` response headers:
| Name | Type | Description |
|------|------|-------------|
{{- range $name, $hdrRef := . }}
{{- with $hdrRef.Value }}
| `{{ $name }}` | {{ with .Schema }}{{ template "openapi-type" . }}{{ end }} | {{ with .Description }}{{ . | chomp | replaceRE "\n" " " }}{{ end }} |
{{- end -}}
{{- end }}
{{- end -}}
{{- end -}}
{{- /* ========== Media examples (author-provided only) ========== */ -}}
{{- define "openapi-media-examples" -}}
{{- $mediaType := .mediaType -}}
{{- $examples := .examples -}}
{{- $example := .example -}}
{{- if $examples -}}
{{- range $name, $exRef := $examples }}
{{- with $exRef.Value }}
{{ with .Summary }}{{ . }}{{ else }}{{ $name }}{{ end }}{{ with .Description }} - {{ . | chomp | replaceRE "\n" " " }}{{ end }}
```jsonc {filename="{{ $mediaType }}"}
{{ jsonify (dict "indent" " ") .Value | safeHTML }}
```
{{- end -}}
{{- end -}}
{{- else if $example }}
```jsonc {filename="{{ $mediaType }}"}
{{ jsonify (dict "indent" " ") $example | safeHTML }}
```
{{- end -}}
{{- end -}}
{{- /* ========== Schema summary (type caption + embedded properties table) ========== */ -}}
{{- define "openapi-schema-summary" -}}
{{- $schemaRef := "" -}}
{{- range $mt, $obj := .content -}}
{{- if not $schemaRef }}{{ with $obj.Schema }}{{ $schemaRef = . }}{{ end }}{{ end -}}
{{- end -}}
{{- with $schemaRef }}
Schema: {{ template "openapi-type" . }}
{{- /* Unwrap arrays so `array of Pet` embeds the Pet table */ -}}
{{- $value := $schemaRef.Value -}}
{{- $itemRef := $schemaRef -}}
{{- if and $value (not $value.Properties) $value.Items -}}
{{- $itemRef = $value.Items -}}
{{- end -}}
{{- with $itemRef.Value -}}
{{- if or .Properties .AllOf }}
{{ template "openapi-properties-table" (dict "schema" $itemRef) }}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* ========== Models ========== */ -}}
{{- define "openapi-models" -}}
{{- $schemas := .schemas }}
## Models
{{ range $name, $schemaRef := $schemas -}}
{{- with $schemaRef.Value }}
### {{ $name }} {id="model-{{ $name | anchorize }}"}
{{ with .Description }}{{ . | chomp }}
{{ end -}}
{{- $parents := slice -}}
{{- range .AllOf }}{{ if .Ref }}{{ $parents = $parents | append . }}{{ end }}{{ end -}}
{{- with $parents }}Inherits from {{ range $i, $p := . }}{{ if $i }}, {{ end }}{{ template "openapi-schema-link" $p }}{{ end }}.
{{ end -}}
{{- if or .Properties .AllOf -}}
{{ template "openapi-properties-table" (dict "schema" $schemaRef) }}
{{- else -}}
Type: {{ template "openapi-type" $schemaRef }}{{ with .Enum }} (enum: {{ range $i, $v := . }}{{ if $i }}, {{ end }}`{{ $v }}`{{ end }}){{ end }}
{{- end }}
{{- end }}
{{ end -}}
{{- end -}}
{{- /* ========== Properties table ========== */ -}}
{{- define "openapi-properties-table" -}}
{{- $schemaRef := .schema -}}
{{- with $schemaRef.Value -}}
{{- $props := dict -}}
{{- $required := slice -}}
{{- with .Properties }}{{ $props = merge $props . }}{{ end -}}
{{- with .Required }}{{ $required = $required | append . }}{{ end -}}
{{- range .AllOf -}}
{{- if not .Ref -}}
{{- with .Value -}}
{{- with .Properties }}{{ $props = merge $props . }}{{ end -}}
{{- with .Required }}{{ $required = $required | append . }}{{ end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if $props }}
| Property | Type | Required | Description |
|----------|------|----------|-------------|
{{- range $name, $prop := $props -}}
{{- $isRequired := in $required $name }}
| `{{ $name }}` | {{ template "openapi-type" $prop }} | {{ if $isRequired }}yes{{ else }}no{{ end }} | {{ with $prop.Value.Description }}{{ . | chomp | replaceRE "\n" " " }}{{ end }} |
{{- end }}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* ========== Type cell (handles ref, array, primitive) ========== */ -}}
{{- define "openapi-type" -}}
{{- if .Ref -}}
{{- template "openapi-schema-link" . -}}
{{- else -}}
{{- with .Value -}}
{{- $t := "" -}}{{- with .Type }}{{ with .Slice }}{{ $t = index . 0 }}{{ end }}{{ end -}}
{{- if eq $t "array" -}}
array of {{ template "openapi-type" .Items }}
{{- else if $t -}}
{{ $t }}{{ with .Format }} ({{ . }}){{ end }}
{{- else if .Enum -}}
enum
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* ========== Schema link (#model-name) ========== */ -}}
{{- define "openapi-schema-link" -}}
{{- $parts := split .Ref "/" -}}
{{- $name := index $parts (sub (len $parts) 1) -}}
[{{ $name }}](#model-{{ $name | anchorize }})
{{- end -}}