forked from openedx/edx-platform
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sw2sphinxopenapi.py
126 lines (91 loc) · 3.57 KB
/
sw2sphinxopenapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""Generate ReST documents for sphinxcontrib-openapi from an OpenAPI swagger file.
This program reads an OpenAPI swagger file, and generates .rst files. Each
file will render a segment of the swagger file, using sphinxcontrib-openapi.
An index.rst file is created listing all of the endpoints, linking to their
detailed segment page.
"""
import functools
import itertools
import os
import os.path
import re
import sys
import textwrap
import yaml
def method_ordered_items(method_data):
"""Yield the HTTP method items from method_data, in a canonical order."""
for key in ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']:
if key in method_data:
yield key, method_data[key]
def rst_header(text, level, anchor=None):
"""Create a ReST header, including a possible anchor.
Returns a multi-line string.
"""
rst = []
if anchor:
rst.append(f".. _{anchor}:")
rst.append("")
char = " #=-"[level]
if level == 1:
rst.append(char * len(text))
rst.append(text)
rst.append(char * len(text))
rst.append("")
return "\n".join(rst)
# Regexes that determine the segments. If one of these matches a URI, the
# matched text is the segment for that endpoint.
SEGMENTERS = [
r"^.*?/v\d+/[\w_-]+",
r"^(/[\w_-]+){,3}",
]
def segment_for_uri(uri):
"""Determine the segment for an endpoint's URI."""
for segmenter in SEGMENTERS:
m = re.search(segmenter, uri)
if m:
return m.group()
return "default"
def convert_swagger_to_sphinx(swagger_file, output_dir):
"""Convert a swagger.yaml file to a series of Sphinx documents.
Args:
swagger_file: the filename of the OpenAPI swagger file to read.
output_dir: the directory where the .rst files should be written.
"""
with open(swagger_file) as swyaml:
swagger = yaml.safe_load(swyaml)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
rel_swagger_path = os.path.relpath(swagger_file, output_dir)
with open(os.path.join(output_dir, 'index.rst'), 'w') as index:
pr_index = functools.partial(print, file=index)
pr_index(rst_header(swagger['info']['title'], level=1))
pr_index(swagger['info']['description'])
pr_index(textwrap.dedent("""\
.. toctree::
:glob:
:hidden:
*
"""))
segment = None
uris = sorted(swagger['paths'])
for segment, segment_uris in itertools.groupby(uris, key=segment_for_uri):
outfile = segment.strip('/').replace('/', '_')
with open(os.path.join(output_dir, outfile + '.rst'), 'w') as outf:
pr_outf = functools.partial(print, file=outf)
pr_outf(rst_header(segment, level=1, anchor="gen_" + outfile))
pr_outf(f".. openapi:: {rel_swagger_path}")
pr_outf(" :format: markdown")
pr_outf(" :include:")
pr_outf(f" {segment}.*")
pr_index(rst_header(segment, level=2))
for uri in segment_uris:
methods = swagger['paths'][uri]
for method, op_data in method_ordered_items(methods):
summary = ''
if 'summary' in op_data:
summary = " --- {}".format(op_data['summary'])
pr_index(f":ref:`{method.upper()} {uri}<gen_{outfile}>`{summary}\n")
def main(args):
convert_swagger_to_sphinx(swagger_file=args[0], output_dir=args[1])
if __name__ == '__main__':
main(sys.argv[1:])