make it so
This commit is contained in:
10
README.md
Normal file
10
README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# API builder
|
||||||
|
|
||||||
|
A simple script to autmatically generate Python API clients from JSON-based
|
||||||
|
Swagger descriptions. Works with a few selected Swagger definitions, but I
|
||||||
|
haven’t tested it on many different formats. Very much just a Proof of Concept
|
||||||
|
that happens to be useful.
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
Have fun!
|
71
api_builder.py
Normal file
71
api_builder.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import inflection
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
with open('prelude.py') as f:
|
||||||
|
prelude = f.read()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reserved = dir(__builtins__)
|
||||||
|
|
||||||
|
|
||||||
|
def download(url):
|
||||||
|
return requests.get(url).json()
|
||||||
|
|
||||||
|
|
||||||
|
def mkname(method, name, params):
|
||||||
|
singular = inflection.singularize(name)
|
||||||
|
if method == 'get' and 'id_' not in params:
|
||||||
|
return 'list_{}'.format(name)
|
||||||
|
elif method == 'get':
|
||||||
|
return singular
|
||||||
|
elif method == 'post':
|
||||||
|
return 'create_{}'.format(singular)
|
||||||
|
elif method == 'put' or method == 'PATCH':
|
||||||
|
return 'update_{}'.format(singular)
|
||||||
|
elif method == 'delete':
|
||||||
|
return 'delete_{}'.format(singular)
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def mkparam(param):
|
||||||
|
param = inflection.underscore(param)
|
||||||
|
if param in reserved:
|
||||||
|
return "{}_".format(param)
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_print(name, data):
|
||||||
|
info = data['info']
|
||||||
|
r = re.compile('\\/\\{([^\\}]*)\\}')
|
||||||
|
r2 = re.compile('\\{([^\\}]*)\\}')
|
||||||
|
print(prelude)
|
||||||
|
print('class {}(Client):'.format(name))
|
||||||
|
print(' """{}\n\n {}"""'.format(info['title'], info['description']))
|
||||||
|
for path, fns in data['paths'].items():
|
||||||
|
for method, body in fns.items():
|
||||||
|
method = method.lower()
|
||||||
|
fmt = r2.sub('{}', path)
|
||||||
|
params = [mkparam(param) for param in r.findall(path)]
|
||||||
|
name = r.sub('', path).replace("/", "_").replace('-', '_')[1:]
|
||||||
|
name = mkname(method, name, params)
|
||||||
|
params = ", {}".format(", ".join(params)) if params else ''
|
||||||
|
print(' def {}(self{}):'.format(name, params))
|
||||||
|
print(' """{}"""'.format(', '.join(body['tags'])))
|
||||||
|
print(' return self.{}("{}"{})\n'.format(method, fmt, params))
|
||||||
|
|
||||||
|
|
||||||
|
def generate(name, url):
|
||||||
|
data = download(url)
|
||||||
|
pretty_print(name, data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("usage: {} <class-name> <url>".format(sys.argv[0]))
|
||||||
|
sys.exit(1)
|
||||||
|
generate(sys.argv[1], sys.argv[2])
|
83
prelude.py
Normal file
83
prelude.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
class Client():
|
||||||
|
"""
|
||||||
|
A generic API client.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, url, verify=True, auth=None):
|
||||||
|
"""
|
||||||
|
Constructs a new Client object.
|
||||||
|
"""
|
||||||
|
self.url = url
|
||||||
|
self.verify = verify
|
||||||
|
self.auth = auth
|
||||||
|
|
||||||
|
def request(self, scheme, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level request interface to client. Takes a HTTP request scheme (lower
|
||||||
|
case!), a URL to request (relative), and optionally data to add to the
|
||||||
|
request. Either returns the JSON body of the request or raises a
|
||||||
|
HttpException.
|
||||||
|
"""
|
||||||
|
url = self.url + url
|
||||||
|
headers = {
|
||||||
|
"User-Agent":
|
||||||
|
"Autogenerated API wrapper (generated by api_builder.py)",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
# this is a nice little hack to make the API nicer
|
||||||
|
# we pass the scheme as string, but have it as attributes in requests
|
||||||
|
fn = requests.__getattribute__(scheme)
|
||||||
|
|
||||||
|
res = fn(url, headers=headers, json=data, params=params, auth=self.auth)
|
||||||
|
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
if not res.content:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
return res.json()
|
||||||
|
except ValueError:
|
||||||
|
return res.content
|
||||||
|
|
||||||
|
def get(self, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level GET request interface to client. Takes a URL to request
|
||||||
|
(relative), and optionally data to add to the request. Either returns
|
||||||
|
the JSON body of the request or raises a HttpException.
|
||||||
|
"""
|
||||||
|
return self.request("get", url, data, params)
|
||||||
|
|
||||||
|
def put(self, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level PUT request interface to client. Takes a URL to request
|
||||||
|
(relative), and optionally data to add to the request. Either returns
|
||||||
|
the JSON body of the request or raises a HttpException.
|
||||||
|
"""
|
||||||
|
return self.request("put", url, data, params)
|
||||||
|
|
||||||
|
def post(self, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level POST request interface to client. Takes a URL to request
|
||||||
|
(relative), and optionally data to add to the request. Either returns
|
||||||
|
the JSON body of the request or raises a HttpException.
|
||||||
|
"""
|
||||||
|
return self.request("post", url, data, params)
|
||||||
|
|
||||||
|
def patch(self, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level PATCH request interface to client. Takes a URL to request
|
||||||
|
(relative), and optionally data to add to the request. Either returns
|
||||||
|
the JSON body of the request or raises a HttpException.
|
||||||
|
"""
|
||||||
|
return self.request("patch", url, data, params)
|
||||||
|
|
||||||
|
def delete(self, url, data=None, params=None):
|
||||||
|
"""
|
||||||
|
Low-level DELETE request interface to client. Takes a URL to request
|
||||||
|
(relative), and optionally data to add to the request. Either returns
|
||||||
|
the JSON body of the request or raises a HttpException.
|
||||||
|
"""
|
||||||
|
return self.request("delete", url, data, params)
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
inflection
|
||||||
|
requests
|
Reference in New Issue
Block a user