feat: paginate issue capture repositories (#403)
This commit is contained in:
1 parent
33a6d1bb6d
commit
9ea590a65c
16 files changed
+393
-46
No files matched your search
+50
-2
@@ -133,6 +133,14 @@ class WorkItems(list[dict]):
|
||||
self.pagination = pagination
|
||||
|
||||
|
||||
class RepositoryItems(list[dict]):
|
||||
"""A list-compatible repository page carrying truthful pagination metadata."""
|
||||
|
||||
def __init__(self, items: list[dict], pagination: dict):
|
||||
super().__init__(items)
|
||||
self.pagination = pagination
|
||||
|
||||
|
||||
class WorkRouteUnavailableError(ValueError):
|
||||
"""Raised when a shared route no longer belongs in the current user's queue."""
|
||||
|
||||
@@ -261,8 +269,48 @@ async def current_user() -> dict:
|
||||
return await fetch("user")
|
||||
|
||||
|
||||
async def repos() -> list[dict]:
|
||||
return await fetch("user/repos?limit=50")
|
||||
async def repo_page(page: int = 1, limit: int = 50) -> dict:
|
||||
"""Load one bounded page of repositories available to the current user."""
|
||||
response = await _get_client().get(
|
||||
"/api/v1/user/repos",
|
||||
headers=_auth(),
|
||||
params={"page": page, "limit": limit},
|
||||
)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
if not isinstance(payload, list):
|
||||
raise ValueError("Gitea repository response was not a list")
|
||||
items = [item for item in payload if isinstance(item, dict)]
|
||||
try:
|
||||
total = max(len(items), int(response.headers.get("X-Total-Count", len(items))))
|
||||
except (TypeError, ValueError):
|
||||
total = len(items)
|
||||
return {
|
||||
"items": items,
|
||||
"page": page,
|
||||
"total": total,
|
||||
"has_more": page * limit < total,
|
||||
}
|
||||
|
||||
|
||||
async def repos() -> RepositoryItems:
|
||||
result = await repo_page()
|
||||
return RepositoryItems(
|
||||
result["items"],
|
||||
{key: result[key] for key in ("page", "total", "has_more")},
|
||||
)
|
||||
|
||||
|
||||
async def repository_access(repository: str) -> dict | None:
|
||||
"""Return a repository only when the authenticated user can access it."""
|
||||
response = await _get_client().get(
|
||||
f"/api/v1/repos/{repository}", headers=_auth()
|
||||
)
|
||||
if response.status_code == 404:
|
||||
return None
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
return payload if isinstance(payload, dict) else None
|
||||
|
||||
|
||||
WORK_SEARCHES = {
|
||||
|
||||
+38
-22
@@ -502,6 +502,9 @@ def _context_payload(user_data, repo_data, issues_data, prs_data) -> dict:
|
||||
and all(field in p for field in ("id", "number", "title", "state", "html_url"))
|
||||
]
|
||||
payload = compute(user_model, repo_models, issue_models, pr_models).model_dump()
|
||||
repository_pagination = getattr(repo_data, "pagination", None)
|
||||
if isinstance(repository_pagination, dict):
|
||||
payload["repository_pagination"] = repository_pagination
|
||||
pagination = {}
|
||||
pagination.update(getattr(issues_data, "pagination", {}))
|
||||
pagination.update(getattr(prs_data, "pagination", {}))
|
||||
@@ -1213,6 +1216,36 @@ async def context() -> JSONResponse:
|
||||
return JSONResponse(_context_payload(user_data, repo_data, issues_data, prs_data))
|
||||
|
||||
|
||||
@app.get("/api/v1/repositories")
|
||||
async def repository_page(
|
||||
page: int = Query(default=1, ge=1, le=1000),
|
||||
limit: int = Query(default=50, ge=1, le=50),
|
||||
) -> JSONResponse:
|
||||
"""Return one bounded repository page for lazy issue-capture selection."""
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
gitea_proxy.repo_page(page=page, limit=limit),
|
||||
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
|
||||
)
|
||||
items = [
|
||||
Repo(
|
||||
id=item["id"], name=item["name"], full_name=item["full_name"],
|
||||
description=item.get("description") or "", url=item["html_url"],
|
||||
updated_at=item.get("updated_at", ""),
|
||||
).model_dump()
|
||||
for item in result.get("items", [])
|
||||
if isinstance(item, dict)
|
||||
and all(field in item for field in ("id", "name", "full_name", "html_url"))
|
||||
]
|
||||
return JSONResponse({**result, "items": items})
|
||||
except Exception:
|
||||
return JSONResponse(
|
||||
{"error": "Repositories could not be loaded. Please retry."},
|
||||
status_code=503,
|
||||
headers={"Retry-After": "1"},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/v1/background-identity")
|
||||
async def background_identity() -> JSONResponse:
|
||||
"""Return only the account key required to safely drain a browser outbox."""
|
||||
@@ -2191,13 +2224,7 @@ async def repository_milestones(owner: str, repo: str):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def load_milestones():
|
||||
available = await gitea_proxy.repos()
|
||||
accessible = {
|
||||
item.get("full_name")
|
||||
for item in (available if isinstance(available, list) else [])
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
if repository not in accessible:
|
||||
if await gitea_proxy.repository_access(repository) is None:
|
||||
raise HTTPException(status_code=404, detail="Repository not found")
|
||||
return await gitea_proxy.repo_milestones(repository)
|
||||
|
||||
@@ -2220,13 +2247,7 @@ async def repository_labels(owner: str, repo: str):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def load_labels():
|
||||
available = await gitea_proxy.repos()
|
||||
accessible = {
|
||||
item.get("full_name")
|
||||
for item in (available if isinstance(available, list) else [])
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
if repository not in accessible:
|
||||
if await gitea_proxy.repository_access(repository) is None:
|
||||
raise HTTPException(status_code=404, detail="Repository not found")
|
||||
return await gitea_proxy.repo_labels(repository)
|
||||
|
||||
@@ -2252,16 +2273,11 @@ async def create_assigned_issue(
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def create_issue():
|
||||
user, available = await asyncio.gather(
|
||||
gitea_proxy.current_user(), gitea_proxy.repos()
|
||||
user, accessible = await asyncio.gather(
|
||||
gitea_proxy.current_user(), gitea_proxy.repository_access(repository)
|
||||
)
|
||||
login = user.get("login") if isinstance(user, dict) else None
|
||||
accessible = {
|
||||
item.get("full_name")
|
||||
for item in (available if isinstance(available, list) else [])
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
if not login or repository not in accessible:
|
||||
if not login or accessible is None:
|
||||
raise HTTPException(status_code=404, detail="Repository not found")
|
||||
if creation.label_ids:
|
||||
available_labels = await gitea_proxy.repo_labels(repository)
|
||||
|
||||
Reference in new issue
Block a user