Python: rest_api

This commit is contained in:
Dmitry Kokorin 2021-07-05 17:40:45 +03:00
parent 5f65385207
commit 6d20598706

View file

@ -1,11 +1,56 @@
import json import json
from collections import defaultdict from collections import defaultdict
from copy import copy
from functools import wraps from functools import wraps
from operator import attrgetter from operator import attrgetter
VALID_HTTP_METHODS = ['GET', 'POST'] VALID_HTTP_METHODS = ['GET', 'POST']
class ApiMethodRegistry:
def __init__(self):
self.methods = {k: {} for k in VALID_HTTP_METHODS}
def register_url(self, url, http_method):
def actual_decorator(f):
@wraps(f)
def _wrapper(f_self, payload):
if payload:
payload = json.loads(payload)
# It would be essential to validate JSON schema here
f_result = f(f_self, payload)
return json.dumps(f_result,
default=lambda obj: obj.__dict__,
sort_keys=True)
self.methods[http_method][url] = _wrapper
return actual_decorator
class RestApiBase:
registry = ApiMethodRegistry()
def _process_request(self, method, url, payload):
methods = self.registry.methods[method]
# It would be natural to handle exceptions here
if url in methods.keys():
return methods[url](self, payload)
def get(self, url, payload=None):
return self._process_request('GET', url, payload)
def post(self, url, payload=None):
return self._process_request('POST', url, payload)
class User: class User:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
@ -40,59 +85,26 @@ class User:
return user return user
class MethodRegistry: class RestAPI(RestApiBase):
def __init__(self):
self.methods = {k: {} for k in VALID_HTTP_METHODS }
def register_url(self, url, http_method): registry = copy(RestApiBase.registry)
def actual_decorator(f):
@wraps(f)
def _wrapper(f_self, payload):
if payload:
payload = json.loads(payload)
#It would be essential to validate JSON schema here
f_result = f(f_self, payload)
return json.dumps(f_result, default=lambda obj: obj.__dict__, sort_keys=True)
self.methods[http_method][url] = _wrapper
return actual_decorator
class RestAPI:
registry = MethodRegistry()
def __init__(self, database=None): def __init__(self, database=None):
self._users = {user['name'] : User.from_dict(user) for user in database['users']} if database else {} self._users = {user['name']: User.from_dict(user)
for user in database['users']} if database else {}
def _process_request(self, method, url, payload):
methods = self.registry.methods[method]
#It would be natural to handle exceptions here
if url in methods.keys():
return methods[url](self, payload)
def get(self, url, payload=None):
return self._process_request('GET', url, payload)
def post(self, url, payload=None):
return self._process_request('POST', url, payload)
@registry.register_url('/users', 'GET') @registry.register_url('/users', 'GET')
def users(self, payload=None): def users(self, payload=None):
if payload: if payload:
requested_users = payload['users'] requested_user_names = payload['users']
response = [user for user in self._users.values() if user.name in requested_users] users = [user for user in self._users.values()
if user.name in requested_user_names]
else: else:
response = [user for user in self._users.values()] users = [user for user in self._users.values()]
return {'users': response} users = sorted(users, key=attrgetter('name'))
return {'users': users}
@registry.register_url('/add', 'POST') @registry.register_url('/add', 'POST')
def add(self, payload=None): def add(self, payload=None):
@ -112,5 +124,5 @@ class RestAPI:
self._users[lender].lend(borrower, amount) self._users[lender].lend(borrower, amount)
self._users[borrower].borrow(lender, amount) self._users[borrower].borrow(lender, amount)
return {'users': sorted([self._users[lender], self._users[borrower]], key=attrgetter('name'))} return {'users': sorted([self._users[lender], self._users[borrower]],
key=attrgetter('name'))}