Files
xiaoxia-saas/apps/api/app/middleware/versioning.py
T

107 lines
3.3 KiB
Python

"""
API versioning middleware.
NOTE: This module is currently not used. To enable API versioning:
1. Import VersionMiddleware in main.py
2. Add it to the app middleware stack
Example usage:
from app.middleware.versioning import APIVersionMiddleware
app.add_middleware(APIVersionMiddleware)
This middleware adds X-API-Version header to responses based on the
version extracted from the URL path (e.g., /v1/... or /v2/...).
"""
from __future__ import annotations
from datetime import datetime
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
class APIVersionMiddleware(BaseHTTPMiddleware):
"""API versioning middleware."""
# API version definitions
VERSIONS = {
"v1": {
"status": "stable",
"deprecated": False,
"sunset_date": None,
"release_date": "2026-06-17",
},
"v2": {
"status": "development",
"deprecated": False,
"sunset_date": None,
"release_date": None,
},
}
async def dispatch(self, request: Request, call_next):
# Extract version from URL path
version = self._extract_version(request.url.path)
# Process request
response = await call_next(request)
# Add version header
if version:
response.headers["X-API-Version"] = version
# Add deprecation headers if version is deprecated
version_info = self.VERSIONS.get(version, {})
if version_info.get("deprecated"):
response.headers["X-API-Deprecated"] = "true"
sunset_date = version_info.get("sunset_date")
if sunset_date:
response.headers["X-API-Sunset-Date"] = sunset_date
response.headers["X-API-Deprecation-Info"] = f"https://docs.xiaoxia-saas.com/api/deprecation/{version}"
return response
def _extract_version(self, path: str) -> str | None:
"""Extract version from URL path."""
parts = path.split("/")
for part in parts:
if part.startswith("v") and part[1:].isdigit():
return part
return None
class VersionNotFoundMiddleware(BaseHTTPMiddleware):
"""Middleware to reject requests for unsupported API versions."""
UNSUPPORTED_VERSIONS = [] # Add deprecated versions here
async def dispatch(self, request: Request, call_next):
version = self._extract_version(request.url.path)
if version in self.UNSUPPORTED_VERSIONS:
from fastapi.responses import JSONResponse
return JSONResponse(
status_code=410,
content={
"error": {
"code": "API_VERSION_SUNSET",
"message": f"API {version} has been sunset and is no longer available",
"sunset_date": "2028-07-01",
"migration_guide": f"https://docs.xiaoxia-saas.com/api/migration/{version}",
}
},
)
return await call_next(request)
def _extract_version(self, path: str) -> str | None:
"""Extract version from URL path."""
parts = path.split("/")
for part in parts:
if part.startswith("v") and part[1:].isdigit():
return part
return None