api docs: Redesign visuals for documenting arguments.

The previous system for documenting arguments was very ugly if any of
the examples or descriptions were wrong.  After thinking about this
for a while, I concluded the core problem was that a table was the
wrong design element to use for API parameters, and we'd be much
better off with individual card-type widgets instead.

This rewrites the API arguments documentation implementation to use a
basic sort of card-like system with some basic styling; I think the
result is a lot more readable, and it's a lot more clear how we would
add additional OpenAPI details (like parameter types) to the
documentation.
This commit is contained in:
Tim Abbott
2020-03-26 21:03:26 -07:00
parent 43996106d8
commit 820f0e275e
4 changed files with 61 additions and 76 deletions

View File

@@ -291,6 +291,16 @@
} }
} }
code {
/* Copied from rendered_markdown.scss */
font-size: 0.857em;
unicode-bidi: embed;
direction: ltr;
white-space: pre-wrap;
padding: 0px 4px;
background-color: hsl(0, 0%, 100%);
}
.code-section { .code-section {
ol { ol {
margin-left: 15px; margin-left: 15px;

View File

@@ -1717,14 +1717,27 @@ input.new-organization-button {
padding-right: 10px; padding-right: 10px;
} }
.table .json-api-example { .api-argument {
width: fit-content; .api-argument-name {
max-width: 300px; font-family: monospace;
word-wrap: break-word; }
}
.table .json-api-example code { .api-argument-example-label {
white-space: pre-wrap; font-style: italic;
}
.api-argument-required {
text-transform: uppercase;
font-weight: 600;
font-size: 12px;
color: hsl(14, 75%, 60%);
}
.api-argument-optional {
text-transform: uppercase;
font-weight: 400;
font-size: 12px;
color: hsl(0, 0%, 47%);
}
} }
.desktop-redirect-box { .desktop-redirect-box {

View File

@@ -1,6 +1,6 @@
import re import re
import os import os
import ujson import json
from django.utils.html import escape as escape_html from django.utils.html import escape as escape_html
from markdown.extensions import Extension from markdown.extensions import Extension
@@ -67,7 +67,7 @@ class APIArgumentsTablePreprocessor(Preprocessor):
raise e raise e
else: else:
with open(filename, 'r') as fp: with open(filename, 'r') as fp:
json_obj = ujson.load(fp) json_obj = json.load(fp)
arguments = json_obj[doc_name] arguments = json_obj[doc_name]
if arguments: if arguments:
@@ -89,29 +89,17 @@ class APIArgumentsTablePreprocessor(Preprocessor):
return lines return lines
def render_table(self, arguments: List[Dict[str, Any]]) -> List[str]: def render_table(self, arguments: List[Dict[str, Any]]) -> List[str]:
# TODO: Fix naming now that this no longer renders a table.
table = [] table = []
beginning = """ argument_template = """
<table class="table"> <div class="api-argument">
<thead> <p class="api-argument-name"><strong>{argument}</strong> {required}</p>
<tr> <div class="api-example">
<th>Argument</th> <span class="api-argument-example-label">Example</span>: <code>{example}</code>
<th>Example</th> </div>
<th>Required</th> <div class="api-description">{description}</div>
<th>Description</th> <hr />
</tr> </div>"""
</thead>
<tbody>
"""
tr = """
<tr>
<td><code>{argument}</code></td>
<td class="json-api-example"><code>{example}</code></td>
<td>{required}</td>
<td>{description}</td>
</tr>
"""
table.append(beginning)
md_engine = markdown.Markdown(extensions=[]) md_engine = markdown.Markdown(extensions=[])
@@ -125,23 +113,20 @@ class APIArgumentsTablePreprocessor(Preprocessor):
default = argument.get('schema', {}).get('default') default = argument.get('schema', {}).get('default')
if default is not None: if default is not None:
description += '\nDefaults to `{}`.'.format(ujson.dumps(default)) description += '\nDefaults to `{}`.'.format(json.dumps(default))
# TODO: Swagger allows indicating where the argument goes # TODO: OpenAPI allows indicating where the argument goes
# (path, querystring, form data...). A column in the table should # (path, querystring, form data...). We should document this detail.
# be added for this. table.append(argument_template.format(
table.append(tr.format(
argument=argument.get('argument') or argument.get('name'), argument=argument.get('argument') or argument.get('name'),
# Show this as JSON to avoid changing the quoting style, which # Show this as JSON to avoid changing the quoting style, which
# may cause problems with JSON encoding. # may cause problems with JSON encoding.
example=escape_html(ujson.dumps(argument['example'])), example=escape_html(json.dumps(argument['example'])),
required='Yes' if argument.get('required') else 'No', required='<span class="api-argument-required">required</span>' if argument.get('required')
else '<span class="api-argument-optional">optional</span>',
description=md_engine.convert(description), description=md_engine.convert(description),
)) ))
table.append("</tbody>")
table.append("</table>")
return table return table
def makeExtension(*args: Any, **kwargs: str) -> MarkdownArgumentsTableGenerator: def makeExtension(*args: Any, **kwargs: str) -> MarkdownArgumentsTableGenerator:

View File

@@ -1328,39 +1328,13 @@ paths:
example: [ example: [
{ {
"id":1, "id": 4,
"value":"short text data" "value": "vim"
}, },
{ {
"id":2, "id": 5,
"value":"long text data" "value": "1909-04-05"
}, },
{
"id":3,
"value":"short text data"
},
{
"id":4,
"value":"vim"
},
{
"id":5,
"value":"1909-3-5"
},
{
"id":6,
"value":"https:\/\/zulipchat.com"
},
{
"id":7,
"value":[
12
]
},
{
"id":8,
"value":"cordelia"
}
] ]
required: false required: false
@@ -2419,8 +2393,9 @@ paths:
example: ['message'] example: ['message']
- name: narrow - name: narrow
in: query in: query
description: A JSON-encoded array of length 2 indicating the narrow description: |
for which you'd like to receive events for. For instance, to A JSON-encoded array of length 2 indicating the narrow
for which you'd like to receive events. For instance, to
receive events for the stream `Denmark`, you would specify receive events for the stream `Denmark`, you would specify
`narrow=['stream', 'Denmark']`. Another example is `narrow=['stream', 'Denmark']`. Another example is
`narrow=['is', 'private']` for private messages. `narrow=['is', 'private']` for private messages.
@@ -2951,7 +2926,7 @@ paths:
description: | description: |
Whether the stream is limited to announcements. Whether the stream is limited to announcements.
**Changes**: Deprecated in Zulip 2.2, use stream_post_policy instead. **Changes**: Deprecated in Zulip 2.2, use `stream_post_policy` instead.
schema: schema:
type: boolean type: boolean
example: true example: true
@@ -2965,7 +2940,7 @@ paths:
* 2 => Only administrators can post. * 2 => Only administrators can post.
* 3 => Only new members can post. * 3 => Only new members can post.
**Changes**: New in Zulip 2.2, replacing the previous is_announcement_only **Changes**: New in Zulip 2.2, replacing the previous `is_announcement_only`
boolean. boolean.
schema: schema:
type: integer type: integer
@@ -2973,7 +2948,9 @@ paths:
required: false required: false
- name: history_public_to_subscribers - name: history_public_to_subscribers
in: query in: query
description: The new state for the history_public_to_subscribers. description: |
Whether subscribers have access to full stream history, even before they joined
the stream.
schema: schema:
type: boolean type: boolean
example: true example: true