diff --git a/backend/app/routers/relations.py b/backend/app/routers/relations.py index 3d6d2a7..59f7b32 100644 --- a/backend/app/routers/relations.py +++ b/backend/app/routers/relations.py @@ -1,8 +1,17 @@ -from fastapi import APIRouter, HTTPException, Query +import json +from datetime import datetime, timezone +from uuid import uuid4 -from app.schemas import EntityResponse, ListResponse, Relation -from app.services.inventory import get_node_relations -from app.services.store import RELATIONS +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session + +from app.database import get_db +from app.models import NetObjectModel, RelationModel +from app.schemas import ( + EntityResponse, ListResponse, Relation, + RelationCreate, RelationUpdate, +) +from app.services.inventory import _to_relation router = APIRouter(prefix='/relations', tags=['relations']) @@ -14,24 +23,84 @@ def list_relations( layer: str | None = None, type: str | None = Query(default=None), min_confidence: float | None = None, -) -> ListResponse[Relation]: - items = RELATIONS + status: str | None = None, + db: Session = Depends(get_db), +): + q = db.query(RelationModel) if source_id: - items = [r for r in items if r.source_id == source_id] + q = q.filter(RelationModel.source_id == source_id) if target_id: - items = [r for r in items if r.target_id == target_id] + q = q.filter(RelationModel.target_id == target_id) if layer: - items = [r for r in items if r.layer == layer] + q = q.filter(RelationModel.layer == layer) if type: - items = [r for r in items if r.type == type] + q = q.filter(RelationModel.type == type) if min_confidence is not None: - items = [r for r in items if r.confidence >= min_confidence] - return ListResponse(count=len(items), items=items) + q = q.filter(RelationModel.confidence >= min_confidence) + if status: + q = q.filter(RelationModel.status == status) + rows = q.all() + return ListResponse(count=len(rows), items=[_to_relation(r) for r in rows]) @router.get('/{relation_id}', response_model=EntityResponse[Relation]) -def get_relation(relation_id: str) -> EntityResponse[Relation]: - for item in RELATIONS: - if item.id == relation_id: - return EntityResponse(item=item) - raise HTTPException(status_code=404, detail='Relation not found') +def get_relation(relation_id: str, db: Session = Depends(get_db)): + row = db.query(RelationModel).filter(RelationModel.id == relation_id).first() + if not row: + raise HTTPException(status_code=404, detail='Relation not found') + return EntityResponse(item=_to_relation(row)) + + +@router.post('', response_model=EntityResponse[Relation], status_code=201) +def create_relation(body: RelationCreate, db: Session = Depends(get_db)): + for fk, val in [('source_id', body.source_id), ('target_id', body.target_id)]: + if not db.query(NetObjectModel).filter(NetObjectModel.id == val).first(): + raise HTTPException(status_code=422, detail=f'{fk} {val!r} not found') + now = datetime.now(timezone.utc) + row = RelationModel( + id=f'rel_{uuid4().hex[:8]}', + type=body.type, + layer=body.layer, + source_id=body.source_id, + target_id=body.target_id, + direction=body.direction, + source=body.source, + confidence=body.confidence, + status=body.status, + attributes_json=json.dumps(body.attributes), + created_at=now, + updated_at=now, + ) + db.add(row) + db.commit() + db.refresh(row) + return EntityResponse(item=_to_relation(row)) + + +@router.patch('/{relation_id}', response_model=EntityResponse[Relation]) +def update_relation(relation_id: str, body: RelationUpdate, db: Session = Depends(get_db)): + row = db.query(RelationModel).filter(RelationModel.id == relation_id).first() + if not row: + raise HTTPException(status_code=404, detail='Relation not found') + data = body.model_dump(exclude_unset=True) + for fk in ('source_id', 'target_id'): + if fk in data and data[fk]: + if not db.query(NetObjectModel).filter(NetObjectModel.id == data[fk]).first(): + raise HTTPException(status_code=422, detail=f'{fk} {data[fk]!r} not found') + if 'attributes' in data: + data['attributes_json'] = json.dumps(data.pop('attributes')) + for field, value in data.items(): + setattr(row, field, value) + row.updated_at = datetime.now(timezone.utc) + db.commit() + db.refresh(row) + return EntityResponse(item=_to_relation(row)) + + +@router.delete('/{relation_id}', status_code=204) +def delete_relation(relation_id: str, db: Session = Depends(get_db)): + row = db.query(RelationModel).filter(RelationModel.id == relation_id).first() + if not row: + raise HTTPException(status_code=404, detail='Relation not found') + db.delete(row) + db.commit()