Skip to content

Instantly share code, notes, and snippets.

@deeplook
Last active December 22, 2017 22:31
Show Gist options
  • Select an option

  • Save deeplook/fdb466008695c0982930383ac7ee3ef7 to your computer and use it in GitHub Desktop.

Select an option

Save deeplook/fdb466008695c0982930383ac7ee3ef7 to your computer and use it in GitHub Desktop.
Explore a RESTful Swagger/OpenAPI specification (JSON file) with Swagger-UI in a browser.
#!/usr/bin/env python
"""
Explore a RESTful Swagger/OpenAPI specification with Swagger-UI in a browser.
Features:
- download a recent Swagger-UI release
- extract its "dist" folder
- copy desired JSON file into "dist" folder
- modify "dist/index.html" to point to this JSON file
- run a local webserver serving static content from "dist"
- open a browser to show the JSON file inside Swagger-UI
- continue to browse the API in Swagger-UI after terminating this script
(and the webserver it runs)
Run like this:
$ python browse_rest_api.py --verbose my_swagger_spec.json
downloading https://github.com/swagger-api/swagger-ui/archive/v3.5.0.zip \
to /Users/gherman/Downloads/v3.5.0.zip
extracting swagger-ui-3.5.0/dist from /Users/gherman/Downloads/v3.5.0.zip
copying my_swagger_spec.json to swagger-ui-3.5.0/dist/
replacing start URL in swagger-ui-3.5.0/dist/index.html
starting webserver on http://localhost:8080
opening webbrowser on http://localhost:8080
127.0.0.1 - - [29/Nov/2017 00:07:23] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [29/Nov/2017 00:07:23] "GET /swagger-ui.css HTTP/1.1" 200 -
127.0.0.1 - - [29/Nov/2017 00:07:23] "GET /swagger-ui-standalone-preset.js HTTP/1.1" 200 -
127.0.0.1 - - [29/Nov/2017 00:07:24] "GET /swagger-ui-bundle.js HTTP/1.1" 200 -
127.0.0.1 - - [29/Nov/2017 00:07:24] "GET /my_swagger_spec.json HTTP/1.1" 200 -
^C
terminating webserver
(Swagger-UI should still work, but not reloading the page!)
Developed and tested on Python 3.6 and MacOS 10.12.6.
"""
import os
import re
import sys
import time
import shutil
import zipfile
import webbrowser
import argparse
from multiprocessing import Process
from http.server import HTTPServer, SimpleHTTPRequestHandler
from os.path import expanduser, expandvars, exists, join, basename
import requests
__author__ = "Dinu Gherman"
__license__ = "GPL3"
# FIXME: find a free port
PORT = 8080
def run_webui_server(root_path):
"""
Run simple HTTP server from some root path.
"""
os.chdir(root_path)
server_address = ('', PORT)
handler = SimpleHTTPRequestHandler
httpd = HTTPServer(server_address, handler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
def open_browser(url):
"""
Open given URL with default browser (to be called from a different process).
"""
webbrowser.open_new(url)
class OpenApiBrowser(object):
"""
Browser a local API specification (JSON file) with default browser.
"""
def __init__(self, json_path, verbose=True):
self.json_path = json_path
self.verbose = verbose
def download(self, url):
"""
Download a zipped release of Swagger-UI.
"""
zip_path = expandvars(join("${HOME}", "Downloads", basename(url)))
if exists(zip_path):
if self.verbose:
print("found {}".format(zip_path))
else:
if self.verbose:
print("downloading {} to {}".format(url, zip_path))
content = requests.get(url).content
open(zip_path, "wb").write(content)
return zip_path
def extract_ui(self, zip_path, dist_path):
"""
Unpack the Swagger-UI folder only from downloaded ZIP.
"""
if self.verbose:
print("extracting {} from {}".format(dist_path, zip_path))
zf = zipfile.ZipFile(zip_path)
members = zf.infolist()
for m in members:
if m.filename.startswith(dist_path):
zf.extract(m)
def inject_spec_file(self, dist_path):
"""
Copy Swagger/OpenAPI specification JSON file into Swagger-UI folder.
"""
if self.verbose:
print("copying {} to {}".format(self.json_path, dist_path + '/'))
shutil.copy(self.json_path, dist_path + '/')
def modify_start_url(self, index_path):
"""
Modify the default URL in index.html to point to given specification file.
"""
if self.verbose:
print("replacing start URL in {} ".format(index_path))
html = open(index_path).read()
default_url = "http://petstore.swagger.io/v2/swagger.json"
new_url = "http://localhost:" + \
str(PORT) + "/" + basename(self.json_path)
html1 = re.sub(default_url, new_url, html)
open(index_path, "w").write(html1)
def run_webui_process(self, address, path):
"""
Run a webserver showing the Swagger-UI at some path.
"""
if self.verbose:
print("starting webserver on {}".format(address))
p1 = Process(target=run_webui_server, args=(path,))
p1.start()
return p1
def open_webui(self, address):
"""
Open the Swagger-UI in a webbrowser.
"""
if self.verbose:
print("opening webbrowser on {}".format(address))
p2 = Process(target=open_browser, args=(address,))
p2.start()
return p2
def wait_until_interrupted(self, p1, p2):
"""
Wait until next KeyboardInterrupt, then exit the completed processes.
"""
try:
p1.join()
p2.join()
except KeyboardInterrupt:
pass
if self.verbose:
print()
print("terminating webserver")
print("(Swagger-UI should still work, but not reloading the page!)")
def open(self, url):
"""
Do all the necessary work.
"""
zip_path = self.download(url)
# FIXME: find this by sniffing into zipfile...
dist_path = "swagger-ui-3.5.0/dist"
self.extract_ui(zip_path, dist_path)
# FIXME: put this into a temporary folder?
# put the json_path file into swagger-ui-3.5.0/dist
self.inject_spec_file(dist_path)
self.modify_start_url(dist_path + "/index.html")
# set-up two processes to run in parallel, one for running a webserver,
# the other for opening a webbrowser showing what the first is serving
address = "http://localhost:" + str(PORT)
p1 = self.run_webui_process(address, dist_path)
time.sleep(0.5)
p2 = self.open_webui(address)
self.wait_until_interrupted(p1, p2)
if __name__ == '__main__':
desc = 'Explore a RESTful API spec. (JSON file) with Swagger-UI in a browser.'
p = argparse.ArgumentParser(description=desc)
p.add_argument('-V', '--verbose',
action='store_true',
help='Print additional information on standard out.')
p.add_argument('path',
metavar='PATH',
help='Path of JSON input file with Swagger/OpenAPI specification.')
args = p.parse_args()
url = "https://github.com/swagger-api/swagger-ui/archive/v3.5.0.zip"
browser = OpenApiBrowser(args.path, verbose=args.verbose)
browser.open(url)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment