krz/hutch-stats
Server-side utility for calculating contributions for sourcehut users.
clone: git clone https://gitbay.org/krz/hutch-stats.git
main: src/srht_contrib/api/routes_repositories.py · raw
1from __future__ import annotations
2
3from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
4from sqlalchemy import select
5from sqlalchemy.exc import IntegrityError
6from sqlalchemy.orm import Session
7
8from srht_contrib.api.dependencies import get_actor_identity_resolver, get_db, require_api_key
9from srht_contrib.models import TrackedRepository
10from srht_contrib.schemas import (
11 TrackedRepositoryCreateRequest,
12 TrackedRepositoryResponse,
13 TrackedRepositoryUpdateRequest,
14)
15from srht_contrib.utils.identity import ActorIdentityResolver
16from srht_contrib.utils.repositories import canonicalize_repository_name
17
18router = APIRouter(prefix="/api/repositories", tags=["repositories"], dependencies=[Depends(require_api_key)])
19
20
21def _to_response(repository: TrackedRepository) -> TrackedRepositoryResponse:
22 return TrackedRepositoryResponse(
23 id=repository.id,
24 service=repository.service,
25 actor=repository.actor,
26 repo_name=repository.repo_name,
27 )
28
29
30def _get_repository_or_404(db: Session, repository_id: int) -> TrackedRepository:
31 repository = db.scalar(select(TrackedRepository).where(TrackedRepository.id == repository_id))
32 if repository is None:
33 raise HTTPException(status_code=404, detail="Tracked repository not found.")
34 return repository
35
36
37@router.get("", response_model=list[TrackedRepositoryResponse])
38def list_tracked_repositories(
39 actor: str | None = Query(default=None),
40 db: Session = Depends(get_db),
41 actor_identity_resolver: ActorIdentityResolver = Depends(get_actor_identity_resolver),
42) -> list[TrackedRepositoryResponse]:
43 stmt = select(TrackedRepository).where(TrackedRepository.service == "git").order_by(TrackedRepository.repo_name)
44 if actor:
45 canonical_actor = actor_identity_resolver.canonicalize(actor, db=db)
46 stmt = stmt.where(TrackedRepository.actor == canonical_actor)
47
48 repositories = db.scalars(stmt).all()
49 return [_to_response(repository) for repository in repositories]
50
51
52@router.get("/{repository_id}", response_model=TrackedRepositoryResponse)
53def get_tracked_repository(
54 repository_id: int,
55 db: Session = Depends(get_db),
56) -> TrackedRepositoryResponse:
57 return _to_response(_get_repository_or_404(db, repository_id))
58
59
60@router.post("", response_model=TrackedRepositoryResponse, status_code=status.HTTP_201_CREATED)
61def create_tracked_repository(
62 payload: TrackedRepositoryCreateRequest,
63 db: Session = Depends(get_db),
64 actor_identity_resolver: ActorIdentityResolver = Depends(get_actor_identity_resolver),
65) -> TrackedRepositoryResponse:
66 canonical_actor = actor_identity_resolver.canonicalize(payload.actor, db=db)
67 repository = TrackedRepository(
68 service="git",
69 actor=canonical_actor,
70 repo_name=canonicalize_repository_name(canonical_actor, payload.repo_name),
71 )
72 db.add(repository)
73 try:
74 db.commit()
75 except IntegrityError as exc:
76 db.rollback()
77 raise HTTPException(status_code=409, detail="Tracked repository already exists.") from exc
78
79 db.refresh(repository)
80 return _to_response(repository)
81
82
83@router.patch("/{repository_id}", response_model=TrackedRepositoryResponse)
84def update_tracked_repository(
85 repository_id: int,
86 payload: TrackedRepositoryUpdateRequest,
87 db: Session = Depends(get_db),
88 actor_identity_resolver: ActorIdentityResolver = Depends(get_actor_identity_resolver),
89) -> TrackedRepositoryResponse:
90 repository = _get_repository_or_404(db, repository_id)
91
92 if payload.actor is not None:
93 repository.actor = actor_identity_resolver.canonicalize(payload.actor, db=db)
94
95 if payload.repo_name is not None:
96 repository.repo_name = canonicalize_repository_name(repository.actor, payload.repo_name)
97
98 db.add(repository)
99 try:
100 db.commit()
101 except IntegrityError as exc:
102 db.rollback()
103 raise HTTPException(status_code=409, detail="Tracked repository already exists.") from exc
104
105 db.refresh(repository)
106 return _to_response(repository)
107
108
109@router.delete("/{repository_id}", status_code=status.HTTP_204_NO_CONTENT)
110def delete_tracked_repository(
111 repository_id: int,
112 db: Session = Depends(get_db),
113) -> Response:
114 repository = _get_repository_or_404(db, repository_id)
115 db.delete(repository)
116 db.commit()
117 return Response(status_code=status.HTTP_204_NO_CONTENT)