[wip][responses] allow to change return format with "format" route argument, add ODSResponse

This commit is contained in:
Maxime Alves LIRMM@home 2021-12-01 21:16:19 +01:00
parent 20cada4fa0
commit a8e5cfc0ff
2 changed files with 68 additions and 27 deletions

View File

@ -17,7 +17,7 @@ import yaml
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from halfapi.lib import acl from halfapi.lib import acl
from halfapi.lib.responses import ORJSONResponse from halfapi.lib.responses import ORJSONResponse, ODSResponse
from halfapi.lib.router import read_router from halfapi.lib.router import read_router
from halfapi.lib.constants import VERBS from halfapi.lib.constants import VERBS
@ -46,39 +46,48 @@ class NoDomainsException(Exception):
def route_decorator(fct: FunctionType, ret_type: str = 'json') -> Coroutine: def route_decorator(fct: FunctionType, ret_type: str = 'json') -> Coroutine:
""" Returns an async function that can be mounted on a router """ Returns an async function that can be mounted on a router
""" """
if ret_type == 'json': @wraps(fct)
@wraps(fct) @acl.args_check
@acl.args_check async def wrapped(request, *args, **kwargs):
async def wrapped(request, *args, **kwargs): fct_args_spec = inspect.getfullargspec(fct).args
fct_args_spec = inspect.getfullargspec(fct).args fct_args = request.path_params.copy()
fct_args = request.path_params.copy()
if 'halfapi' in fct_args_spec: if 'halfapi' in fct_args_spec:
fct_args['halfapi'] = { fct_args['halfapi'] = {
'user': request.user if 'user': request.user if
'user' in request else None, 'user' in request else None,
'config': request.scope['config'], 'config': request.scope['config'],
'domain': request.scope['domain'], 'domain': request.scope['domain'],
} }
if 'data' in fct_args_spec: if 'data' in fct_args_spec:
fct_args['data'] = kwargs.get('data') fct_args['data'] = kwargs.get('data')
try: """ If format argument is specified (either by get or by post param)
"""
ret_type = fct_args.get('data', {}).get('format', 'json')
try:
if ret_type == 'json':
return ORJSONResponse(fct(**fct_args)) return ORJSONResponse(fct(**fct_args))
except NotImplementedError as exc: elif ret_type == 'ods':
raise HTTPException(501) from exc res = fct(**fct_args)
except Exception as exc: assert isinstance(res, list)
# TODO: Write tests for elt in res:
if not isinstance(exc, HTTPException): assert isinstance(elt, dict)
raise HTTPException(500) from exc
raise exc
return ODSResponse(res)
else: else:
raise Exception('Return type not available') raise NotImplementedError
except NotImplementedError as exc:
raise HTTPException(501) from exc
except Exception as exc:
# TODO: Write tests
if not isinstance(exc, HTTPException):
raise HTTPException(500) from exc
raise exc
return wrapped return wrapped

View File

@ -13,10 +13,13 @@ Classes :
- PlainTextResponse - PlainTextResponse
- ServiceUnavailableResponse - ServiceUnavailableResponse
- UnauthorizedResponse - UnauthorizedResponse
- ODSResponse
""" """
from datetime import date
import decimal import decimal
import typing import typing
from io import BytesIO
import orjson import orjson
# asgi framework # asgi framework
@ -110,3 +113,32 @@ class HJSONResponse(ORJSONResponse):
""" """
def render(self, content: typing.Generator): def render(self, content: typing.Generator):
return super().render(list(content)) return super().render(list(content))
class ODSResponse(Response):
def __init__(self, d_rows: typing.List[typing.Dict]):
try:
import pyexcel as pe
except ImportError:
""" ODSResponse is not handled
"""
super().__init__(content=
'pyexcel is not installed, ods format not available'
)
return
with BytesIO() as ods_file:
# rows.insert(0, rownames)
self.sheet = pe.Sheet(d_rows)
self.sheet.save_to_memory(
file_type='ods',
stream=ods_file)
filename = f'{date.today()}.ods'
super().__init__(
content=ods_file.getvalue(),
headers={
'Content-Type': 'application/vnd.oasis.opendocument.spreadsheet; charset=UTF-8',
'Content-Disposition': f'attachment; filename="{filename}"'},
status_code = 200)