implement pagination on all endpoints

This commit is contained in:
johnpccd 2025-05-25 22:20:20 +02:00
parent fe082026c1
commit 80d8a74b12
5 changed files with 76 additions and 25 deletions

View File

@ -234,7 +234,11 @@ The API provides the following main endpoints with their authentication and pagi
#### Team Management
- `/api/v1/teams/*` - **Complete team management (no authentication required)**
- `POST /api/v1/teams` - Create new team
- `GET /api/v1/teams` - List all teams (no pagination - returns all teams)
- `GET /api/v1/teams` - List all teams with **pagination support**
- **Query Parameters:**
- `skip` (default: 0, min: 0) - Number of items to skip
- `limit` (default: 50, min: 1, max: 100) - Number of items per page
- **Response includes:** `teams`, `total`, `skip`, `limit`
- `GET /api/v1/teams/{team_id}` - Get team by ID
- `PUT /api/v1/teams/{team_id}` - Update team
- `DELETE /api/v1/teams/{team_id}` - Delete team
@ -242,7 +246,12 @@ The API provides the following main endpoints with their authentication and pagi
#### User Management
- `/api/v1/users/*` - **Complete user management (no authentication required)**
- `POST /api/v1/users` - Create new user (requires `team_id`)
- `GET /api/v1/users` - List users (no pagination - returns all users, optionally filtered by team)
- `GET /api/v1/users` - List users with **pagination support**
- **Query Parameters:**
- `skip` (default: 0, min: 0) - Number of items to skip
- `limit` (default: 50, min: 1, max: 100) - Number of items per page
- `team_id` (optional) - Filter by team
- **Response includes:** `users`, `total`, `skip`, `limit`
- `GET /api/v1/users/{user_id}` - Get user by ID
- `PUT /api/v1/users/{user_id}` - Update user
- `DELETE /api/v1/users/{user_id}` - Delete user
@ -252,7 +261,11 @@ The API provides the following main endpoints with their authentication and pagi
### 🔐 **Protected Endpoints (API Key Authentication Required)**
#### API Key Management (Authenticated)
- `/api/v1/auth/api-keys` (GET) - List API keys for current user
- `/api/v1/auth/api-keys` (GET) - List API keys for current user with **pagination support**
- **Query Parameters:**
- `skip` (default: 0, min: 0) - Number of items to skip
- `limit` (default: 50, min: 1, max: 100) - Number of items per page
- **Response includes:** `api_keys`, `total`, `skip`, `limit`
- `/api/v1/auth/api-keys/{key_id}` (DELETE) - Revoke API key
- `/api/v1/auth/admin/api-keys/{user_id}` (POST) - Create API key for another user (admin only)
- `/api/v1/auth/verify` - Verify current authentication
@ -271,10 +284,11 @@ The API provides the following main endpoints with their authentication and pagi
- `GET /api/v1/search` - Search images with **pagination support**
- **Query Parameters:**
- `q` (required) - Search query
- `skip` (default: 0, min: 0) - Number of items to skip
- `limit` (default: 10, min: 1, max: 50) - Number of results
- `similarity_threshold` (default: 0.7, min: 0.0, max: 1.0) - Similarity threshold
- `collection_id` (optional) - Filter by collection
- **Response includes:** `results`, `total`, `limit`, `similarity_threshold`, `query`
- **Response includes:** `results`, `total`, `skip`, `limit`, `similarity_threshold`, `query`
- `POST /api/v1/search` - Advanced search with same pagination
### 🔑 **Authentication Model**
@ -289,14 +303,14 @@ A **hybrid authentication model**:
| Endpoint Category | Authentication | Pagination Status | Notes |
|------------------|----------------|------------------|-------|
| **Users Management** | 🔓 **Public** | **Not Implemented** | Complete CRUD operations, no auth required |
| **Teams Management** | 🔓 **Public** | **Not Implemented** | Complete CRUD operations, no auth required |
| **Users Management** | 🔓 **Public** | **Fully Implemented** | `skip`, `limit`, `total` with team filtering |
| **Teams Management** | 🔓 **Public** | **Fully Implemented** | `skip`, `limit`, `total` with proper validation |
| **API Key Creation** | 🔓 **Public** | N/A | Requires `user_id` and `team_id` parameters |
| **Images API** | 🔐 **Protected** | ✅ **Fully Implemented** | `skip`, `limit`, `total` with proper validation |
| **Search API** | 🔐 **Protected** | ✅ **Fully Implemented** | `limit`, `total` with similarity scoring |
| **API Key Management** | 🔐 **Protected** | **Not Implemented** | List/revoke existing keys (small datasets) |
| **Search API** | 🔐 **Protected** | ✅ **Fully Implemented** | `skip`, `limit`, `total` with similarity scoring |
| **API Key Management** | 🔐 **Protected** | **Fully Implemented** | `skip`, `limit`, `total` for user's API keys |
**Note:** Public endpoints (users, teams) don't implement pagination as they typically return small datasets and are designed for management use cases where full data visibility is preferred.
**Note:** All endpoints now implement consistent pagination with `skip` and `limit` parameters for optimal performance and user experience.
Refer to the Swagger UI documentation at `/docs` for detailed endpoint information.
@ -437,7 +451,6 @@ This modular architecture provides several benefits:
### Medium Priority
- [ ] Implement caching layer for frequently accessed embeddings
- [ ] Implement caching for frequently accessed data
- [ ] Consider adding pagination to admin endpoints (users, teams, API keys) if datasets grow large
### Low Priority
- [ ] Move all auth logic to auth module

View File

