"""Graph HTTP router — panorama and neighbor query endpoints.""" from __future__ import annotations from dataclasses import asdict from fastapi import APIRouter from app.modules.project.application.services import ProjectService from app.modules.scanner.application.services import ScanService from app.modules.graph.application.services import GraphService router = APIRouter(prefix="/projects/{project_id}/graph", tags=["graph"]) _project_service: ProjectService | None = None _scan_service: ScanService | None = None _graph_service: GraphService | None = None def init_router( project_service: ProjectService, scan_service: ScanService, graph_service: GraphService, ) -> None: global _project_service, _scan_service, _graph_service _project_service = project_service _scan_service = scan_service _graph_service = graph_service def _get_or_trigger_scan(project_id: str): """Get cached scan or trigger a new one.""" result = _scan_service.get_latest_scan(project_id) if result is None: project = _project_service.get_project(project_id) result = _scan_service.scan(project) return result @router.get("") def get_graph(project_id: str): """Build and return the full panorama graph for a project.""" project = _project_service.get_project(project_id) # Ensure project exists (raises 404) scan_result = _get_or_trigger_scan(project_id) view = _graph_service.build_panorama(scan_result, design_dir=project.design_dir) return asdict(view) @router.get("/nodes/{node_id}/neighbors") def get_neighbors(project_id: str, node_id: str): """Return the subgraph of neighbors for a given node.""" project = _project_service.get_project(project_id) # Ensure project exists (raises 404) scan_result = _get_or_trigger_scan(project_id) view = _graph_service.build_panorama(scan_result, design_dir=project.design_dir) neighbors = _graph_service.get_neighbors(view, node_id) return asdict(neighbors)