Source code for aioli.controller.decorators

# -*- coding: utf-8 -*-

from functools import wraps

from starlette.requests import Request
from starlette.responses import Response

from aioli.exceptions import AioliException

from .consts import Method, RequestProp
from .registry import RouteRegistry


[docs]def route(path, method, description=None): """Prepares route registration, and performs handler injection. :param path: Handler path, relative to application and package paths :param method: HTTP Method :param description: Endpoint description :return: Route handler """ def wrapper(fn): @wraps(fn) async def handler(*args, **kwargs): return await fn(*args, **kwargs) if not isinstance(method, Method): raise AioliException( f"Invalid HTTP method supplied in @route for handler: {handler}. " f"Must be of type: {Method.__module__}.{Method.__name__}" ) stack = RouteRegistry.get_stack(handler) # Adds the handler for registration once the loop is ready. stack.add_route(handler, path, method.value, description) return handler return wrapper
[docs]def takes(props=None, **schemas): """Takes a list of schemas used to validate and transform parts of a request object. The selected parts are injected into the route handler as arguments. :param props: List of `Pluck` targets :param schemas: list of schemas (kwargs) :return: Route handler """ props = props or [] schemas = {part: schema() for part, schema in schemas.items()} def wrapper(fn): @wraps(fn) async def handler(*args, **kwargs): args_new = list(args) request = kwargs["request"] if "request" in kwargs else args_new.pop(1) for prop in props: value = RequestProp(prop).value assert "." in value target = request for member in value.split("."): target = getattr(target, member) kwargs.update({RequestProp(prop).name: target}) if "headers" in schemas: kwargs.update({"headers": schemas["headers"].load(request.headers)}) if "path" in schemas: kwargs.update(schemas["path"].load(request.path_params)) if "body" in schemas: payload = await request.json() kwargs.update({"body": schemas["body"].load(payload)}) if "query" in schemas: kwargs.update({"query": schemas["query"].load(request.query_params)}) return await fn(*args_new, **kwargs) stack = RouteRegistry.get_stack(handler) # Add the provided schemas to the RouteStack stack.schemas.update(schemas) return handler return wrapper
[docs]def returns(schema_cls=None, status=200, many=False): """Returns a transformed and serialized Response :param schema_cls: Marshmallow.Schema class :param status: Return status (on success) :param many: Whether to return a list or single object :return: Response """ schema = schema_cls(many=many) if schema_cls else None def wrapper(fn): @wraps(fn) async def handler(*args, **kwargs): args_new = list(args) # Remove `Request` object from args (in case it wasn't consumed by an `input_local`). if len(args) > 1 and isinstance(args[1], Request): args_new.pop(1) rv = await fn(*args_new, **kwargs) data = ( schema.dumps(rv, indent=4, ensure_ascii=False).encode("utf8") if schema else None ) # Return HTTP encoded JSON response return Response( content=data, status_code=status, headers={"content-type": "application/json"}, ) stack = RouteRegistry.get_stack(handler) # Add the `response` schema to this route handler's stack stack.schemas.update({"response": schema}) return handler return wrapper