arch-design-agent-skill-das.../backend/app/modules/scanner/infrastructure/parsers/openapi_parser.py
openclaw 699e2ad919 feat(scanner): add YAML and OpenAPI parsers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:32:11 +00:00

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}