feat: make repository reads alias-aware
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a049a4-ee9f-78e1-9d66-2cb0f9bea3e3
This commit is contained in:
parent
639b9aed08
commit
2e2ae1e5d0
19 changed files with 982 additions and 94 deletions
|
|
@ -27,6 +27,7 @@ from api.services.sbom_nexus import (
|
|||
reads_from_nexus,
|
||||
writes_to_nexus,
|
||||
)
|
||||
from api.services.repository_aliases import resolve_repository_slug
|
||||
|
||||
router = APIRouter(prefix="/sbom", tags=["sbom"])
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -68,9 +69,12 @@ async def ingest_sbom(
|
|||
session: AsyncSession = Depends(get_session),
|
||||
) -> dict:
|
||||
"""Create a new SBOM snapshot for a repo. Previous snapshots are retained."""
|
||||
repo = await _get_repo_by_slug(body.repo_slug, session)
|
||||
resolution = await resolve_repository_slug(session, body.repo_slug)
|
||||
repo = resolution.repo
|
||||
if writes_to_nexus():
|
||||
payload = await _nexus_post("/sbom/ingest/", body=body.model_dump(mode="json"))
|
||||
nexus_body = body.model_dump(mode="json")
|
||||
nexus_body["repo_slug"] = resolution.canonical_slug
|
||||
payload = await _nexus_post("/sbom/ingest/", body=nexus_body)
|
||||
try:
|
||||
snapshot_at = datetime.fromisoformat(
|
||||
payload["snapshot_at"].replace("Z", "+00:00")
|
||||
|
|
@ -126,7 +130,7 @@ async def ingest_sbom(
|
|||
await session.commit()
|
||||
await _meter_compat(session, request, "POST", "/sbom/ingest/")
|
||||
return {
|
||||
"repo_slug": body.repo_slug,
|
||||
"repo_slug": resolution.canonical_slug,
|
||||
"snapshot_id": str(snap.id),
|
||||
"ingested": len(body.entries),
|
||||
"snapshot_at": now.isoformat(),
|
||||
|
|
@ -142,6 +146,10 @@ async def list_snapshots(
|
|||
"""List SBOM snapshots, newest first. Optionally filter by repo."""
|
||||
await _meter_compat(session, request, "GET", "/sbom/snapshots/")
|
||||
if reads_from_nexus():
|
||||
if repo_slug:
|
||||
repo_slug = (
|
||||
await resolve_repository_slug(session, repo_slug)
|
||||
).canonical_slug
|
||||
payload = await _nexus_get(
|
||||
"/sbom/snapshots/",
|
||||
params={"repo_slug": repo_slug} if repo_slug else None,
|
||||
|
|
@ -207,6 +215,10 @@ async def list_sbom_entries(
|
|||
"""Return entries from the latest snapshot per repo (default) or filter by repo."""
|
||||
await _meter_compat(session, request, "GET", "/sbom/")
|
||||
if reads_from_nexus():
|
||||
if repo_slug:
|
||||
repo_slug = (
|
||||
await resolve_repository_slug(session, repo_slug)
|
||||
).canonical_slug
|
||||
params = {
|
||||
key: value
|
||||
for key, value in {
|
||||
|
|
@ -299,10 +311,11 @@ async def get_repo_sbom(
|
|||
session: AsyncSession = Depends(get_session),
|
||||
) -> SBOMRepoView:
|
||||
"""Return the latest snapshot entries for a specific repo."""
|
||||
repo = await _get_repo_by_slug(repo_slug, session)
|
||||
resolution = await resolve_repository_slug(session, repo_slug)
|
||||
repo = resolution.repo
|
||||
await _meter_compat(session, request, "GET", "/sbom/{repo_slug}")
|
||||
if reads_from_nexus():
|
||||
payload = await _nexus_get(f"/sbom/{repo_slug}")
|
||||
payload = await _nexus_get(f"/sbom/{resolution.canonical_slug}")
|
||||
payload["entries"] = [
|
||||
_translate_entry(entry, repo.id) for entry in payload.get("entries", [])
|
||||
]
|
||||
|
|
@ -322,7 +335,7 @@ async def get_repo_sbom(
|
|||
)
|
||||
entries = list(rows.scalars().all())
|
||||
return SBOMRepoView(
|
||||
repo_slug=repo_slug,
|
||||
repo_slug=resolution.canonical_slug,
|
||||
last_sbom_at=repo.last_sbom_at,
|
||||
entry_count=len(entries),
|
||||
entries=[SBOMEntryRead.model_validate(e) for e in entries],
|
||||
|
|
@ -330,11 +343,8 @@ async def get_repo_sbom(
|
|||
|
||||
|
||||
async def _get_repo_by_slug(slug: str, session: AsyncSession) -> ManagedRepo:
|
||||
result = await session.execute(select(ManagedRepo).where(ManagedRepo.slug == slug))
|
||||
repo = result.scalar_one_or_none()
|
||||
if repo is None:
|
||||
raise HTTPException(status_code=404, detail=f"Repo '{slug}' not found")
|
||||
return repo
|
||||
resolution = await resolve_repository_slug(session, slug)
|
||||
return resolution.repo
|
||||
|
||||
|
||||
async def _nexus_get(path: str, *, params: dict | None = None):
|
||||
|
|
@ -355,10 +365,11 @@ async def _local_repo_ids(items: list[dict], session: AsyncSession) -> dict[str,
|
|||
slugs = {item.get("repo_slug") for item in items}
|
||||
if None in slugs:
|
||||
raise HTTPException(status_code=502, detail="SBOM Nexus response omitted repo_slug")
|
||||
result = await session.execute(
|
||||
select(ManagedRepo.slug, ManagedRepo.id).where(ManagedRepo.slug.in_(slugs))
|
||||
)
|
||||
repo_ids = dict(result.all())
|
||||
repo_ids: dict[str, uuid.UUID] = {}
|
||||
for slug in slugs:
|
||||
resolution = await resolve_repository_slug(session, slug, required=False)
|
||||
if resolution is not None:
|
||||
repo_ids[slug] = resolution.repo.id
|
||||
missing = sorted(slugs - repo_ids.keys())
|
||||
if missing:
|
||||
raise HTTPException(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue