implement pagination on all endpoints
This commit is contained in:
parent
fe082026c1
commit
80d8a74b12
33
README.md
33
README.md
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user