PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/swagger2html.py

https://bitbucket.org/josedaudi_/traccar
Python | 360 lines | 349 code | 10 blank | 1 comment | 46 complexity | 71e733e613ff41359e6bf8ad30beef2f MD5 | raw file
  1. #!/usr/bin/python
  2. import sys, argparse, json, re
  3. def handleException(etype, e=None):
  4. if etype == 'KeyError':
  5. print "Error: Required property {} not found".format(e)
  6. elif etype == 'IOError':
  7. print "Error ({}): {}".format(e.errno, e.strerror)
  8. elif etype == 'ValueError':
  9. print "Error: Unable to parse input as JSON"
  10. elif etype == 'Custom':
  11. print e
  12. sys.exit(1)
  13. def get_json(filename):
  14. try:
  15. with open(filename) as json_file:
  16. json_data = json.load(json_file)
  17. return json_data
  18. except IOError as e:
  19. handleException('IOError', e)
  20. except ValueError:
  21. handleException('ValueError')
  22. except:
  23. print "Unexpected error: {}".format(sys.exc_info()[0])
  24. raise
  25. def write_file(filename, body):
  26. try:
  27. with open(filename, 'w') as md_file:
  28. md_file.write(body)
  29. except IOError as e:
  30. handleException('IOError', e)
  31. def make_header(json_data):
  32. try:
  33. if not 'swagger' in json_data:
  34. raise KeyError
  35. info = json_data['info']
  36. md = "<h1>{}</h1>\n".format(info['title'])
  37. md += "<p>Version: {}</p>\n".format(info['version'])
  38. if 'license' in info:
  39. md += "<p>License: "
  40. license = info['license']
  41. if 'url' in license:
  42. md += '<a href="{}">{}</a>'.format(license['url'], license['name'])
  43. else:
  44. md += license['name']
  45. md += '</p>\n'
  46. if 'contact' in info:
  47. contact = info['contact']
  48. if 'name' in contact or 'email' in contact:
  49. md += '<p>Contact: '
  50. if not 'name' in contact:
  51. md += '<a href="mailto:{0}">{0}</a>'.format(contact['email'])
  52. elif not 'email' in contact:
  53. md += contact['name']
  54. else:
  55. md += '{0} &lt;<a href="mailto:{1}"&gt;{1}</a>'.format(contact['name'], contact['email'])
  56. md += ' \n'
  57. if 'url' in contact:
  58. md += "<p>Website: {}</p>\n".format(contact['url'])
  59. if 'termsOfService' in info:
  60. md += '<p>Terms of Service: {}</p>\n'.format(info['termsOfService'])
  61. if 'host' in json_data:
  62. md += '<p>Base URL: '
  63. base = json_data['host']
  64. if 'basePath' in json_data:
  65. base += json_data['basePath']
  66. else:
  67. base += '/'
  68. if 'schemes' in json_data:
  69. md += (', ').join(map(
  70. lambda x: '<a href="{0}://{1}">{0}://{1}</a>'.format(x, base),
  71. json_data['schemes']
  72. ))
  73. else:
  74. md += '<a href="{0}">{0}</a>'.format(base)
  75. md += '</p>\n'
  76. if 'description' in info:
  77. md += '<p>Description: {}</p>\n'.format(info['description'])
  78. md += '\n'
  79. return md
  80. except KeyErrori as e:
  81. handleException('KeyError', e)
  82. def make_ref(ref):
  83. href = ref.split('/')[1:]
  84. return '<a href="#{}">{}</a>'.format('_'.join(href), href[-1])
  85. def get_ref(ref, raw):
  86. keys = ref.split('/')
  87. return raw[keys[1]][keys[2]]
  88. def make_html(s):
  89. reg = re.compile(r"[*_]{3}(.+?)[*_]{3}")
  90. s = reg.sub(r"<strong><em>\1</em></strong>", s)
  91. reg = re.compile(r"[*_]{2}(.+?)[*_]{2}")
  92. s = reg.sub(r"<strong>\1</strong>", s)
  93. reg = re.compile(r"[*_](.+?)[*_]")
  94. s = reg.sub(r"<em>\1</em>", s)
  95. reg = re.compile(r"\`(.+?)\`")
  96. s = reg.sub(r"<code>\1</code>", s)
  97. return s
  98. def make_table(data):
  99. md = '<table class="table-bordered">\n'
  100. md += ' <thead>\n'
  101. md += ' <tr>\n'
  102. for col in data[0]:
  103. md += ' <td>{}</td>\n'.format(col)
  104. md += ' </tr>\n'
  105. md += ' </thead>\n'
  106. md += ' <tbody>\n'
  107. for row in data[1:]:
  108. md += ' <tr>\n'
  109. for cell in row:
  110. md += ' <td>{}</td>\n'.format(cell)
  111. md += ' </tr>\n'
  112. md += ' </tbody>\n'
  113. md += '</table>\n'
  114. return md
  115. def make_params_table(itemsraw, raw):
  116. items = []
  117. for item in itemsraw:
  118. if '$ref' in item:
  119. items.append(get_ref(item['$ref'], raw))
  120. else:
  121. items.append(item)
  122. try:
  123. fields = list(set([]).union(*map(lambda x: x.keys(), items)))
  124. row = [ 'Name', 'ParamType' ]
  125. if 'description' in fields:
  126. row.append('Description')
  127. if 'required' in fields:
  128. row.append('Required')
  129. if 'type' in fields:
  130. row.append('DataType')
  131. if 'schema' in fields:
  132. row.append('Schema')
  133. table = [ row ]
  134. for item in items:
  135. row = [ "<em>{}</em>".format(item['name']), item['in'] ]
  136. if 'description' in fields:
  137. if 'description' in item:
  138. row.append(make_html(item['description']))
  139. else:
  140. row.append('')
  141. if 'required' in fields:
  142. required = 'False'
  143. if 'required' in item and item['required']:
  144. required = "<strong>True</strong>"
  145. row.append(required)
  146. if 'type' in fields:
  147. type = ''
  148. if 'type' in item:
  149. if item['type'] == 'array':
  150. type = "[ <em>{}</em> ]".format(item['items']['type'])
  151. else:
  152. type = item['type']
  153. if 'format' in item:
  154. type += " ({})".format(item['format'])
  155. type = "<em>{}</em>".format(type)
  156. row.append(type)
  157. if 'schema' in fields:
  158. if 'schema' in item:
  159. if '$ref' in item['schema']:
  160. row.append(make_ref(item['schema']['$ref']))
  161. else:
  162. row.append('')
  163. table.append(row)
  164. return make_table(table)
  165. except KeyError as e:
  166. handleException('KeyError', e)
  167. def make_responses_table(responses):
  168. try:
  169. fields = list(
  170. set([]).union(*map(lambda x: x.keys(),
  171. map(lambda x: responses[x], responses.keys())
  172. ))
  173. )
  174. row = [ 'Status Code', 'Description' ]
  175. if 'headers' in fields:
  176. row.append('Headers')
  177. if 'schema' in fields:
  178. row.append('Schema')
  179. if 'examples' in fields:
  180. row.append('Examples')
  181. table = [ row ]
  182. for key in sorted(responses):
  183. response = responses[key]
  184. row = [ "<em>{}</em>".format(key), make_html(response['description']) ]
  185. if 'headers' in fields:
  186. header = ''
  187. if 'headers' in response:
  188. hrow = []
  189. for header, h_obj in response['headers'].iteritems():
  190. hrow += "{} ({})".format(header, h_obj['type'])
  191. if 'description' in h_obj:
  192. hrow += ": {}".format(h_obj['description'])
  193. header = ' \n'.join(hrow)
  194. row.append(header)
  195. if 'schema' in fields:
  196. schema = ''
  197. if 'schema' in response:
  198. if '$ref' in response['schema']:
  199. schema += make_ref(response['schema']['$ref'])
  200. if 'type' in response['schema']:
  201. if response['schema']['type'] == 'array':
  202. if '$ref' in response['schema']['items']:
  203. schema += make_ref(response['schema']['items']['$ref'])
  204. schema = "[ {} ]".format(schema)
  205. row.append(schema)
  206. if 'examples' in fields:
  207. if 'examples' in response:
  208. row.append(response['examples'])
  209. else:
  210. row.append('')
  211. table.append(row)
  212. return make_table(table)
  213. except KeyError as e:
  214. handleException('KeyError', e)
  215. def sorted_by_method(section):
  216. sorting_function = lambda x: [ 'GET', 'POST', 'PUT', 'DELETE' ].index(
  217. x['title'].split(' ')[0]
  218. )
  219. return sorted(sorted(section), key=sorting_function)
  220. def make_paths(sections, json_data):
  221. md = '<h2><a name="paths"></a>Paths</h2>\n'
  222. for key in sorted(sections):
  223. md += '<h3><a name="paths_{0}"></a>{0}</h3>\n'.format(key)
  224. for section in sorted_by_method(sections[key]):
  225. md += '<h4><a name="{}"></a><code>{}</code></h4>\n'.format(
  226. section['href'], section['title']
  227. )
  228. operation = section['operation']
  229. if 'summary' in operation:
  230. md += '<p>Summary: {}</p>\n'.format(make_html(operation['summary']))
  231. if 'description' in operation:
  232. md += '<p>Description: {}</p>\n'.format(make_html(operation['description']))
  233. md += '<h5>Parameters</h5>\n'
  234. if 'parameters' in operation and len(operation['parameters']) > 0:
  235. md += make_params_table(operation['parameters'], json_data)
  236. else:
  237. md += "<p><em>None</em></p>\n"
  238. md += '<h5>Responses</h5>\n'
  239. md += make_responses_table(operation['responses'])
  240. md += '\n'
  241. md += '\n'
  242. return md
  243. def make_contents(path_section, json_data):
  244. md = '<h3>Contents</h3>\n'
  245. md += '<ul>\n'
  246. md += ' <li><a href="#paths">Paths</a>\n'
  247. md += ' <ul>\n'
  248. for key in sorted(path_section):
  249. md += ' <li><a href="#paths_{0}">{0}</a>\n'.format(key)
  250. md += ' <ul>\n'
  251. for section in sorted_by_method(path_section[key]):
  252. md += ' <li><a href="#{}">{}</a></li>\n'.format(
  253. section['href'], section['title']
  254. )
  255. md += ' </ul>\n'
  256. md += ' </li>\n'
  257. md += ' </ul>\n'
  258. md += ' </li>\n'
  259. md += ' <li><a href="#definitions">Models</a>\n'
  260. md += ' <ul>\n'
  261. for key in sorted(json_data['definitions']):
  262. md += ' <li><a href="#definitions_{0}">{0}</a></li>\n'.format(key)
  263. md += ' </ul>\n'
  264. md += ' </li>\n'
  265. md += '</ul>\n'
  266. return md
  267. def make_definitions(json_data):
  268. md = '<h2><a name="definitions"></a>Models</h2>\n'
  269. for name in sorted(json_data['definitions']):
  270. md += '<h3><a name="definitions_{0}"></a>{0}</h3>\n'.format(name)
  271. model = json_data['definitions'][name]
  272. if 'properties' in model:
  273. fields = list(
  274. set(['type']).union(
  275. *map(lambda x: x.keys(),
  276. map(lambda x: model['properties'][x], model['properties'].keys())
  277. )
  278. )
  279. )
  280. row = [ 'Property', 'Type' ]
  281. if 'description' in fields:
  282. row.append('Description')
  283. table = [ row ]
  284. for key in sorted(model['properties']):
  285. property = model['properties'][key]
  286. row = [ "<em>{}</em>".format(key) ]
  287. if 'type' in property:
  288. type = property['type']
  289. if 'format' in property:
  290. type += " ({})".format(property['format'])
  291. row.append("<em>{}</em>".format(type))
  292. elif '$ref' in property:
  293. row.append(make_ref(property['$ref']))
  294. else:
  295. row.append('')
  296. if 'description' in fields:
  297. if 'description' in property:
  298. row.append(make_html(property['description']))
  299. else:
  300. row.append('')
  301. table.append(row)
  302. md += make_table(table)
  303. return md
  304. def make_markdown(json_data):
  305. path_sections = {}
  306. for endpoint in json_data['paths']:
  307. path_split = endpoint.split('/')
  308. path_key = path_split[1]
  309. if not path_key in path_sections:
  310. path_sections[path_key] = []
  311. for method, operation in json_data['paths'][endpoint].iteritems():
  312. if 'operationId' in operation:
  313. link = operation['operationId']
  314. else:
  315. link = ''.join([
  316. c for c in endpoint if c not in ['/', '{', '}']
  317. ])
  318. path_sections[path_key].append({
  319. 'title': '{} {}'.format(method.upper(), endpoint),
  320. 'href': 'paths_{}_{}'.format(link, method.upper()),
  321. 'operation': operation
  322. })
  323. md = make_header(json_data)
  324. md += make_contents(path_sections, json_data)
  325. md += make_paths(path_sections, json_data)
  326. md += make_definitions(json_data)
  327. return md
  328. def main():
  329. parser = argparse.ArgumentParser()
  330. parser.add_argument('SPECFILE', help="path to swagger.json file")
  331. parser.add_argument('OUTFILE', help="path to output HTML file")
  332. args = parser.parse_args()
  333. marked_down = make_markdown(get_json(args.SPECFILE))
  334. if args.OUTFILE:
  335. write_file(args.OUTFILE, marked_down)
  336. print " success: {}".format(args.OUTFILE)
  337. else:
  338. print marked_down
  339. if __name__ == '__main__':
  340. main()