forked from traccar/traccar
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add script to generate HTML from swagger.json
- Loading branch information
Showing
1 changed file
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,354 @@ | ||
#!/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 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 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 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() |