Initial commit
This commit is contained in:
130
skills/api-design-standards/templates/fastapi-crud-endpoint.py
Normal file
130
skills/api-design-standards/templates/fastapi-crud-endpoint.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# Grey Haven Studio - FastAPI CRUD Endpoint Template
|
||||
# Copy this template for new resource endpoints
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlmodel import Session
|
||||
from typing import Optional
|
||||
|
||||
from app.core.dependencies import get_current_user, get_db
|
||||
from app.models.user import User
|
||||
from app.repositories.resource_repository import ResourceRepository # TODO: Update import
|
||||
from app.schemas.resource import ResourceCreate, ResourceRead, ResourceUpdate, PaginatedResponse # TODO: Update import
|
||||
|
||||
# TODO: Update prefix, tags, and model name
|
||||
router = APIRouter(prefix="/api/v1/resources", tags=["resources"])
|
||||
|
||||
|
||||
@router.get("", response_model=PaginatedResponse[ResourceRead], status_code=status.HTTP_200_OK)
|
||||
async def list_resources(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
# TODO: Add filter parameters as needed
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> PaginatedResponse[ResourceRead]:
|
||||
"""
|
||||
List all resources in the current tenant with pagination.
|
||||
|
||||
- **skip**: Number of records to skip (pagination offset)
|
||||
- **limit**: Maximum number of records to return (max 100)
|
||||
- **Returns**: Paginated list of resources
|
||||
"""
|
||||
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
|
||||
|
||||
# TODO: Build filter criteria if needed
|
||||
filters = {}
|
||||
|
||||
resources = await repository.list(filters=filters, skip=skip, limit=limit)
|
||||
total = await repository.count(filters=filters)
|
||||
|
||||
return PaginatedResponse(
|
||||
items=resources,
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
has_more=(skip + limit) < total,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK)
|
||||
async def get_resource(
|
||||
resource_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> ResourceRead:
|
||||
"""Get a single resource by ID (tenant-isolated)."""
|
||||
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
|
||||
resource = await repository.get_by_id(resource_id)
|
||||
if not resource:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Resource with ID {resource_id} not found",
|
||||
)
|
||||
return resource
|
||||
|
||||
|
||||
@router.post("", response_model=ResourceRead, status_code=status.HTTP_201_CREATED)
|
||||
async def create_resource(
|
||||
resource_data: ResourceCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> ResourceRead:
|
||||
"""
|
||||
Create a new resource in the current tenant.
|
||||
|
||||
**Errors**:
|
||||
- 409 Conflict: Duplicate resource
|
||||
- 422 Validation Error: Invalid data
|
||||
"""
|
||||
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
|
||||
|
||||
try:
|
||||
resource = await repository.create(resource_data)
|
||||
return resource
|
||||
except IntegrityError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Resource with this identifier already exists",
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK)
|
||||
async def update_resource(
|
||||
resource_id: str,
|
||||
resource_data: ResourceUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> ResourceRead:
|
||||
"""
|
||||
Update an existing resource (tenant-isolated).
|
||||
|
||||
All fields are optional - only provided fields will be updated.
|
||||
"""
|
||||
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
|
||||
resource = await repository.update(resource_id, resource_data)
|
||||
if not resource:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Resource with ID {resource_id} not found",
|
||||
)
|
||||
return resource
|
||||
|
||||
|
||||
@router.delete("/{resource_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_resource(
|
||||
resource_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> None:
|
||||
"""
|
||||
Soft-delete a resource (tenant-isolated).
|
||||
|
||||
Returns 204 No Content on success (no response body).
|
||||
"""
|
||||
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
|
||||
success = await repository.delete(resource_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Resource with ID {resource_id} not found",
|
||||
)
|
||||
Reference in New Issue
Block a user