Files
asf/services/asf-core/app/api/v1/artifacts.py
T
2026-09-08 07:21:34 -04:00

110 lines
3.3 KiB
Python

"""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)