63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
"""OpenAPI parser — extracts ApiContract entities from OpenAPI YAML specifications."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
from app.modules.design.domain.entities import ApiContract
|
|
|
|
|
|
class OpenapiParser:
|
|
"""Parse OpenAPI YAML file and return dict mapping entity type name to list of instances.
|
|
|
|
Returns {'api_contracts': [ApiContract, ...]}
|
|
"""
|
|
|
|
def parse(self, file_path: Path) -> dict[str, list[Any]]:
|
|
try:
|
|
with open(file_path, encoding="utf-8") as f:
|
|
spec = yaml.safe_load(f)
|
|
except Exception:
|
|
return {}
|
|
|
|
if not isinstance(spec, dict) or "paths" not in spec:
|
|
return {}
|
|
|
|
contracts: list[ApiContract] = []
|
|
paths = spec["paths"]
|
|
if not isinstance(paths, dict):
|
|
return {}
|
|
|
|
for path, path_item in paths.items():
|
|
if not isinstance(path_item, dict):
|
|
continue
|
|
for method, operation in path_item.items():
|
|
# Skip non-HTTP-method keys (e.g., 'parameters', 'summary')
|
|
if method.lower() not in (
|
|
"get", "post", "put", "delete", "patch", "options", "head", "trace",
|
|
):
|
|
continue
|
|
if not isinstance(operation, dict):
|
|
continue
|
|
|
|
operation_id = operation.get("operationId", "")
|
|
summary = operation.get("summary", "")
|
|
|
|
doc_id = f"API-{operation_id or method.upper()}-{path}"
|
|
|
|
contracts.append(ApiContract(
|
|
doc_id=doc_id,
|
|
path=path,
|
|
method=method.upper(),
|
|
operation_id=operation_id or "",
|
|
summary=summary or "",
|
|
))
|
|
|
|
if not contracts:
|
|
return {}
|
|
|
|
return {"api_contracts": contracts}
|