mirror of
https://github.com/traccar/traccar.git
synced 2025-01-07 03:07:01 +08:00
361 lines
13 KiB
Python
Executable File
361 lines
13 KiB
Python
Executable 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()
|