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']