feat(crud): full CRUD for Relation — wire to SQLAlchemy, drop store.py dep
CI / basic-check (push) Has been cancelled

This commit is contained in:
2026-08-06 16:10:56 +00:00
parent f47443ad98
commit 76886b93d3
+86 -17
View File
@@ -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()