diff options
Diffstat (limited to 'api/v1/util.py')
-rw-r--r-- | api/v1/util.py | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/api/v1/util.py b/api/v1/util.py new file mode 100644 index 0000000..f2dddad --- /dev/null +++ b/api/v1/util.py @@ -0,0 +1,61 @@ +import typing + +from flask import jsonify, request +from peewee import Model, ModelSelect + +_V = typing.TypeVar('_V', bound=Model) + + +def model_paginator(model: typing.Type[_V], mapping: typing.Callable[[_V], dict]): + return query_paginator(model.select(), mapping) + + +class ConstrainFailed(Exception): + def __init__(self, description: str, parameter: str, reason: str): + self.reason = reason + self.parameter = parameter + self.description = description + + +def try_or_fail_contrain(func: typing.Callable, parameter: str, value=None, default=None): + if value is None: + value = request.args.get(parameter, default) + try: + return func(value) + except BaseException as e: + func = func.__name__ + raise ConstrainFailed(f"{func}({parameter}) failed with an exception", parameter, + f"{func} raised {type(e).__name__}") + + +def max_constrain(max: int, parameter: str, value=None, default=None): + value = try_or_fail_contrain(int, parameter, value, default) + if value > max: + raise ConstrainFailed(f"requested {parameter} too big", parameter, f'> {max}') + return value + + +def min_constrain(min: int, parameter: str, value=None, default=None): + value = try_or_fail_contrain(int, parameter, value, default) + if value < min: + raise ConstrainFailed(f"requested {parameter} too big", parameter, f'< {min}') + return value + + +def query_paginator(query: ModelSelect, mapping: typing.Callable[[_V], dict]): + size = try_or_fail_contrain(int, 'size', default='50') + max_constrain(50, 'size', size) + min_constrain(2, 'size', size) + offset = try_or_fail_contrain(int, 'offset', default='0') + min_constrain(0, 'offset', offset) + res = [] + for obj in query.limit(size + 1).offset(offset): + res.append(mapping(obj)) + more = len(res) > size + if more: + res[-1:] = [] + return jsonify({ + 'more': more, + 'next': offset + size if more else None, + 'results': res, + }) |