feat: load pull review feedback on demand (Closes #1342)
This commit is contained in:
1 parent
50817bed9a
commit
611e79fc1e
7 files changed
+364
-28
No files matched your search
+34
-14
@@ -2938,20 +2938,6 @@ async def pull_completion_review(repository: str, number: int) -> dict:
|
||||
diff, diff_truncated = diff_result
|
||||
previews = _diff_previews(diff, diff_truncated)
|
||||
reviewers = _normalize_reviewer_statuses(pull, reviews, sha)
|
||||
feedback_ids = _latest_feedback_review_ids(reviews)
|
||||
if feedback_ids:
|
||||
feedback_payloads = await asyncio.gather(*(
|
||||
fetch(f"{base}/reviews/{review_id}/comments")
|
||||
for review_id in feedback_ids.values()
|
||||
))
|
||||
feedback = {
|
||||
login: _normalize_review_comments(payload)
|
||||
for login, payload in zip(feedback_ids, feedback_payloads)
|
||||
}
|
||||
for reviewer in reviewers:
|
||||
comments = feedback.get(reviewer["login"].casefold(), [])
|
||||
if comments:
|
||||
reviewer["comments"] = comments
|
||||
return {
|
||||
"repository": repository,
|
||||
"number": number,
|
||||
@@ -2985,6 +2971,40 @@ async def pull_completion_review(repository: str, number: int) -> dict:
|
||||
}
|
||||
|
||||
|
||||
async def pull_review_feedback(
|
||||
repository: str, number: int, review_id: int, expected_head_sha: str
|
||||
) -> dict:
|
||||
"""Load optional inline feedback only for a review on the confirmed pull head."""
|
||||
base = f"repos/{repository}/pulls/{number}"
|
||||
pull, reviews = await asyncio.gather(
|
||||
fetch(base),
|
||||
fetch(f"{base}/reviews?limit=100"),
|
||||
)
|
||||
head = pull.get("head") if isinstance(pull, dict) else None
|
||||
head_sha = head.get("sha") if isinstance(head, dict) else None
|
||||
if head_sha != expected_head_sha:
|
||||
raise StaleReviewError("Pull request head changed")
|
||||
review_items = reviews if isinstance(reviews, list) else []
|
||||
latest_ids = set(_latest_feedback_review_ids(review_items).values())
|
||||
review = next(
|
||||
(
|
||||
item for item in review_items
|
||||
if isinstance(item, dict) and item.get("id") == review_id
|
||||
),
|
||||
None,
|
||||
)
|
||||
if review is None or review_id not in latest_ids:
|
||||
raise StaleReviewError("Review feedback is no longer current")
|
||||
comments = await fetch(f"{base}/reviews/{review_id}/comments")
|
||||
reviewed_head = review.get("commit_id")
|
||||
return {
|
||||
"review_id": review_id,
|
||||
"head_sha": head_sha,
|
||||
"reviewed_head_sha": reviewed_head if isinstance(reviewed_head, str) else "",
|
||||
"comments": _normalize_review_comments(comments),
|
||||
}
|
||||
|
||||
|
||||
async def release_receipt_status(repository: str, commit_sha: str) -> dict:
|
||||
"""Return CI and release evidence for one exact merge commit."""
|
||||
status, releases = await asyncio.gather(
|
||||
|
||||
+44
@@ -6640,6 +6640,50 @@ async def assigned_pull_review_data(
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/v1/repos/{owner}/{repo}/pulls/{number}/reviews/{review_id}/feedback")
|
||||
async def assigned_pull_review_feedback(
|
||||
owner: str,
|
||||
repo: str,
|
||||
number: int = PathParam(gt=0),
|
||||
review_id: int = PathParam(gt=0),
|
||||
expected_head_sha: str = Query(min_length=7, max_length=64, pattern=r"^[A-Fa-f0-9]+$"),
|
||||
):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def load_feedback():
|
||||
if not await gitea_proxy.is_assigned_pull(repository, number):
|
||||
raise HTTPException(status_code=404, detail="Assigned pull request not found")
|
||||
return await gitea_proxy.pull_review_feedback(
|
||||
repository, number, review_id, expected_head_sha
|
||||
)
|
||||
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
load_feedback(), timeout=REVIEW_DETAIL_TIMEOUT_SECONDS
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except gitea_proxy.StaleReviewError:
|
||||
return JSONResponse(
|
||||
{"error": "The pull request or review changed. Reload review data before retrying."},
|
||||
status_code=409,
|
||||
headers={"Cache-Control": "no-store"},
|
||||
)
|
||||
except TimeoutError:
|
||||
return JSONResponse(
|
||||
{"error": "Loading review feedback timed out. Please retry."},
|
||||
status_code=503,
|
||||
headers={"Cache-Control": "no-store", "Retry-After": "1"},
|
||||
)
|
||||
except Exception:
|
||||
return JSONResponse(
|
||||
{"error": "Review feedback is temporarily unavailable. Please retry."},
|
||||
status_code=503,
|
||||
headers={"Cache-Control": "no-store", "Retry-After": "1"},
|
||||
)
|
||||
return JSONResponse(result, headers={"Cache-Control": "no-store"})
|
||||
|
||||
|
||||
@app.get("/api/v1/repos/{owner}/{repo}/pulls/{number}/checks")
|
||||
async def assigned_pull_checks(
|
||||
owner: str, repo: str, number: int = PathParam(gt=0)
|
||||
|
||||
Reference in new issue
Block a user