"""CRUD APIs for Artifacts — the heart of the ASF domain model.""" from __future__ import annotations from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query, status from asf_domain import ( Artifact, ArtifactCreate, ArtifactUpdate, ArtifactType, ArtifactRelationship, ) from ...repositories import NotFoundError, TenantMismatchError from .deps import artifact_service, relationship_service from ...services import ArtifactService, RelationshipService router = APIRouter() @router.get("", response_model=list[Artifact], summary="List artifacts (tenant scoped)") async def list_artifacts( project_id: Optional[UUID] = Query(None), type: Optional[ArtifactType] = Query(None), status: Optional[str] = Query(None), search: Optional[str] = Query(None, description="Case insensitive regex on title+content"), limit: int = Query(50, ge=1, le=500), offset: int = Query(0, ge=0), svc: ArtifactService = Depends(artifact_service), ) -> list[Artifact]: return await svc.list( project_id=project_id, artifact_type=type, status=status, search=search, limit=limit, offset=offset, ) @router.post( "", response_model=Artifact, status_code=status.HTTP_201_CREATED, summary="Create a new artifact", ) async def create_artifact( body: ArtifactCreate, svc: ArtifactService = Depends(artifact_service), ) -> Artifact: try: return await svc.create(body) except NotFoundError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc @router.get("/{artifact_id}", response_model=Artifact, summary="Get artifact by id") async def get_artifact( artifact_id: UUID, svc: ArtifactService = Depends(artifact_service), ) -> Artifact: a = await svc.get(artifact_id) if a is None: raise HTTPException(status_code=404, detail=f"Artifact {artifact_id} not found") return a @router.patch("/{artifact_id}", response_model=Artifact, summary="Update an artifact") async def update_artifact( artifact_id: UUID, body: ArtifactUpdate, svc: ArtifactService = Depends(artifact_service), ) -> Artifact: try: return await svc.update(artifact_id, body) except (NotFoundError, TenantMismatchError) as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc @router.delete( "/{artifact_id}", status_code=status.HTTP_204_NO_CONTENT, summary="Delete an artifact", ) async def delete_artifact( artifact_id: UUID, svc: ArtifactService = Depends(artifact_service), ) -> None: try: await svc.delete(artifact_id) except (NotFoundError, TenantMismatchError) as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc return None @router.get( "/{artifact_id}/relationships", response_model=list[ArtifactRelationship], summary="List all relationships (in + out) for an artifact — convenience wrapper for /relationships/for-artifact/{id}", ) async def artifact_relationships( artifact_id: UUID, limit: int = Query(200, ge=1, le=500), rel_svc: RelationshipService = Depends(relationship_service), ) -> list[ArtifactRelationship]: return await rel_svc.for_artifact(artifact_id, limit=limit)