/tools/swagger2html.py
Python | 360 lines | 349 code | 10 blank | 1 comment | 46 complexity | 71e733e613ff41359e6bf8ad30beef2f MD5 | raw file
- #!/usr/bin/python
- import sys, argparse, json, re
- def handleException(etype, e=None):
- if etype == 'KeyError':
- print "Error: Required property {} not found".format(e)
- elif etype == 'IOError':
- print "Error ({}): {}".format(e.errno, e.strerror)
- elif etype == 'ValueError':
- print "Error: Unable to parse input as JSON"
- elif etype == 'Custom':
- print e
- sys.exit(1)
- def get_json(filename):
- try:
- with open(filename) as json_file:
- json_data = json.load(json_file)
- return json_data
- except IOError as e:
- handleException('IOError', e)
- except ValueError:
- handleException('ValueError')
- except:
- print "Unexpected error: {}".format(sys.exc_info()[0])
- raise
- def write_file(filename, body):
- try:
- with open(filename, 'w') as md_file:
- md_file.write(body)
- except IOError as e:
- handleException('IOError', e)
- def make_header(json_data):
- try:
- if not 'swagger' in json_data:
- raise KeyError
- info = json_data['info']
- md = "<h1>{}</h1>\n".format(info['title'])
- md += "<p>Version: {}</p>\n".format(info['version'])
- if 'license' in info:
- md += "<p>License: "
- license = info['license']
- if 'url' in license:
- md += '<a href="{}">{}</a>'.format(license['url'], license['name'])
- else:
- md += license['name']
- md += '</p>\n'
- if 'contact' in info:
- contact = info['contact']
- if 'name' in contact or 'email' in contact:
- md += '<p>Contact: '
- if not 'name' in contact:
- md += '<a href="mailto:{0}">{0}</a>'.format(contact['email'])
- elif not 'email' in contact:
- md += contact['name']
- else:
- md += '{0} <<a href="mailto:{1}">{1}</a>'.format(contact['name'], contact['email'])
- md += ' \n'
- if 'url' in contact:
- md += "<p>Website: {}</p>\n".format(contact['url'])
- if 'termsOfService' in info:
- md += '<p>Terms of Service: {}</p>\n'.format(info['termsOfService'])
- if 'host' in json_data:
- md += '<p>Base URL: '
- base = json_data['host']
- if 'basePath' in json_data:
- base += json_data['basePath']
- else:
- base += '/'
- if 'schemes' in json_data:
- md += (', ').join(map(
- lambda x: '<a href="{0}://{1}">{0}://{1}</a>'.format(x, base),
- json_data['schemes']
- ))
- else:
- md += '<a href="{0}">{0}</a>'.format(base)
- md += '</p>\n'
- if 'description' in info:
- md += '<p>Description: {}</p>\n'.format(info['description'])
- md += '\n'
- return md
- except KeyErrori as e:
- handleException('KeyError', e)
- def make_ref(ref):
- href = ref.split('/')[1:]
- return '<a href="#{}">{}</a>'.format('_'.join(href), href[-1])
- def get_ref(ref, raw):
- keys = ref.split('/')
- return raw[keys[1]][keys[2]]
- def make_html(s):
- reg = re.compile(r"[*_]{3}(.+?)[*_]{3}")
- s = reg.sub(r"<strong><em>\1</em></strong>", s)
- reg = re.compile(r"[*_]{2}(.+?)[*_]{2}")
- s = reg.sub(r"<strong>\1</strong>", s)
- reg = re.compile(r"[*_](.+?)[*_]")
- s = reg.sub(r"<em>\1</em>", s)
- reg = re.compile(r"\`(.+?)\`")
- s = reg.sub(r"<code>\1</code>", s)
- return s
- def make_table(data):
- md = '<table class="table-bordered">\n'
- md += ' <thead>\n'
- md += ' <tr>\n'
- for col in data[0]:
- md += ' <td>{}</td>\n'.format(col)
- md += ' </tr>\n'
- md += ' </thead>\n'
- md += ' <tbody>\n'
- for row in data[1:]:
- md += ' <tr>\n'
- for cell in row:
- md += ' <td>{}</td>\n'.format(cell)
- md += ' </tr>\n'
- md += ' </tbody>\n'
- md += '</table>\n'
- return md
- def make_params_table(itemsraw, raw):
- items = []
- for item in itemsraw:
- if '$ref' in item:
- items.append(get_ref(item['$ref'], raw))
- else:
- items.append(item)
- try:
- fields = list(set([]).union(*map(lambda x: x.keys(), items)))
- row = [ 'Name', 'ParamType' ]
- if 'description' in fields:
- row.append('Description')
- if 'required' in fields:
- row.append('Required')
- if 'type' in fields:
- row.append('DataType')
- if 'schema' in fields:
- row.append('Schema')
- table = [ row ]
- for item in items:
- row = [ "<em>{}</em>".format(item['name']), item['in'] ]
- if 'description' in fields:
- if 'description' in item:
- row.append(make_html(item['description']))
- else:
- row.append('')
- if 'required' in fields:
- required = 'False'
- if 'required' in item and item['required']:
- required = "<strong>True</strong>"
- row.append(required)
- if 'type' in fields:
- type = ''
- if 'type' in item:
- if item['type'] == 'array':
- type = "[ <em>{}</em> ]".format(item['items']['type'])
- else:
- type = item['type']
- if 'format' in item:
- type += " ({})".format(item['format'])
- type = "<em>{}</em>".format(type)
- row.append(type)
- if 'schema' in fields:
- if 'schema' in item:
- if '$ref' in item['schema']:
- row.append(make_ref(item['schema']['$ref']))
- else:
- row.append('')
- table.append(row)
- return make_table(table)
- except KeyError as e:
- handleException('KeyError', e)
- def make_responses_table(responses):
- try:
- fields = list(
- set([]).union(*map(lambda x: x.keys(),
- map(lambda x: responses[x], responses.keys())
- ))
- )
- row = [ 'Status Code', 'Description' ]
- if 'headers' in fields:
- row.append('Headers')
- if 'schema' in fields:
- row.append('Schema')
- if 'examples' in fields:
- row.append('Examples')
- table = [ row ]
- for key in sorted(responses):
- response = responses[key]
- row = [ "<em>{}</em>".format(key), make_html(response['description']) ]
- if 'headers' in fields:
- header = ''
- if 'headers' in response:
- hrow = []
- for header, h_obj in response['headers'].iteritems():
- hrow += "{} ({})".format(header, h_obj['type'])
- if 'description' in h_obj:
- hrow += ": {}".format(h_obj['description'])
- header = ' \n'.join(hrow)
- row.append(header)
- if 'schema' in fields:
- schema = ''
- if 'schema' in response:
- if '$ref' in response['schema']:
- schema += make_ref(response['schema']['$ref'])
- if 'type' in response['schema']:
- if response['schema']['type'] == 'array':
- if '$ref' in response['schema']['items']:
- schema += make_ref(response['schema']['items']['$ref'])
- schema = "[ {} ]".format(schema)
- row.append(schema)
- if 'examples' in fields:
- if 'examples' in response:
- row.append(response['examples'])
- else:
- row.append('')
- table.append(row)
- return make_table(table)
- except KeyError as e:
- handleException('KeyError', e)
- def sorted_by_method(section):
- sorting_function = lambda x: [ 'GET', 'POST', 'PUT', 'DELETE' ].index(
- x['title'].split(' ')[0]
- )
- return sorted(sorted(section), key=sorting_function)
- def make_paths(sections, json_data):
- md = '<h2><a name="paths"></a>Paths</h2>\n'
- for key in sorted(sections):
- md += '<h3><a name="paths_{0}"></a>{0}</h3>\n'.format(key)
- for section in sorted_by_method(sections[key]):
- md += '<h4><a name="{}"></a><code>{}</code></h4>\n'.format(
- section['href'], section['title']
- )
- operation = section['operation']
- if 'summary' in operation:
- md += '<p>Summary: {}</p>\n'.format(make_html(operation['summary']))
- if 'description' in operation:
- md += '<p>Description: {}</p>\n'.format(make_html(operation['description']))
- md += '<h5>Parameters</h5>\n'
- if 'parameters' in operation and len(operation['parameters']) > 0:
- md += make_params_table(operation['parameters'], json_data)
- else:
- md += "<p><em>None</em></p>\n"
- md += '<h5>Responses</h5>\n'
- md += make_responses_table(operation['responses'])
- md += '\n'
- md += '\n'
- return md
- def make_contents(path_section, json_data):
- md = '<h3>Contents</h3>\n'
- md += '<ul>\n'
- md += ' <li><a href="#paths">Paths</a>\n'
- md += ' <ul>\n'
- for key in sorted(path_section):
- md += ' <li><a href="#paths_{0}">{0}</a>\n'.format(key)
- md += ' <ul>\n'
- for section in sorted_by_method(path_section[key]):
- md += ' <li><a href="#{}">{}</a></li>\n'.format(
- section['href'], section['title']
- )
- md += ' </ul>\n'
- md += ' </li>\n'
- md += ' </ul>\n'
- md += ' </li>\n'
- md += ' <li><a href="#definitions">Models</a>\n'
- md += ' <ul>\n'
- for key in sorted(json_data['definitions']):
- md += ' <li><a href="#definitions_{0}">{0}</a></li>\n'.format(key)
- md += ' </ul>\n'
- md += ' </li>\n'
- md += '</ul>\n'
- return md
- def make_definitions(json_data):
- md = '<h2><a name="definitions"></a>Models</h2>\n'
- for name in sorted(json_data['definitions']):
- md += '<h3><a name="definitions_{0}"></a>{0}</h3>\n'.format(name)
- model = json_data['definitions'][name]
- if 'properties' in model:
- fields = list(
- set(['type']).union(
- *map(lambda x: x.keys(),
- map(lambda x: model['properties'][x], model['properties'].keys())
- )
- )
- )
- row = [ 'Property', 'Type' ]
- if 'description' in fields:
- row.append('Description')
- table = [ row ]
- for key in sorted(model['properties']):
- property = model['properties'][key]
- row = [ "<em>{}</em>".format(key) ]
- if 'type' in property:
- type = property['type']
- if 'format' in property:
- type += " ({})".format(property['format'])
- row.append("<em>{}</em>".format(type))
- elif '$ref' in property:
- row.append(make_ref(property['$ref']))
- else:
- row.append('')
- if 'description' in fields:
- if 'description' in property:
- row.append(make_html(property['description']))
- else:
- row.append('')
- table.append(row)
- md += make_table(table)
- return md
- def make_markdown(json_data):
- path_sections = {}
- for endpoint in json_data['paths']:
- path_split = endpoint.split('/')
- path_key = path_split[1]
- if not path_key in path_sections:
- path_sections[path_key] = []
- for method, operation in json_data['paths'][endpoint].iteritems():
- if 'operationId' in operation:
- link = operation['operationId']
- else:
- link = ''.join([
- c for c in endpoint if c not in ['/', '{', '}']
- ])
- path_sections[path_key].append({
- 'title': '{} {}'.format(method.upper(), endpoint),
- 'href': 'paths_{}_{}'.format(link, method.upper()),
- 'operation': operation
- })
- md = make_header(json_data)
- md += make_contents(path_sections, json_data)
- md += make_paths(path_sections, json_data)
- md += make_definitions(json_data)
- return md
- def main():
- parser = argparse.ArgumentParser()
- parser.add_argument('SPECFILE', help="path to swagger.json file")
- parser.add_argument('OUTFILE', help="path to output HTML file")
- args = parser.parse_args()
- marked_down = make_markdown(get_json(args.SPECFILE))
- if args.OUTFILE:
- write_file(args.OUTFILE, marked_down)
- print " success: {}".format(args.OUTFILE)
- else:
- print marked_down
- if __name__ == '__main__':
- main()