init openapi_first
This commit is contained in:
109
openapi_first/loader.py
Normal file
109
openapi_first/loader.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
fastapi_openapi_first.loaders
|
||||
=============================
|
||||
|
||||
OpenAPI specification loading and validation utilities.
|
||||
|
||||
This module is responsible for loading an OpenAPI 3.x specification
|
||||
from disk and validating it before it is used by the application.
|
||||
|
||||
It enforces the principle that an invalid or malformed OpenAPI document
|
||||
must never reach the routing or runtime layers.
|
||||
|
||||
Design principles
|
||||
-----------------
|
||||
- OpenAPI is treated as an authoritative contract.
|
||||
- Invalid specifications fail fast at application startup.
|
||||
- Supported formats are JSON and YAML.
|
||||
- Validation errors are surfaced clearly and early.
|
||||
|
||||
This module intentionally does NOT:
|
||||
-----------------------------------
|
||||
- Modify the OpenAPI document
|
||||
- Infer missing fields
|
||||
- Generate models or code
|
||||
- Perform request/response validation at runtime
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
from openapi_spec_validator import validate_spec
|
||||
|
||||
from .errors import OpenAPIFirstError
|
||||
|
||||
|
||||
class OpenAPISpecLoadError(OpenAPIFirstError):
|
||||
"""
|
||||
Raised when an OpenAPI specification cannot be loaded or validated.
|
||||
|
||||
This error indicates that the OpenAPI document is unreadable,
|
||||
malformed, or violates the OpenAPI 3.x specification.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def load_openapi(path: str | Path) -> dict[str, Any]:
|
||||
"""
|
||||
Load and validate an OpenAPI 3.x specification from disk.
|
||||
|
||||
The specification is parsed based on file extension and validated
|
||||
using a strict OpenAPI schema validator. Any error results in an
|
||||
immediate exception, preventing application startup.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : str or pathlib.Path
|
||||
Filesystem path to an OpenAPI specification file.
|
||||
Supported extensions:
|
||||
- `.json`
|
||||
- `.yaml`
|
||||
- `.yml`
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Parsed and validated OpenAPI specification.
|
||||
|
||||
Raises
|
||||
------
|
||||
OpenAPISpecLoadError
|
||||
If the file does not exist, cannot be parsed, or fails
|
||||
OpenAPI schema validation.
|
||||
"""
|
||||
spec_path = Path(path)
|
||||
|
||||
if not spec_path.exists():
|
||||
raise OpenAPISpecLoadError(f"OpenAPI spec not found: {spec_path}")
|
||||
|
||||
try:
|
||||
if spec_path.suffix == ".json":
|
||||
with spec_path.open("r", encoding="utf-8") as f:
|
||||
spec = json.load(f)
|
||||
|
||||
elif spec_path.suffix in {".yaml", ".yml"}:
|
||||
with spec_path.open("r", encoding="utf-8") as f:
|
||||
spec = yaml.safe_load(f)
|
||||
|
||||
else:
|
||||
raise OpenAPISpecLoadError(
|
||||
f"Unsupported OpenAPI file format: {spec_path.suffix}"
|
||||
)
|
||||
|
||||
except Exception as exc:
|
||||
raise OpenAPISpecLoadError(
|
||||
f"Failed to parse OpenAPI spec: {spec_path}"
|
||||
) from exc
|
||||
|
||||
try:
|
||||
validate_spec(spec)
|
||||
except Exception as exc:
|
||||
raise OpenAPISpecLoadError(
|
||||
f"OpenAPI spec validation failed: {spec_path}"
|
||||
) from exc
|
||||
|
||||
return spec
|
||||
Reference in New Issue
Block a user