Source code for ourobori.services.apis.decorators

"""
This module contains the decorators to be used for the api-methods in your
``service.py``.
They are used to transform the request-data and the response-data using
``pydantic``.
The :func:`input_transform` requires a `BaseModel` defined in your
``schemata.py`` as parameter.
Calling the decorated api-method then converts the request-data to an instance
of your passed Model-class.
The :func:`output_transform` also requires a `BaseModel` defined in your
``schemata.py`` as parameter.
Calling the decorated api-method then converts the returned value of the
api-method to a json-response based on the attributes of the passed
Model-class passed to the decorator.
"""


import asyncio
from functools import wraps

import aiohttp
from pydantic import BaseModel
from pydantic import ValidationError

from ourobori.services.errors import InvalidInputException
from ourobori.services.errors import InvalidOutputException


[docs]def input_transform(schema: BaseModel): """ Decorator to be used in api-methods to convert the request-data to an instance of the passed ``schema``. Also adds the api_id for the request and the api_params (as an object of the passed schema) extracted from the request-params to the request-object to be used in the decorated api. Parameters: schema: The pydantic-Model to use to convert the request-data passed to the decorated api-method. Returns: decorator: decorated version of the function """ def decorator(func): @wraps(func) async def function_wrapper(request, *args, **kwargs): body = await request.content.read() decoded = body.decode('utf-8') if not decoded: raise ValidationError('No request-body found!') try: params = schema.parse_raw(decoded) except ValidationError as err: raise InvalidInputException() request.api_id = hash(f'{request.remote}{params.json()}') request.api_params = params request.log = request.app.logger.bind(api_id=request.api_id) request.log.info(f'api received request: {params}') result = await func(request, *args, **kwargs) return result return function_wrapper return decorator
[docs]def output_transform(schema: BaseModel): """ Decorator to be used in api-methods to convert the response-data of the decorated api-method to a json based on the passed ``schema``. Parameters: schema: The pydantic-Model to use to convert the response data of the decorated api-method. Returns: decorator: decorated version of the function """ def decorator(func): @wraps(func) async def function_wrapper(request, *args, **kwargs): service_result = await func(request, *args, **kwargs) try: if not isinstance(service_result, schema): output = schema(**service_result).json() else: output = service_result.json() except Exception as err: raise InvalidOutputException() request.log.info(f'api returned result: {output}') return aiohttp.web.Response(body=output, content_type='application/json') return function_wrapper return decorator
__all__ = ['input_transform', 'output_transform']