add spec-driven auth dependency injection
- security.py: parse securitySchemes, resolve {ENV_VAR} from env,
generate FastAPI Depends for Bearer JWT introspection
- app.py: extract schemes, build deps, pass to binder at init
- binder.py: inject Depends() per operation based on spec's security
- __init__.py: export security module
- pyproject.toml: add httpx dependency
This commit is contained in:
@@ -34,8 +34,9 @@ Notes:
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from .loader import load_openapi
|
||||
from .binder import bind_routes
|
||||
from .loader import load_openapi
|
||||
from .security import make_security_dependencies, parse_security_schemes
|
||||
|
||||
|
||||
class OpenAPIFirstApp(FastAPI):
|
||||
@@ -48,15 +49,19 @@ class OpenAPIFirstApp(FastAPI):
|
||||
- `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route
|
||||
registration with OpenAPI-driven binding.
|
||||
- All routes are derived from the provided OpenAPI specification,
|
||||
and each `operationId` is mapped to a Python function in the
|
||||
and each ``operationId`` is mapped to a Python function in the
|
||||
supplied routes module.
|
||||
- Auth dependencies are auto-injected from the spec's
|
||||
``securitySchemes`` and per-operation ``security`` fields.
|
||||
|
||||
**Guarantees:**
|
||||
|
||||
- No route can exist without an OpenAPI declaration.
|
||||
- No OpenAPI operation can exist without a handler.
|
||||
- Swagger UI and `/openapi.json` always reflect the provided spec.
|
||||
- Swagger UI and ``/openapi.json`` always reflect the provided spec.
|
||||
- Handler functions remain framework-agnostic and testable.
|
||||
- Auth enforcement is driven entirely by the spec — no manual
|
||||
middleware or decorators required.
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -87,16 +92,16 @@ class OpenAPIFirstApp(FastAPI):
|
||||
specification is treated as the authoritative API contract.
|
||||
routes_module (module):
|
||||
Python module containing handler functions whose names correspond
|
||||
exactly to OpenAPI `operationId` values.
|
||||
exactly to OpenAPI ``operationId`` values.
|
||||
**fastapi_kwargs (Any):
|
||||
Additional keyword arguments passed directly to
|
||||
`fastapi.FastAPI` (e.g., title, version, middleware, lifespan
|
||||
``fastapi.FastAPI`` (e.g., title, version, middleware, lifespan
|
||||
handlers).
|
||||
|
||||
Raises:
|
||||
OpenAPIFirstError:
|
||||
If the OpenAPI specification is invalid, or if any declared
|
||||
`operationId` does not have a corresponding handler function.
|
||||
``operationId`` does not have a corresponding handler function.
|
||||
"""
|
||||
# Initialize FastAPI normally
|
||||
super().__init__(**fastapi_kwargs)
|
||||
@@ -104,11 +109,16 @@ class OpenAPIFirstApp(FastAPI):
|
||||
# Load and validate OpenAPI specification
|
||||
self._openapi_spec = load_openapi(openapi_path)
|
||||
|
||||
# Bind routes strictly from OpenAPI spec
|
||||
# Parse security schemes and build per-route dependencies
|
||||
security_schemes = parse_security_schemes(self._openapi_spec)
|
||||
security_deps = make_security_dependencies(self._openapi_spec, security_schemes)
|
||||
|
||||
# Bind routes strictly from OpenAPI spec (with security deps)
|
||||
bind_routes(
|
||||
app=self,
|
||||
spec=self._openapi_spec,
|
||||
routes_module=routes_module,
|
||||
security_deps=security_deps,
|
||||
)
|
||||
|
||||
# Override FastAPI's OpenAPI generation
|
||||
|
||||
Reference in New Issue
Block a user