Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Look for examples in all possible locations #2076

Merged
merged 8 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/internal/newsfragments/2076.clarification
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support more locations for examples in OpenAPI definitions and JSON schemas.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove an erroneous `room_id` field in a few examples.
1 change: 0 additions & 1 deletion data/api/server-server/invites-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ paths:
$ref: ../../event-schemas/examples/invite_room_state.json
type: object
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
Expand Down
1 change: 0 additions & 1 deletion data/api/server-server/invites-v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ paths:
example: {
"room_version": "2",
"event": {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
Expand Down
1 change: 0 additions & 1 deletion data/api/server-server/joins-v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ paths:
- type
- content
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
Expand Down
1 change: 0 additions & 1 deletion data/api/server-server/knocks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ paths:
- type
- content
example: {
"room_id": "!somewhere:example.org",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
Expand Down
8 changes: 1 addition & 7 deletions layouts/partials/events/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,4 @@
{{ $path := delimit (slice "event-schemas/examples" .name) "/" }}

{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" .schema "path" $path) }}
{{ $example_json := jsonify (dict "indent" " ") $example_content }}
{{ $example_json = replace $example_json "\\u003c" "<" }}
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}

```json
{{ $example_json }}
```
{{ partial "render-example" (dict "example" $example_content) }}
46 changes: 0 additions & 46 deletions layouts/partials/json-schema/resolve-example.html

This file was deleted.

66 changes: 66 additions & 0 deletions layouts/partials/json-schema/resolve-examples.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{{/*

Find examples in the given JSON schema.

Tries to find examples in the `examples` and `example` keys of the schema
first.

If it doesn't succeed, iterates through the properties and subproperties to
collect their examples, to merge them into a complete example for the whole
object.

Parameter: the JSON schema to extract the examples from.

*/}}

{{ $this_object := . }}
{{ $examples := slice }}

{{ if $this_object.examples }}
{{/* This is an array of examples, we can just use it */}}
{{ $examples = $this_object.examples }}
{{ else if $this_object.example }}
{{ $examples = slice $this_object.example }}
{{ else }}
{{/* Resolve the nested examples */}}
{{ if eq $this_object.type "object" }}
{{ $example := dict }}

{{ range $key, $property := $this_object.properties}}
{{ $this_property_examples := partial "json-schema/resolve-examples" $property }}
{{/*
It would be too complex to handle several nested examples,
just use the first one.
*/}}
{{ with index $this_property_examples 0 }}
{{ $example = merge (dict $key .) $example }}
{{ end }}
{{ end }}

{{ if (or $example (not $this_object.properties)) }}
{{ $examples = slice $example }}
{{ end }}

{{ else if eq $this_object.type "array" }}

{{/* the "items" within an array can either be an object (where we have a
list of items which match the schema), or a list (for tuple
validation, where each item has a different schema).

TODO: support tuple validation here.
*/}}
{{ if reflect.IsMap $this_object.items }}
{{ $items_examples := partial "json-schema/resolve-examples" $this_object.items }}
{{/*
It would be too complex to handle several nested examples,
just use the first one.
*/}}
{{ with index $items_examples 0 }}
{{ $examples = slice (slice .) }}
{{ end }}
{{ end }}
{{ end }}

{{ end }}

{{ return $examples }}
98 changes: 98 additions & 0 deletions layouts/partials/openapi/render-media-type-objects.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{{/*

Render a map of [Media Type Objects](https://spec.openapis.org/oas/v3.1.1#media-type-object).
This map can be found as the `content` field of requests and responses.

Parameters:

* `content`: A map of MIME type to Media Type Object
* `kind`: the kind of object that is rendered, either `request` or `response`.
* `anchor_base`: a prefix to add to the HTML anchors generated for each object

This template renders:

* Object tables if the map contains an `application/json` MIME type, or a
table with the MIME types and the corresponding description of the object.
* The examples of the Media Type Objects.

*/}}

{{ $json_body := index .content "application/json" }}
{{ if $json_body }}
{{/*
Display the JSON schemas
*/}}

{{ $schema := $json_body.schema }}

{{/*
All this is to work out how to express the content of the response
in the case where it is an array.
*/}}
{{ if eq $schema.type "array" }}
{{ $type_of := "" }}
{{ if $schema.items.anyOf }}
{{ $types := slice }}
{{ range $schema.items.anyOf }}
{{ if .title }}
{{ $types = $types | append .title}}
{{ else }}
{{ $types = $types | append .type }}
{{ end }}
{{ end }}
{{ $type_of = delimit $types ", "}}
{{ else }}
{{ $type_of = $schema.items.title }}
{{ end }}
<p>Array of <code>{{ $type_of }}</code>.</p>
{{ end }}

{{/*
Render object tables for any objects referenced in the response.

This will be a no-op for response types which aren't objects or arrays.
*/}}
{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" .anchor_base) }}
{{ range $additional_types }}
{{ partial "openapi/render-object-table" . }}
{{ end }}
{{ else }}
{{/*
Show the content types and description.
*/}}
{{ partial "openapi/render-content-type" (dict "content_types" .content) }}
{{ end }}

{{/*
Show all the examples.
*/}}
{{ if eq .kind "request" }}
<h3>Request body example</h3>
{{ end }}

{{ range $mime, $body := .content }}
{{ $examples := slice }}

{{/*
Find the examples to display, with the following priority:

1. The `examples` field
2. The `example` field
3. The examples in the `schema` field
*/}}
{{ if $body.examples }}
{{/* This is a map of string to Example Object */}}
{{ range $key, $example := $body.examples }}
{{/* The example is in the `value` field of the object */}}
{{ $examples = $examples | append (slice $example.value) }}
{{ end }}
{{ else if $body.example }}
{{ $examples = slice $body.example }}
{{ else if $body.schema }}
{{ $examples = partial "json-schema/resolve-examples" $body.schema }}
{{ end }}

{{ range $examples }}
{{ partial "render-example" (dict "example" . "mime" $mime) }}
{{ end }}
{{ end }}
59 changes: 1 addition & 58 deletions layouts/partials/openapi/render-request.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,65 +33,8 @@ <h3>Request parameters</h3>

{{ if $request_body }}
<h3>Request body</h3>
{{/*
A request can have several content types.
*/}}
{{ $json_body := index $request_body.content "application/json" }}
{{ if $json_body }}
{{/*
Display the JSON schemas
*/}}
{{ $schema := $json_body.schema }}

{{ $additional_types := partial "json-schema/resolve-additional-types" (dict "schema" $schema "anchor_base" $anchor) }}
{{ range $additional_types }}
{{ partial "openapi/render-object-table" . }}
{{ end }}
{{ else }}
{{/*
Show the content types and description.
*/}}
{{ partial "openapi/render-content-type" (dict "content_types" $request_body.content) }}
{{ end }}

<h3>Request body example</h3>
{{/*
Show all the examples.
*/}}
{{ range $mime, $body := $request_body.content }}
{{ $example := dict }}

{{ if $body.schema }}
{{ $example = partial "json-schema/resolve-example" $body.schema }}
{{ end }}

{{ if and (eq ($example | len) 0) $body.example }}
{{/*
If no example was generated from the schema, fallback to the
main example.
*/}}
{{ $example = $body.example }}
{{ end }}

{{ if eq $mime "application/json" }}
{{ $example_json := jsonify (dict "indent" " ") $example }}
{{ $example_json = replace $example_json "\\u003c" "<" }}
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
```json
{{ $example_json }}
```
{{ else }}
{{ $example = $example | safeHTML }}
{{/*
We need to set a language for the code otherwise the styling
is different than other examples.
*/}}
```json
{{ $example }}
```
{{ end }}

{{ end }}
{{ partial "openapi/render-media-type-objects" (dict "content" $request_body.content "kind" "request" "anchor_base" $anchor) }}
{{ end }}

{{ else }}
Expand Down
Loading