summaryrefslogtreecommitdiff
path: root/api/v1/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'api/v1/util.py')
-rw-r--r--api/v1/util.py61
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,
+ })