feat: apply repository templates during mobile issue filing
Closes #845
This commit is contained in:
1 parent
2ac6b1f56b
commit
31f95589cb
10 files changed
+344
-8
No files matched your search
@@ -30,7 +30,7 @@ FEATURE_SOURCES = {
|
||||
"device-setup": ("static/install-app.js", "static/mobile-device-setup.js"),
|
||||
"security-center": ("static/security-center.js",),
|
||||
"today-timer": (
|
||||
"static/commands.js", "static/saved-searches.js", "static/task-overlay-history.js", "static/search-preview.js", "static/search-defer.js", "static/mobile-search-viewport.js", "static/my-work.js", "static/protect-today.js", "static/mobile-task-dock.js", "static/mobile-queue-launcher.js", "static/update-triage-session.js", "static/update-review-handoff.js", "static/update-triage-launcher.js", "static/update-triage-gesture.js", "static/notification-undo.js", "static/today-timer.js", "static/today-recap.js",
|
||||
"static/today-completion.js", "static/work-detail-position.js", "static/commands.js", "static/saved-searches.js", "static/task-overlay-history.js", "static/search-preview.js", "static/search-defer.js", "static/mobile-search-viewport.js", "static/my-work.js", "static/protect-today.js", "static/mobile-task-dock.js", "static/mobile-queue-launcher.js", "static/update-triage-session.js", "static/update-review-handoff.js", "static/update-triage-launcher.js", "static/update-triage-gesture.js", "static/notification-undo.js", "static/today-timer.js", "static/today-recap.js",
|
||||
"static/today-rollover.js", "static/later-work.js", "static/drafts.js", "static/unfiled-captures.js",
|
||||
"static/assign-and-start.js", "static/queue-today.js",
|
||||
"static/draft-filing-session.js", "static/draft-capacity-dialog.js", "static/work-selection.js",
|
||||
|
||||
@@ -1400,6 +1400,51 @@ async def repo_labels(repository: str) -> list[dict]:
|
||||
]
|
||||
|
||||
|
||||
async def repo_issue_templates(repository: str) -> list[dict]:
|
||||
"""Return a small allowlisted view of repository issue templates."""
|
||||
response = await _get_client().get(
|
||||
f"/api/v1/repos/{repository}/issue_templates", headers=_auth()
|
||||
)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
if payload is None:
|
||||
return []
|
||||
if not isinstance(payload, list):
|
||||
raise ValueError("Gitea issue templates response was not a list")
|
||||
templates = []
|
||||
for item in payload:
|
||||
if len(templates) >= 20:
|
||||
break
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
name = item.get("name")
|
||||
if not isinstance(name, str) or not name.strip():
|
||||
continue
|
||||
name = name.strip()[:80]
|
||||
identifier = re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-")[:80]
|
||||
labels = []
|
||||
for label in item.get("labels", []) if isinstance(item.get("labels"), list) else []:
|
||||
if not isinstance(label, str):
|
||||
continue
|
||||
clean = label.strip()[:80]
|
||||
if clean and clean not in labels:
|
||||
labels.append(clean)
|
||||
if len(labels) >= 20:
|
||||
break
|
||||
scalar = lambda key, limit: (
|
||||
item.get(key, "")[:limit] if isinstance(item.get(key), str) else ""
|
||||
)
|
||||
templates.append({
|
||||
"id": identifier or f"template-{len(templates) + 1}",
|
||||
"name": name,
|
||||
"about": scalar("about", 240).strip(),
|
||||
"title": scalar("title", 255),
|
||||
"body": scalar("content", 9000).strip(),
|
||||
"labels": labels,
|
||||
})
|
||||
return templates
|
||||
|
||||
|
||||
async def repo_milestones(repository: str) -> list[dict]:
|
||||
response = await _get_client().get(
|
||||
f"/api/v1/repos/{repository}/milestones",
|
||||
|
||||
+21
@@ -4528,6 +4528,27 @@ async def repository_labels(owner: str, repo: str):
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/v1/repos/{owner}/{repo}/issue-templates")
|
||||
async def repository_issue_templates(owner: str, repo: str):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def load_templates():
|
||||
if await gitea_proxy.repository_access(repository) is None:
|
||||
raise HTTPException(status_code=404, detail="Repository not found")
|
||||
return await gitea_proxy.repo_issue_templates(repository)
|
||||
|
||||
try:
|
||||
return await asyncio.wait_for(load_templates(), timeout=ISSUE_ACTION_TIMEOUT_SECONDS)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
return JSONResponse(
|
||||
{"error": "Issue types could not be loaded. Blank issue creation is still available."},
|
||||
status_code=503,
|
||||
headers={"Retry-After": "1"},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/v1/repos/{owner}/{repo}/issue-assignees")
|
||||
async def repository_issue_assignees(owner: str, repo: str):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
Reference in new issue
Block a user