This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 551722e Add some validation to the tasks API endpoint
551722e is described below
commit 551722eb2ed9d34a5c5feeae6fda7aa2d7220c7b
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Jul 2 17:17:31 2025 +0100
Add some validation to the tasks API endpoint
---
atr/blueprints/api/api.py | 51 +++++++++++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/atr/blueprints/api/api.py b/atr/blueprints/api/api.py
index 51563be..b874b89 100644
--- a/atr/blueprints/api/api.py
+++ b/atr/blueprints/api/api.py
@@ -41,34 +41,47 @@ class Pagination:
limit: int = 20
+# We implicitly have /api/openapi.json
+
+
[email protected]("/projects")
+@quart_schema.validate_response(list[models.Committee], 200)
+async def projects() -> tuple[list[Mapping], int]:
+ """List all projects in the database."""
+ async with db.session() as data:
+ committees = await data.committee().all()
+ return [committee.model_dump() for committee in committees], 200
+
+
[email protected]("/projects/<name>")
+@quart_schema.validate_response(models.Committee, 200)
+async def projects_name(name: str) -> tuple[Mapping, int]:
+ async with db.session() as data:
+ committee = await
data.committee(name=name).demand(exceptions.NotFound())
+ return committee.model_dump(), 200
+
+
@api.BLUEPRINT.route("/tasks")
@quart_schema.validate_querystring(Pagination)
-async def api_tasks(query_args: Pagination) -> quart.Response:
+async def tasks(query_args: Pagination) -> quart.Response:
+ _pagination_args_validate(query_args)
+ via = models.validate_instrumented_attribute
async with db.session() as data:
statement = (
sqlmodel.select(models.Task)
.limit(query_args.limit)
.offset(query_args.offset)
- .order_by(models.Task.id.desc()) # type: ignore
+ .order_by(via(models.Task.id).desc())
)
paged_tasks = (await data.execute(statement)).scalars().all()
- count = (await
data.execute(sqlalchemy.select(sqlalchemy.func.count(models.Task.id)))).scalar_one()
# type: ignore
- result = {"data": [x.model_dump(exclude={"result"}) for x in
paged_tasks], "count": count}
+ count = (await
data.execute(sqlalchemy.select(sqlalchemy.func.count(via(models.Task.id))))).scalar_one()
+ result = {"data": [paged_task.model_dump(exclude={"result"}) for
paged_task in paged_tasks], "count": count}
return quart.jsonify(result)
[email protected]("/projects/<name>")
-@quart_schema.validate_response(models.Committee, 200)
-async def project_by_name(name: str) -> tuple[Mapping, int]:
- async with db.session() as data:
- committee = await
data.committee(name=name).demand(exceptions.NotFound())
- return committee.model_dump(), 200
-
-
[email protected]("/projects")
-@quart_schema.validate_response(list[models.Committee], 200)
-async def projects() -> tuple[list[Mapping], int]:
- """List all projects in the database."""
- async with db.session() as data:
- committees = await data.committee().all()
- return [committee.model_dump() for committee in committees], 200
+def _pagination_args_validate(query_args: Pagination) -> None:
+ # Users could request any amount using limit=N with arbitrarily high N
+ # We therefore limit the maximum limit to 1000
+ if query_args.limit > 1000:
+ # quart.abort(400, "Limit is too high")
+ raise exceptions.BadRequest("Maximum limit of 1000 exceeded")
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]