Skip to content

Commit 228e0a4

Browse files
committed
fix: admin query param parsing
1 parent 4bc28ee commit 228e0a4

13 files changed

Lines changed: 263 additions & 189 deletions

File tree

app/db/crud/admin.py

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,6 @@ async def get_admins(
267267
List[Admin] | tuple[list[Admin], int, int, int]: A list of admin objects or tuple with counts.
268268
"""
269269
params = query
270-
base_query = select(Admin)
271-
if params.ids:
272-
base_query = base_query.where(Admin.id.in_(params.ids))
273-
if params.usernames:
274-
base_query = base_query.where(Admin.username.in_(params.usernames))
275-
if params.username:
276-
base_query = base_query.where(Admin.username.ilike(f"%{params.username}%"))
277270

278271
total = None
279272
active = None
@@ -285,23 +278,19 @@ async def get_admins(
285278
func.sum(case((Admin.is_disabled.is_(False), 1), else_=0)).label("active"),
286279
func.sum(case((Admin.is_disabled.is_(True), 1), else_=0)).label("disabled"),
287280
)
281+
if params.ids:
282+
counts_stmt = counts_stmt.where(Admin.id.in_(params.ids))
283+
if params.usernames:
284+
counts_stmt = counts_stmt.where(Admin.username.in_(params.usernames))
288285
if params.username:
289286
counts_stmt = counts_stmt.where(Admin.username.ilike(f"%{params.username}%"))
287+
290288
result = await db.execute(counts_stmt)
291289
row = result.one()
292290
total = row.total or 0
293291
active = row.active or 0
294292
disabled = row.disabled or 0
295293

296-
stmt = base_query
297-
if params.sort:
298-
stmt = stmt.order_by(*[_build_admin_sort_clause(sort_option) for sort_option in params.sort])
299-
300-
if params.offset:
301-
stmt = stmt.offset(params.offset)
302-
if params.limit:
303-
stmt = stmt.limit(params.limit)
304-
305294
if compact:
306295
users_count_subq = (
307296
select(User.admin_id.label("admin_id"), func.count(User.id).label("total_users"))
@@ -317,25 +306,36 @@ async def get_admins(
317306
.subquery()
318307
)
319308

320-
stmt = (
321-
select(
322-
Admin,
323-
func.coalesce(users_count_subq.c.total_users, 0).label("total_users"),
324-
func.coalesce(reset_usage_subq.c.reseted_usage, 0).label("reseted_usage"),
325-
)
326-
.outerjoin(users_count_subq, users_count_subq.c.admin_id == Admin.id)
327-
.outerjoin(reset_usage_subq, reset_usage_subq.c.admin_id == Admin.id)
309+
stmt = select(
310+
Admin,
311+
func.coalesce(users_count_subq.c.total_users, 0).label("total_users"),
312+
func.coalesce(reset_usage_subq.c.reseted_usage, 0).label("reseted_usage"),
328313
)
329-
if params.username:
330-
stmt = stmt.where(Admin.username.ilike(f"%{params.username}%"))
331-
if params.sort:
332-
stmt = stmt.order_by(*[_build_admin_sort_clause(sort_option) for sort_option in params.sort])
333-
if params.offset:
334-
stmt = stmt.offset(params.offset)
335-
if params.limit:
336-
stmt = stmt.limit(params.limit)
337-
338-
rows = (await db.execute(stmt)).all()
314+
stmt = stmt.outerjoin(users_count_subq, users_count_subq.c.admin_id == Admin.id)
315+
stmt = stmt.outerjoin(reset_usage_subq, reset_usage_subq.c.admin_id == Admin.id)
316+
else:
317+
stmt = select(Admin)
318+
319+
# Apply filters consistently
320+
if params.ids:
321+
stmt = stmt.where(Admin.id.in_(params.ids))
322+
if params.usernames:
323+
stmt = stmt.where(Admin.username.in_(params.usernames))
324+
if params.username:
325+
stmt = stmt.where(Admin.username.ilike(f"%{params.username}%"))
326+
327+
# Apply sorting
328+
if params.sort:
329+
stmt = stmt.order_by(*[_build_admin_sort_clause(sort_option) for sort_option in params.sort])
330+
331+
# Apply pagination
332+
if params.offset is not None:
333+
stmt = stmt.offset(params.offset)
334+
if params.limit is not None:
335+
stmt = stmt.limit(params.limit)
336+
337+
if compact:
338+
rows = (await db.execute(stmt)).unique().all()
339339
admins = []
340340
for admin, total_users, reseted_usage in rows:
341341
lifetime_used_traffic = int((reseted_usage or 0) + (admin.used_traffic or 0))
@@ -359,14 +359,10 @@ async def get_admins(
359359
lifetime_used_traffic=lifetime_used_traffic,
360360
)
361361
)
362-
363-
if return_with_count:
364-
return admins, total, active, disabled
365-
return admins
366-
367-
admins = (await db.execute(stmt)).scalars().all()
368-
for admin in admins:
369-
await load_admin_attrs(admin)
362+
else:
363+
admins = list((await db.execute(stmt)).scalars().all())
364+
for admin in admins:
365+
await load_admin_attrs(admin)
370366

371367
if return_with_count:
372368
return admins, total, active, disabled
@@ -401,13 +397,13 @@ async def get_admins_simple(
401397

402398
# Get count BEFORE pagination (always)
403399
count_stmt = select(func.count()).select_from(stmt.subquery())
404-
total = (await db.execute(count_stmt)).scalar()
400+
total = (await db.execute(count_stmt)).scalar() or 0
405401

406402
# Apply pagination or safety limit
407403
if not query.all:
408-
if query.offset:
404+
if query.offset is not None:
409405
stmt = stmt.offset(query.offset)
410-
if query.limit:
406+
if query.limit is not None:
411407
stmt = stmt.limit(query.limit)
412408
else:
413409
stmt = stmt.limit(10000) # Safety limit when all=true

app/routers/dependencies/_common.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from inspect import Parameter, Signature
33
from typing import Any, Callable, cast
44

5-
from fastapi import HTTPException, status
5+
from fastapi import HTTPException, Query, status
6+
from fastapi.params import Query as QueryParam
67
from pydantic import BaseModel, ValidationError
78
from pydantic_core import PydanticUndefined
89

@@ -46,6 +47,11 @@ def make_query_dependency(
4647
default = Parameter.empty
4748
else:
4849
default = field_info.default
50+
51+
# Ensure everything is explicitly a Query parameter to prevent Orval body generation
52+
if not isinstance(default, (QueryParam, ParameterOverride)) and default is not Parameter.empty:
53+
default = Query(default)
54+
4955
parameters.append(
5056
Parameter(
5157
field_name,

app/routers/dependencies/admin.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@
44

55
from ._common import make_query_dependency, query_param
66

7-
87
get_admin_list_query = make_query_dependency(
98
AdminListQuery,
109
field_overrides={
1110
"ids": Query(None),
1211
"usernames": Query(None),
12+
"username": Query(None),
13+
"offset": Query(None),
14+
"limit": Query(None),
15+
"sort": query_param(str | None, None),
1316
},
1417
)
1518
get_admin_simple_list_query = make_query_dependency(
1619
AdminSimpleListQuery,
1720
field_overrides={
1821
"ids": Query(None),
1922
"usernames": Query(None),
23+
"search": Query(None),
24+
"offset": Query(None),
25+
"limit": Query(None),
2026
"sort": query_param(str | None, None),
27+
"all": Query(False),
2128
},
2229
)
2330
get_admin_usage_query = make_query_dependency(

app/routers/dependencies/client_template.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,24 @@
44

55
from ._common import make_query_dependency, query_param
66

7-
87
get_client_template_list_query = make_query_dependency(
98
ClientTemplateListQuery,
10-
field_overrides={"ids": Query(None)},
9+
field_overrides={
10+
"ids": Query(None),
11+
"template_type": Query(None),
12+
"offset": Query(None),
13+
"limit": Query(None),
14+
},
1115
)
1216
get_client_template_simple_list_query = make_query_dependency(
1317
ClientTemplateSimpleListQuery,
14-
field_overrides={"ids": Query(None), "sort": query_param(str | None, None)},
18+
field_overrides={
19+
"ids": Query(None),
20+
"template_type": Query(None),
21+
"offset": Query(None),
22+
"limit": Query(None),
23+
"search": Query(None),
24+
"sort": query_param(str | None, None),
25+
"all": Query(False),
26+
},
1527
)

app/routers/dependencies/core.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@
44

55
from ._common import make_query_dependency, query_param
66

7-
87
get_core_list_query = make_query_dependency(
98
CoreListQuery,
10-
field_overrides={"ids": Query(None)},
9+
field_overrides={
10+
"ids": Query(None),
11+
"offset": Query(None),
12+
"limit": Query(None),
13+
},
1114
)
1215
get_core_simple_list_query = make_query_dependency(
1316
CoreSimpleListQuery,
14-
field_overrides={"ids": Query(None), "sort": query_param(str | None, None)},
17+
field_overrides={
18+
"ids": Query(None),
19+
"offset": Query(None),
20+
"limit": Query(None),
21+
"search": Query(None),
22+
"sort": query_param(str | None, None),
23+
"all": Query(False),
24+
},
1525
)

app/routers/dependencies/group.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@
44

55
from ._common import make_query_dependency, query_param
66

7-
87
get_group_list_query = make_query_dependency(
98
GroupListQuery,
10-
field_overrides={"ids": Query(None)},
9+
field_overrides={
10+
"ids": Query(None),
11+
"offset": Query(None),
12+
"limit": Query(None),
13+
},
1114
)
1215
get_group_simple_list_query = make_query_dependency(
1316
GroupSimpleListQuery,
14-
field_overrides={"ids": Query(None), "sort": query_param(str | None, None)},
17+
field_overrides={
18+
"ids": Query(None),
19+
"offset": Query(None),
20+
"limit": Query(None),
21+
"search": Query(None),
22+
"sort": query_param(str | None, None),
23+
"all": Query(False),
24+
},
1525
)

app/routers/dependencies/host.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
from ._common import make_query_dependency
66

7-
87
get_host_list_query = make_query_dependency(
98
HostListQuery,
10-
field_overrides={"ids": Query(None)},
9+
field_overrides={
10+
"ids": Query(None),
11+
"offset": Query(0),
12+
"limit": Query(0),
13+
},
1114
)

app/routers/dependencies/node.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,54 @@
77
NodeStatsPeriodQuery,
88
NodeUsageQuery,
99
)
10+
from app.models.stats import Period
1011

1112
from ._common import make_query_dependency, query_param
1213

13-
1414
get_node_usage_query = make_query_dependency(
1515
NodeUsageQuery,
1616
field_overrides={
17+
"period": Query(Period.hour),
18+
"node_id": Query(None),
1719
"start": Query(None, examples=["2024-01-01T00:00:00+03:30"]),
1820
"end": Query(None, examples=["2024-01-31T23:59:59+03:30"]),
1921
},
2022
)
2123
get_node_stats_period_query = make_query_dependency(
2224
NodeStatsPeriodQuery,
2325
field_overrides={
26+
"period": Query(Period.hour),
2427
"start": Query(None, examples=["2024-01-01T00:00:00+03:30"]),
2528
"end": Query(None, examples=["2024-01-31T23:59:59+03:30"]),
2629
},
2730
)
28-
get_node_clear_usage_query = make_query_dependency(NodeClearUsageQuery)
31+
get_node_clear_usage_query = make_query_dependency(
32+
NodeClearUsageQuery,
33+
field_overrides={
34+
"start": Query(None),
35+
"end": Query(None),
36+
},
37+
)
2938
get_node_list_query = make_query_dependency(
3039
NodeListQuery,
3140
field_overrides={
32-
"status": Query(None),
41+
"core_id": Query(None),
42+
"offset": Query(None),
43+
"limit": Query(None),
3344
"ids": Query(None),
45+
"status": Query(None),
46+
"enabled": Query(False),
47+
"search": Query(None),
3448
},
3549
)
3650
get_node_simple_list_query = make_query_dependency(
3751
NodeSimpleListQuery,
38-
field_overrides={"ids": Query(None), "sort": query_param(str | None, None)},
52+
field_overrides={
53+
"ids": Query(None),
54+
"offset": Query(None),
55+
"limit": Query(None),
56+
"search": Query(None),
57+
"sort": query_param(str | None, None),
58+
"all": Query(False),
59+
},
3960
)

app/routers/dependencies/subscription.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from fastapi import Query
22

3+
from app.models.stats import Period
34
from app.models.subscription import SubscriptionUsageQuery
45

56
from ._common import make_query_dependency
67

7-
88
get_subscription_usage_query = make_query_dependency(
99
SubscriptionUsageQuery,
1010
field_overrides={
11+
"period": Query(Period.hour),
1112
"start": Query(None, examples=["2024-01-01T00:00:00+03:30"]),
1213
"end": Query(None, examples=["2024-01-31T23:59:59+03:30"]),
1314
},

0 commit comments

Comments
 (0)