| """
|
| OpenAPI documentation configuration and customization.
|
|
|
| This module provides comprehensive OpenAPI schema customization,
|
| including enhanced documentation, examples, and client generation support.
|
| """
|
|
|
| from typing import Dict, Any, List, Optional
|
| from fastapi import FastAPI
|
| from fastapi.openapi.utils import get_openapi
|
|
|
| from .config import get_settings
|
|
|
| settings = get_settings()
|
|
|
|
|
| class OpenAPIConfig:
|
| """OpenAPI configuration and customization manager."""
|
|
|
| @staticmethod
|
| def get_api_description() -> str:
|
| """Get comprehensive API description for OpenAPI documentation."""
|
| return """
|
| ## FastAPI Video Generation Backend
|
|
|
| This API provides comprehensive video generation capabilities using multi-agent AI systems.
|
| The backend supports video creation from textual descriptions, job management, file handling,
|
| and real-time progress monitoring.
|
|
|
| ### Key Features
|
|
|
| - **Video Generation**: Create educational videos from topic and context descriptions
|
| - **Job Management**: Track video generation progress with real-time updates
|
| - **File Handling**: Secure upload, download, and streaming of video content
|
| - **Authentication**: Clerk-based user authentication and authorization
|
| - **Batch Processing**: Support for multiple video generation requests
|
| - **Monitoring**: Comprehensive system health and performance monitoring
|
|
|
| ### Authentication
|
|
|
| This API uses Clerk authentication. Include your Clerk session token in the Authorization header:
|
|
|
| ```
|
| Authorization: Bearer <your-clerk-session-token>
|
| ```
|
|
|
| ### Rate Limiting
|
|
|
| API requests are rate-limited per user. Current limits:
|
| - 100 requests per minute for authenticated users
|
| - 50 requests per minute for video generation endpoints
|
|
|
| ### Error Handling
|
|
|
| All errors follow a consistent format:
|
|
|
| ```json
|
| {
|
| "error": {
|
| "message": "Human-readable error message",
|
| "error_code": "MACHINE_READABLE_CODE",
|
| "details": {},
|
| "timestamp": "2024-01-15T10:30:00Z",
|
| "path": "/api/v1/videos/generate"
|
| }
|
| }
|
| ```
|
|
|
| ### WebSocket Support
|
|
|
| Real-time job status updates are available via WebSocket connections:
|
| - `ws://localhost:8000/ws/jobs/{job_id}` - Job-specific updates
|
| - `ws://localhost:8000/ws/system/health` - System health monitoring
|
|
|
| ### Pagination
|
|
|
| List endpoints support pagination with the following parameters:
|
| - `page`: Page number (default: 1)
|
| - `items_per_page`: Items per page (default: 10, max: 100)
|
|
|
| ### Filtering and Sorting
|
|
|
| Many endpoints support filtering and sorting:
|
| - `status`: Filter by job status
|
| - `created_after`: Filter by creation date
|
| - `sort_by`: Sort field (created_at, updated_at, etc.)
|
| - `sort_order`: Sort order (asc, desc)
|
|
|
| ### Client SDKs
|
|
|
| This API supports automatic client SDK generation using OpenAPI specifications.
|
| Supported languages include:
|
| - TypeScript/JavaScript
|
| - Python
|
| - Java
|
| - C#
|
| - Go
|
| - PHP
|
| - Ruby
|
|
|
| ### API Versioning
|
|
|
| This API uses URL-based versioning:
|
| - Current version: v1
|
| - Base URL: `/api/v1`
|
| - Deprecated versions will be supported for 6 months after deprecation notice
|
|
|
| ### Support
|
|
|
| For API support and questions:
|
| - Documentation: https://docs.example.com
|
| - Support Email: support@example.com
|
| - GitHub Issues: https://github.com/example/video-api/issues
|
| """
|
|
|
| @staticmethod
|
| def get_openapi_tags() -> List[Dict[str, Any]]:
|
| """Get OpenAPI tags for endpoint organization."""
|
| return [
|
| {
|
| "name": "videos",
|
| "description": "Video generation and management operations. Create videos from text descriptions, monitor progress, and download results.",
|
| "externalDocs": {
|
| "description": "Video Generation Guide",
|
| "url": "https://docs.example.com/video-generation",
|
| },
|
| },
|
| {
|
| "name": "jobs",
|
| "description": "Job management operations. Monitor job status, cancel jobs, retrieve logs, and manage job lifecycle.",
|
| "externalDocs": {
|
| "description": "Job Management Guide",
|
| "url": "https://docs.example.com/job-management",
|
| },
|
| },
|
| {
|
| "name": "files",
|
| "description": "File upload, download, and management operations. Handle video files, thumbnails, and related assets.",
|
| "externalDocs": {
|
| "description": "File Handling Guide",
|
| "url": "https://docs.example.com/file-handling",
|
| },
|
| },
|
| {
|
| "name": "system",
|
| "description": "System monitoring, health checks, and administrative operations. Monitor system performance and health.",
|
| "externalDocs": {
|
| "description": "System Monitoring Guide",
|
| "url": "https://docs.example.com/system-monitoring",
|
| },
|
| },
|
| {
|
| "name": "auth",
|
| "description": "Authentication and authorization operations. Manage user sessions and authentication status.",
|
| "externalDocs": {
|
| "description": "Authentication Guide",
|
| "url": "https://docs.example.com/authentication",
|
| },
|
| },
|
| {
|
| "name": "testing",
|
| "description": "Testing endpoints for API functionality verification.",
|
| },
|
| ]
|
|
|
| @staticmethod
|
| def get_api_servers() -> List[Dict[str, Any]]:
|
| """Get API server configurations for different environments."""
|
| servers = []
|
|
|
| if settings.environment == "development":
|
| servers.extend([
|
| {
|
| "url": "http://localhost:8000",
|
| "description": "Development server",
|
| "variables": {
|
| "port": {
|
| "default": "8000",
|
| "description": "Development server port"
|
| }
|
| }
|
| },
|
| {
|
| "url": "http://127.0.0.1:8000",
|
| "description": "Local development server"
|
| }
|
| ])
|
| elif settings.environment == "staging":
|
| servers.append({
|
| "url": "https://api-staging.example.com",
|
| "description": "Staging server"
|
| })
|
| elif settings.environment == "production":
|
| servers.append({
|
| "url": "https://api.example.com",
|
| "description": "Production server"
|
| })
|
|
|
| return servers
|
|
|
| @staticmethod
|
| def get_security_schemes() -> Dict[str, Any]:
|
| """Get security scheme definitions."""
|
| return {
|
| "ClerkAuth": {
|
| "type": "http",
|
| "scheme": "bearer",
|
| "bearerFormat": "JWT",
|
| "description": "Clerk session token authentication. Obtain token from Clerk authentication flow.",
|
| "x-clerk-docs": "https://clerk.com/docs/authentication/overview"
|
| },
|
| "ApiKeyAuth": {
|
| "type": "apiKey",
|
| "in": "header",
|
| "name": "X-API-Key",
|
| "description": "API key for service-to-service authentication (internal use only).",
|
| }
|
| }
|
|
|
| @staticmethod
|
| def get_custom_extensions() -> Dict[str, Any]:
|
| """Get custom OpenAPI extensions."""
|
| return {
|
| "x-logo": {
|
| "url": "https://example.com/logo.png",
|
| "altText": "Video Generation API",
|
| "href": "https://example.com"
|
| },
|
| "x-api-version": {
|
| "current": "v1",
|
| "supported": ["v1"],
|
| "deprecated": [],
|
| "sunset": {},
|
| "changelog": "https://docs.example.com/changelog"
|
| },
|
| "x-rate-limit": {
|
| "default": "100 requests per minute",
|
| "video-generation": "50 requests per minute",
|
| "file-upload": "20 requests per minute",
|
| "documentation": "https://docs.example.com/rate-limits"
|
| },
|
| "x-response-time": {
|
| "typical": "< 200ms",
|
| "video-generation": "5-10 minutes",
|
| "file-upload": "< 30 seconds"
|
| }
|
| }
|
|
|
| @classmethod
|
| def customize_openapi_schema(cls, app: FastAPI) -> None:
|
| """Customize OpenAPI schema with enhanced documentation."""
|
| def custom_openapi():
|
| if app.openapi_schema:
|
| return app.openapi_schema
|
|
|
| openapi_schema = get_openapi(
|
| title=app.title,
|
| version=app.version,
|
| description=app.description,
|
| routes=app.routes,
|
| tags=app.openapi_tags,
|
| servers=app.servers,
|
| )
|
|
|
|
|
| if "components" not in openapi_schema:
|
| openapi_schema["components"] = {}
|
|
|
| openapi_schema["components"]["securitySchemes"] = cls.get_security_schemes()
|
|
|
|
|
| openapi_schema["security"] = [
|
| {"ClerkAuth": []},
|
| {"ApiKeyAuth": []}
|
| ]
|
|
|
|
|
| openapi_schema.update(cls.get_custom_extensions())
|
|
|
|
|
| cls._enhance_operation_ids(openapi_schema)
|
|
|
|
|
| cls._add_comprehensive_examples(openapi_schema)
|
|
|
|
|
| cls._add_response_headers(openapi_schema)
|
|
|
|
|
| cls._add_webhook_documentation(openapi_schema)
|
|
|
| app.openapi_schema = openapi_schema
|
| return app.openapi_schema
|
|
|
| app.openapi = custom_openapi
|
|
|
| @staticmethod
|
| def _enhance_operation_ids(openapi_schema: Dict[str, Any]) -> None:
|
| """Enhance operation IDs for better client generation."""
|
| for path, path_data in openapi_schema.get("paths", {}).items():
|
| for method, operation in path_data.items():
|
| if isinstance(operation, dict) and "operationId" in operation:
|
| operation_id = operation["operationId"]
|
|
|
|
|
| if not any(operation_id.startswith(prefix) for prefix in ["get", "post", "put", "delete", "patch"]):
|
|
|
| summary = operation.get("summary", "").lower()
|
| if method.upper() == "POST":
|
| if "create" in summary or "generate" in summary:
|
| operation["operationId"] = f"create_{operation_id}"
|
| elif "upload" in summary:
|
| operation["operationId"] = f"upload_{operation_id}"
|
| else:
|
| operation["operationId"] = f"post_{operation_id}"
|
| elif method.upper() == "GET":
|
| if "list" in summary:
|
| operation["operationId"] = f"list_{operation_id}"
|
| else:
|
| operation["operationId"] = f"get_{operation_id}"
|
| elif method.upper() == "PUT":
|
| operation["operationId"] = f"update_{operation_id}"
|
| elif method.upper() == "DELETE":
|
| operation["operationId"] = f"delete_{operation_id}"
|
| elif method.upper() == "PATCH":
|
| operation["operationId"] = f"patch_{operation_id}"
|
|
|
|
|
| if "description" not in operation and "summary" in operation:
|
| operation["description"] = f"{operation['summary']}. {OpenAPIConfig._get_operation_description(path, method)}"
|
|
|
| @staticmethod
|
| def _get_operation_description(path: str, method: str) -> str:
|
| """Get enhanced description for operations."""
|
| descriptions = {
|
| "/api/v1/videos/generate": "Submits a video generation request and returns a job ID for tracking progress.",
|
| "/api/v1/videos/jobs/{job_id}/status": "Retrieves current status and progress information for a video generation job.",
|
| "/api/v1/videos/jobs/{job_id}/download": "Downloads the completed video file with support for range requests.",
|
| "/api/v1/jobs": "Lists jobs with pagination, filtering, and sorting options.",
|
| "/api/v1/jobs/{job_id}/cancel": "Cancels a job if it's in a cancellable state (queued or processing).",
|
| "/api/v1/system/health": "Provides comprehensive system health status including all components.",
|
| }
|
| return descriptions.get(path, "")
|
|
|
| @staticmethod
|
| def _add_comprehensive_examples(openapi_schema: Dict[str, Any]) -> None:
|
| """Add comprehensive examples to schema definitions."""
|
| if "components" not in openapi_schema:
|
| return
|
|
|
| schemas = openapi_schema["components"].get("schemas", {})
|
|
|
|
|
| if "HTTPValidationError" in schemas:
|
| schemas["HTTPValidationError"]["examples"] = {
|
| "validation_error": {
|
| "summary": "Validation Error Example",
|
| "description": "Example of a validation error response",
|
| "value": {
|
| "detail": [
|
| {
|
| "loc": ["body", "configuration", "topic"],
|
| "msg": "field required",
|
| "type": "value_error.missing"
|
| },
|
| {
|
| "loc": ["body", "configuration", "context"],
|
| "msg": "ensure this value has at least 1 characters",
|
| "type": "value_error.any_str.min_length",
|
| "ctx": {"limit_value": 1}
|
| }
|
| ]
|
| }
|
| }
|
| }
|
|
|
|
|
| job_examples = {
|
| "queued_job": {
|
| "summary": "Queued Job",
|
| "description": "A newly created job in the queue",
|
| "value": {
|
| "job_id": "550e8400-e29b-41d4-a716-446655440000",
|
| "status": "queued",
|
| "progress": {
|
| "percentage": 0.0,
|
| "current_stage": None,
|
| "stages_completed": [],
|
| "estimated_completion": "2024-01-15T10:35:00Z"
|
| },
|
| "created_at": "2024-01-15T10:30:00Z"
|
| }
|
| },
|
| "processing_job": {
|
| "summary": "Processing Job",
|
| "description": "A job currently being processed",
|
| "value": {
|
| "job_id": "550e8400-e29b-41d4-a716-446655440000",
|
| "status": "processing",
|
| "progress": {
|
| "percentage": 45.0,
|
| "current_stage": "video_generation",
|
| "stages_completed": ["validation", "content_preparation"],
|
| "estimated_completion": "2024-01-15T10:33:00Z"
|
| },
|
| "created_at": "2024-01-15T10:30:00Z"
|
| }
|
| },
|
| "completed_job": {
|
| "summary": "Completed Job",
|
| "description": "A successfully completed job",
|
| "value": {
|
| "job_id": "550e8400-e29b-41d4-a716-446655440000",
|
| "status": "completed",
|
| "progress": {
|
| "percentage": 100.0,
|
| "current_stage": "completed",
|
| "stages_completed": ["validation", "content_preparation", "video_generation", "post_processing"],
|
| "estimated_completion": None
|
| },
|
| "created_at": "2024-01-15T10:30:00Z"
|
| }
|
| }
|
| }
|
|
|
|
|
| for schema_name, schema_data in schemas.items():
|
| if "JobResponse" in schema_name:
|
| schema_data["examples"] = job_examples
|
|
|
| @staticmethod
|
| def _add_response_headers(openapi_schema: Dict[str, Any]) -> None:
|
| """Add response headers documentation."""
|
| common_headers = {
|
| "X-Request-ID": {
|
| "description": "Unique request identifier for tracking and debugging",
|
| "schema": {"type": "string", "format": "uuid"}
|
| },
|
| "X-Rate-Limit-Remaining": {
|
| "description": "Number of requests remaining in the current rate limit window",
|
| "schema": {"type": "integer"}
|
| },
|
| "X-Rate-Limit-Reset": {
|
| "description": "Unix timestamp when the rate limit window resets",
|
| "schema": {"type": "integer"}
|
| }
|
| }
|
|
|
|
|
| for path_data in openapi_schema.get("paths", {}).values():
|
| for operation in path_data.values():
|
| if isinstance(operation, dict) and "responses" in operation:
|
| for response_code, response_data in operation["responses"].items():
|
| if isinstance(response_data, dict):
|
| if "headers" not in response_data:
|
| response_data["headers"] = {}
|
| response_data["headers"].update(common_headers)
|
|
|
| @staticmethod
|
| def _add_webhook_documentation(openapi_schema: Dict[str, Any]) -> None:
|
| """Add webhook documentation."""
|
| webhook_info = {
|
| "x-webhooks": {
|
| "job_status_changed": {
|
| "post": {
|
| "summary": "Job Status Changed",
|
| "description": "Triggered when a job status changes (queued -> processing -> completed/failed)",
|
| "requestBody": {
|
| "content": {
|
| "application/json": {
|
| "schema": {
|
| "type": "object",
|
| "properties": {
|
| "event": {"type": "string", "example": "job.status.changed"},
|
| "job_id": {"type": "string", "format": "uuid"},
|
| "user_id": {"type": "string"},
|
| "old_status": {"type": "string", "enum": ["queued", "processing", "completed", "failed", "cancelled"]},
|
| "new_status": {"type": "string", "enum": ["queued", "processing", "completed", "failed", "cancelled"]},
|
| "timestamp": {"type": "string", "format": "date-time"}
|
| }
|
| }
|
| }
|
| }
|
| },
|
| "responses": {
|
| "200": {
|
| "description": "Webhook received successfully"
|
| }
|
| }
|
| }
|
| },
|
| "job_completed": {
|
| "post": {
|
| "summary": "Job Completed",
|
| "description": "Triggered when a video generation job is completed successfully",
|
| "requestBody": {
|
| "content": {
|
| "application/json": {
|
| "schema": {
|
| "type": "object",
|
| "properties": {
|
| "event": {"type": "string", "example": "job.completed"},
|
| "job_id": {"type": "string", "format": "uuid"},
|
| "user_id": {"type": "string"},
|
| "video_id": {"type": "string", "format": "uuid"},
|
| "download_url": {"type": "string", "format": "uri"},
|
| "duration_seconds": {"type": "number"},
|
| "file_size": {"type": "integer"},
|
| "timestamp": {"type": "string", "format": "date-time"}
|
| }
|
| }
|
| }
|
| }
|
| },
|
| "responses": {
|
| "200": {
|
| "description": "Webhook received successfully"
|
| }
|
| }
|
| }
|
| }
|
| }
|
| }
|
|
|
| openapi_schema.update(webhook_info)
|
|
|
|
|
| def setup_openapi_documentation(app: FastAPI) -> None:
|
| """Set up comprehensive OpenAPI documentation for the FastAPI application."""
|
| OpenAPIConfig.customize_openapi_schema(app) |