75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""
|
|
Exceptions for OpenAPI-first FastAPI applications.
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
This module defines a small hierarchy of explicit, intention-revealing
|
|
exceptions used to signal contract violations between an OpenAPI
|
|
specification and its Python implementation.
|
|
|
|
Notes:
|
|
**Design Principles:**
|
|
|
|
- Errors represent programmer mistakes, not runtime conditions
|
|
- All errors are raised during application startup
|
|
- Messages are actionable and suitable for CI/CD output
|
|
- Exceptions are explicit rather than reused from generic built-ins
|
|
|
|
These errors should normally cause immediate application failure.
|
|
"""
|
|
|
|
class OpenAPIFirstError(Exception):
|
|
"""
|
|
Base exception for all OpenAPI-first enforcement errors.
|
|
|
|
Notes:
|
|
**Responsibilities:**
|
|
|
|
- This exception exists to allow callers, test suites, and CI pipelines to catch and distinguish OpenAPI contract violations from unrelated runtime errors
|
|
- All exceptions raised by the OpenAPI-first core should inherit from this type
|
|
"""
|
|
pass
|
|
|
|
|
|
class MissingOperationHandler(OpenAPIFirstError):
|
|
"""
|
|
Raised when an OpenAPI operation cannot be resolved to a handler.
|
|
|
|
Notes:
|
|
**Scenarios:**
|
|
|
|
- An OpenAPI operation does not define an operationId
|
|
- An operationId is defined but no matching function exists in the provided routes module
|
|
|
|
**Guarantees:**
|
|
|
|
- This represents a violation of the OpenAPI-first contract and indicates that the specification and implementation are out of sync
|
|
"""
|
|
|
|
def __init__(self, *, path: str, method: str, operation_id: str | None = None):
|
|
"""
|
|
Initialize the error.
|
|
|
|
Args:
|
|
path (str):
|
|
The HTTP path declared in the OpenAPI specification.
|
|
method (str):
|
|
The HTTP method (as declared in the OpenAPI spec).
|
|
operation_id (str, optional):
|
|
The operationId declared in the OpenAPI spec, if present.
|
|
"""
|
|
if operation_id:
|
|
message = (
|
|
f"Missing handler for operationId '{operation_id}' "
|
|
f"({method.upper()} {path})"
|
|
)
|
|
else:
|
|
message = (
|
|
f"Missing operationId for operation "
|
|
f"({method.upper()} {path})"
|
|
)
|
|
|
|
super().__init__(message)
|