All checks were successful
continuous-integration/drone/tag Build is passing
103 lines
3.1 KiB
Python
103 lines
3.1 KiB
Python
"""
|
|
openapi_first.binder
|
|
============================
|
|
|
|
OpenAPI-driven route binding for FastAPI.
|
|
|
|
This module is responsible for translating an OpenAPI 3.x specification
|
|
into concrete FastAPI routes. It enforces a strict one-to-one mapping
|
|
between OpenAPI operations and Python handler functions using operationId.
|
|
|
|
Core responsibility
|
|
-------------------
|
|
- Read path + method definitions from an OpenAPI specification
|
|
- Resolve each operationId to a Python callable
|
|
- Register routes with FastAPI using APIRoute
|
|
- Fail fast when contract violations are detected
|
|
|
|
Design constraints
|
|
------------------
|
|
- All routes MUST be declared in the OpenAPI specification.
|
|
- All OpenAPI operations MUST define an operationId.
|
|
- Every operationId MUST resolve to a handler function.
|
|
- Handlers are plain Python callables (no decorators required).
|
|
- No implicit route creation or inference is allowed.
|
|
|
|
This module intentionally does NOT:
|
|
-------------------------------
|
|
- Perform request or response validation
|
|
- Generate Pydantic models
|
|
- Modify FastAPI dependency injection
|
|
- Interpret OpenAPI semantics beyond routing metadata
|
|
|
|
Those concerns belong to other layers or tooling.
|
|
"""
|
|
|
|
from fastapi.routing import APIRoute
|
|
|
|
from .errors import MissingOperationHandler
|
|
|
|
|
|
def bind_routes(app, spec: dict, routes_module) -> None:
|
|
"""
|
|
Bind OpenAPI operations to FastAPI routes.
|
|
|
|
Iterates through the OpenAPI specification paths and methods,
|
|
resolves each operationId to a handler function, and registers
|
|
a corresponding APIRoute on the FastAPI application.
|
|
|
|
Parameters
|
|
----------
|
|
app : fastapi.FastAPI
|
|
The FastAPI application instance to which routes will be added.
|
|
|
|
spec : dict
|
|
Parsed OpenAPI 3.x specification dictionary.
|
|
|
|
routes_module : module
|
|
Python module containing handler functions. Each handler's
|
|
name MUST exactly match an OpenAPI operationId.
|
|
|
|
Raises
|
|
------
|
|
MissingOperationHandler
|
|
If an operationId is missing from the spec or if no corresponding
|
|
handler function exists in the routes module.
|
|
|
|
Behavior guarantees
|
|
-------------------
|
|
- Route registration is deterministic and spec-driven.
|
|
- No route decorators are required or supported.
|
|
- Handler resolution errors surface at application startup.
|
|
"""
|
|
|
|
paths = spec.get("paths", {})
|
|
|
|
for path, methods in paths.items():
|
|
for http_method, operation in methods.items():
|
|
|
|
operation_id = operation.get("operationId")
|
|
if not operation_id:
|
|
raise MissingOperationHandler(
|
|
path=path,
|
|
method=http_method,
|
|
)
|
|
|
|
try:
|
|
endpoint = getattr(routes_module, operation_id)
|
|
except AttributeError:
|
|
raise MissingOperationHandler(
|
|
path=path,
|
|
method=http_method,
|
|
operation_id=operation_id,
|
|
)
|
|
|
|
route = APIRoute(
|
|
path=path,
|
|
endpoint=endpoint,
|
|
methods=[http_method.upper()],
|
|
name=operation_id,
|
|
)
|
|
|
|
app.router.routes.append(route)
|