Sitemap generator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

133 lines
4.6 KiB

import inspect
from collections import OrderedDict
import json
from aiohttp.http_exceptions import HttpBadRequest
from aiohttp.web_exceptions import HTTPMethodNotAllowed
from aiohttp.web_request import Request
from aiohttp.web_response import Response
from aiohttp.web_routedef import UrlDispatcher
from db import session, get_or_create
from models import Domain, DomainGroup
DEFAULT_METHODS = ('GET', 'POST', 'PUT', 'DELETE')
class RestEndpoint:
def __init__(self):
self.methods = {}
for method_name in DEFAULT_METHODS:
method = getattr(self, method_name.lower(), None)
if method:
self.register_method(method_name, method)
def register_method(self, method_name, method):
self.methods[method_name.upper()] = method
async def dispatch(self, request: Request):
method = self.methods.get(request.method.upper())
if not method:
raise HTTPMethodNotAllowed('', DEFAULT_METHODS)
wanted_args = list(inspect.signature(method).parameters.keys())
available_args = request.match_info.copy()
available_args.update({'request': request})
unsatisfied_args = set(wanted_args) - set(available_args.keys())
if unsatisfied_args:
# Expected match info that doesn't exist
raise HttpBadRequest('')
return await method(**{arg_name: available_args[arg_name] for arg_name in wanted_args})
class DomainEndpoint(RestEndpoint):
def __init__(self, resource):
super().__init__()
self.resource = resource
async def get(self) -> Response:
data = []
domains = session.query(Domain).all()
for instance in self.resource.collection.values():
data.append(self.resource.render(instance))
return Response(status=200, body=self.resource.encode({
'domains': [
{
'id': domain.id, 'title': domain.domain,
'groups': [{'id': group.id, 'name': group.name} for group in domain.groups]
} for domain in session.query(Domain).all()
]}), content_type='application/json')
async def post(self, request):
data = await request.json()
domain, _created = get_or_create(Domain, domain=data['domain'])
return Response(status=200, body=self.resource.encode(
{'id': domain.id, 'domain': domain.domain, 'created': _created},
), content_type='application/json')
class DomainGroupEndpoint(RestEndpoint):
def __init__(self, resource):
super().__init__()
self.resource = resource
async def get(self) -> Response:
return Response(status=200, body=self.resource.encode({
'domain_groups': [
{
'id': group.id, 'name': group.name,
'domains': [{'id': domain.id, 'name': domain.domain} for domain in group.domains]
} for group in session.query(DomainGroup).all()
]}), content_type='application/json')
async def post(self, request):
data = await request.json()
group, _created = get_or_create(session, DomainGroup, name=data['name'])
domains = []
if data.get('domains'):
for domain_el in data.get('domains'):
domain, _domain_created = get_or_create(session, Domain, domain=domain_el)
domains.append({'id': domain.id, 'domain': domain_el, 'created': _domain_created})
return Response(
status=200,
body=self.resource.encode({
'id': group.id,
'name': group.name,
'domains': domains,
'created': _created
}), content_type='application/json')
class RestResource:
def __init__(self, notes, factory, collection, properties, id_field):
self.notes = notes
self.factory = factory
self.collection = collection
self.properties = properties
self.id_field = id_field
self.domain_endpoint = DomainEndpoint(self)
self.domain_groups_endpoint = DomainGroupEndpoint(self)
def register(self, router: UrlDispatcher):
router.add_route('*', '/{domains}'.format(notes=self.notes), self.domain_endpoint.dispatch)
router.add_route('*', '/{domain_groups}'.format(notes=self.notes), self.domain_groups_endpoint.dispatch)
def render(self, instance):
return OrderedDict((notes, getattr(instance, notes)) for notes in self.properties)
@staticmethod
def encode(data):
return json.dumps(data, indent=4).encode('utf-8')
def render_and_encode(self, instance):
return self.encode(self.render(instance))