[wip][responses] allow to change return format with "format" route argument, add ODSResponse
This commit is contained in:
parent
20cada4fa0
commit
a8e5cfc0ff
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue