from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import or_ from sqlalchemy.orm import Session from ..auth import verify_token from ..database import get_db from ..models import CrossPillarLink, RequirementNode from ..schemas import LinkCreate, LinkResponse router = APIRouter(prefix="/api/links", tags=["links"], dependencies=[Depends(verify_token)]) @router.get("", response_model=list[LinkResponse]) def list_links( node_id: Optional[str] = Query(None, description="Filter links involving this node"), db: Session = Depends(get_db), ): q = db.query(CrossPillarLink) if node_id: q = q.filter( or_( CrossPillarLink.source_id == node_id, CrossPillarLink.target_id == node_id, ) ) return q.all() @router.post("", response_model=LinkResponse, status_code=201) def create_link(data: LinkCreate, db: Session = Depends(get_db)): if not db.get(RequirementNode, data.source_id): raise HTTPException(404, f"Source {data.source_id} not found") if not db.get(RequirementNode, data.target_id): raise HTTPException(404, f"Target {data.target_id} not found") existing = db.get(CrossPillarLink, (data.source_id, data.target_id)) if existing: raise HTTPException(409, "Link already exists") link = CrossPillarLink(**data.model_dump()) db.add(link) db.commit() db.refresh(link) return link @router.delete("/{source_id}/{target_id}", status_code=204) def delete_link(source_id: str, target_id: str, db: Session = Depends(get_db)): link = db.get(CrossPillarLink, (source_id, target_id)) if not link: raise HTTPException(404, "Link not found") db.delete(link) db.commit()