127 lines
3.6 KiB
Python
127 lines
3.6 KiB
Python
"""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:])
|