zulip/tools/create-test-api-docs

156 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python3
# check for the venv
from lib import sanity_check
sanity_check.check_venv(__file__)
import html
import os
import pprint
from collections import defaultdict
from typing import Any, Dict, List, Set
import orjson
Call = Dict[str, Any]
def clean_up_pattern(s: str) -> str:
paren_level = 0
in_braces = False
result = ""
prior_char = None
for c in s:
if c == "(":
paren_level += 1
if c == "<" and prior_char == "P":
in_braces = True
if in_braces or (paren_level == 0):
if c != "?":
result += c
if c == ")":
paren_level -= 1
if c == ">":
in_braces = False
prior_char = c
return result
def encode_info(info: Any) -> str:
try:
result = ""
try:
info = orjson.loads(info)
result = "(stringified)\n"
except orjson.JSONDecodeError:
pass
result += html.escape(pprint.pformat(info, indent=4))
return "<pre>" + result + "</pre>"
except Exception:
pass
try:
return html.escape(str(info))
except Exception:
pass
return "NOT ENCODABLE"
def fix_test_name(s: str) -> str:
return s.replace("zerver.tests.", "")
def create_single_page(pattern: str, out_dir: str, href: str, calls: List[Call]) -> None:
fn = out_dir + "/" + href
with open(fn, "w") as f:
f.write(
"""
<style>
.test {
margin: 20px;
}
</style>
"""
)
f.write(f"<h3>{html.escape(pattern)}</h3>\n")
calls.sort(key=lambda call: call["status_code"])
for call in calls:
f.write("<hr>")
f.write("\n{}".format(fix_test_name(call["test_name"])))
f.write('<div class="test">')
f.write(call["url"])
f.write("<br>\n")
f.write(call["method"] + "<br>\n")
f.write("status code: {}<br>\n".format(call["status_code"]))
f.write("<br>")
f.write("</div>")
def create_user_docs() -> None:
fn = "var/url_coverage.txt" # TODO: make path more robust, maybe use json suffix
out_dir = "var/api_docs"
try:
os.mkdir(out_dir)
except OSError:
pass
main_page = out_dir + "/index.html"
with open(main_page, "w") as f:
f.write(
"""
<style>
li {
list-style-type: none;
}
a {
text-decoration: none;
}
</style>
"""
)
with open(fn, "rb") as coverage:
calls = [orjson.loads(line) for line in coverage]
pattern_dict: Dict[str, List[Call]] = defaultdict(list)
for call in calls:
if "pattern" in call:
pattern = clean_up_pattern(call["pattern"])
if pattern:
pattern_dict[pattern].append(call)
patterns = set(pattern_dict.keys())
tups = [
("api/v1/external", "webhooks"),
("api/v1", "api"),
("json", "legacy"),
]
groups: Dict[str, Set[str]] = {}
for prefix, name in tups:
groups[name] = {p for p in patterns if p.startswith(prefix)}
patterns -= groups[name]
groups["other"] = patterns
for name in ["api", "legacy", "webhooks", "other"]:
f.write(name + " endpoints:\n\n")
f.write("<ul>\n")
for pattern in sorted(groups[name]):
href = pattern.replace("/", "-") + ".html"
link = f'<a href="{href}">{html.escape(pattern)}</a>'
f.write("<li>" + link + "</li>\n")
create_single_page(pattern, out_dir, href, pattern_dict[pattern])
f.write("</ul>")
f.write("\n")
print(f"open {main_page}")
if __name__ == "__main__":
create_user_docs()