Skip to content

Commit 7d41a7d

Browse files
committed
add pagination
1 parent 38708e1 commit 7d41a7d

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

backend/app/api/routes/project_user.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import uuid
2-
from fastapi import APIRouter, Depends, HTTPException
2+
from fastapi import APIRouter, Depends, HTTPException, Query
33
from sqlmodel import Session
44
from typing import Annotated
55
from app.api.deps import get_db, verify_user_project_organization
@@ -42,14 +42,22 @@ def add_user(
4242
@router.get("/", response_model=APIResponse[list[ProjectUserPublic]])
4343
def list_project_users(
4444
session: Session = Depends(get_db),
45-
current_user: UserProjectOrg = Depends(verify_user_project_organization)
45+
current_user: UserProjectOrg = Depends(verify_user_project_organization),
46+
skip: int = Query(0, ge=0),
47+
limit: int = Query(100, ge=1, le=100)
4648
):
4749
"""
4850
Get all users in a project.
4951
"""
50-
users = get_users_by_project(session, current_user.project_id)
51-
return APIResponse.success_response(users)
52+
users, total_count = get_users_by_project(session, current_user.project_id, skip, limit)
53+
54+
metadata = {
55+
"total_count": total_count,
56+
"limit": limit,
57+
"skip": skip
58+
}
5259

60+
return APIResponse.success_response(data=users, metadata=metadata)
5361

5462
# Remove a user from a project
5563
@router.delete("/{user_id}", response_model=APIResponse[Message])

backend/app/crud/project_user.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import uuid
2-
from sqlmodel import Session, select, delete
2+
from sqlmodel import Session, select, delete, func
33
from app.models import ProjectUser, ProjectUserPublic, User
44
from datetime import datetime
55

@@ -60,10 +60,23 @@ def remove_user_from_project(session: Session, project_id: uuid.UUID, user_id: u
6060
session.commit()
6161

6262

63-
def get_users_by_project(session: Session, project_id: uuid.UUID) -> list[ProjectUserPublic]:
63+
def get_users_by_project(
64+
session: Session, project_id: uuid.UUID, skip: int = 0, limit: int = 100
65+
) -> tuple[list[ProjectUserPublic], int]:
6466
"""
65-
Returns all users in a given project.
67+
Returns paginated users in a given project along with the total count.
6668
"""
67-
statement = select(ProjectUser).where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False)
69+
count_statement = select(func.count()).select_from(ProjectUser).where(
70+
ProjectUser.project_id == project_id, ProjectUser.is_deleted == False
71+
)
72+
total_count = session.exec(count_statement).one()
73+
74+
statement = (
75+
select(ProjectUser)
76+
.where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False)
77+
.offset(skip)
78+
.limit(limit)
79+
)
6880
users = session.exec(statement).all()
69-
return [ProjectUserPublic.model_validate(user) for user in users]
81+
82+
return [ProjectUserPublic.model_validate(user) for user in users], total_count

backend/app/tests/crud/test_project_user.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,7 @@ def test_get_users_by_project(db: Session) -> None:
129129
project_user_crud.add_user_to_project(db, project.id, user1.id)
130130
project_user_crud.add_user_to_project(db, project.id, user2.id)
131131

132-
users = project_user_crud.get_users_by_project(db, project.id)
132+
users, total_count = project_user_crud.get_users_by_project(db, project.id, skip=0, limit=10)
133+
134+
assert total_count == 2
133135
assert len(users) == 2

backend/app/utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ class APIResponse(BaseModel, Generic[T]):
2424
success: bool
2525
data: Optional[T] = None
2626
error: Optional[str] = None
27+
metadata: Optional[Dict[str, Any]] = None
2728

2829
@classmethod
29-
def success_response(cls, data: T) -> "APIResponse[T]":
30-
return cls(success=True, data=data, error=None)
30+
def success_response(cls, data: T, metadata: Optional[Dict[str, Any]] = None) -> "APIResponse[T]":
31+
return cls(success=True, data=data, error=None, metadata=metadata)
3132

3233
@classmethod
3334
def failure_response(cls, error: str) -> "APIResponse[None]":

0 commit comments

Comments
 (0)