@ -98,25 +98,43 @@ async def create_api_key_for_user(
async def list_api_keys(
request: Request,
auth_service: AuthServiceDep,
current_user: UserModel = Depends(get_current_user)
current_user: UserModel = Depends(get_current_user),
skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
limit: int = Query(50, ge=1, le=100, description="Maximum number of records to return (1-100)")
):
"""
List API keys for the current authenticated user
Returns all active and inactive API keys belonging to the authenticated user.
Returns a paginated list of all active and inactive API keys belonging
to the authenticated user.
Args:
skip: Number of records to skip for pagination (default: 0)
limit: Maximum number of records to return, 1-100 (default: 50)
current_user: The authenticated user
auth_service: Injected auth service
Returns:
ApiKeyListResponse: Paginated list of API keys with total count
Raises:
400: Invalid pagination parameters
500: Internal server error
"""
auth_context = create_auth_context(
user=current_user,
resource_type="api_key",
action="list",
skip=skip,
limit=limit,
path=request.url.path,
method=request.method
)
log_authorization_context(auth_context, success=True)
try:
response = await auth_service.list_user_api_keys(current_user)
logger.info(f"Listed {response.total} API keys for user {current_user.id}")
response = await auth_service.list_user_api_keys(current_user, skip, limit)
logger.info(f"Listed {len(response.api_keys)} API keys (total: {response.total}) for user {current_user.id}")
return response
except Exception as e:
raise handle_service_error(e, "API key listing")

View File

@ -24,6 +24,7 @@ async def search_images(
request: Request,
search_service: SearchServiceDep,
q: str = Query(..., description="Search query for semantic image search"),
skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
limit: int = Query(10, ge=1, le=50, description="Number of results to return (1-50)"),
similarity_threshold: float = Query(0.65, ge=0.0, le=1.0, description="Similarity threshold (0.0-1.0)"),
collection_id: Optional[str] = Query(None, description="Filter results by collection ID"),
@ -38,6 +39,7 @@ async def search_images(
Args:
q: The search query text to find similar images
skip: Number of records to skip for pagination (default: 0)
limit: Maximum number of results to return (1-50, default: 10)
similarity_threshold: Minimum similarity score (0.0-1.0, default: 0.65)
collection_id: Optional filter to search within a specific collection
@ -56,6 +58,7 @@ async def search_images(
resource_type="image",
action="search",
query=q,
skip=skip,
limit=limit,
similarity_threshold=similarity_threshold,
collection_id=collection_id,
@ -70,6 +73,7 @@ async def search_images(
query=q,
user=current_user,
request=request,
skip=skip,
limit=limit,
similarity_threshold=similarity_threshold,
collection_id=collection_id

View File

@ -1,5 +1,5 @@
import logging
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi import APIRouter, Depends, HTTPException, Request, status, Query
from bson import ObjectId
from src.dependencies import TeamServiceDep
@ -55,32 +55,42 @@ async def create_team(
@router.get("", response_model=TeamListResponse)
async def list_teams(
request: Request,
team_service: TeamServiceDep
team_service: TeamServiceDep,
skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
limit: int = Query(50, ge=1, le=100, description="Maximum number of records to return (1-100)")
):
"""
List all teams
Retrieves a complete list of all teams in the system with their
Retrieves a paginated list of all teams in the system with their
basic information and member counts.
Args:
skip: Number of records to skip for pagination (default: 0)
limit: Maximum number of records to return, 1-100 (default: 50)
team_service: Injected team service
Returns:
TeamListResponse: List of all teams with total count
TeamListResponse: Paginated list of teams with total count
Raises:
400: Invalid pagination parameters
500: Internal server error
"""
auth_context = create_auth_context(
user=None, # No authentication required for listing teams
resource_type="team",
action="list",
skip=skip,
limit=limit,
path=request.url.path,
method=request.method
)
log_authorization_context(auth_context, success=True)
try:
response = await team_service.list_teams()
logger.info(f"Listed {response.total} teams")
response = await team_service.list_teams(skip, limit)
logger.info(f"Listed {len(response.teams)} teams (total: {response.total})")
return response
except Exception as e:
raise handle_service_error(e, "team listing")

View File

@ -147,29 +147,35 @@ async def create_user(
async def list_users(
request: Request,
user_service: UserServiceDep,
skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
limit: int = Query(50, ge=1, le=100, description="Maximum number of records to return (1-100)"),
team_id: Optional[str] = Query(None, description="Filter users by team ID")
):
"""
List users with optional team filtering
Retrieves a list of all users in the system. Can be filtered by team
Retrieves a paginated list of all users in the system. Can be filtered by team
to show only users belonging to a specific team.
Args:
skip: Number of records to skip for pagination (default: 0)
limit: Maximum number of records to return, 1-100 (default: 50)
team_id: Optional team ID to filter users by
user_service: Injected user service
Returns:
UserListResponse: List of users with total count
UserListResponse: Paginated list of users with total count
Raises:
400: Invalid team ID format
400: Invalid pagination parameters or team ID format
500: Internal server error
"""
auth_context = create_auth_context(
user=None, # No authentication required for listing users
resource_type="user",
action="list",
skip=skip,
limit=limit,
team_id=team_id,
path=request.url.path,
method=request.method
@ -177,8 +183,8 @@ async def list_users(
log_authorization_context(auth_context, success=True)
try:
response = await user_service.list_users(team_id)
logger.info(f"Listed {response.total} users" + (f" for team {team_id}" if team_id else ""))
response = await user_service.list_users(skip, limit, team_id)
logger.info(f"Listed {len(response.users)} users (total: {response.total})" + (f" for team {team_id}" if team_id else ""))
return response
except Exception as e:
raise handle_service_error(e, "user listing")