feat: submit pull reviews from mobile My Work (#155)
This commit is contained in:
1 parent
7f5e671f8d
commit
9b81dce8aa
7 files changed
+443
-11
No files matched your search
@@ -14,6 +14,10 @@ REVIEW_DIFF_MAX_LINES = 400
|
||||
_client: httpx.AsyncClient | None = None
|
||||
|
||||
|
||||
class StaleReviewError(ValueError):
|
||||
"""Raised before mutation when a pull request head changed during review."""
|
||||
|
||||
|
||||
def _auth() -> dict[str, str]:
|
||||
headers: dict[str, str] = {"Accept": "application/json"}
|
||||
if GITEA_TOKEN:
|
||||
@@ -416,6 +420,43 @@ async def pull_review_detail(repository: str, number: int) -> dict:
|
||||
}
|
||||
|
||||
|
||||
async def submit_pull_review(
|
||||
repository: str,
|
||||
number: int,
|
||||
expected_head_sha: str,
|
||||
decision: str,
|
||||
body: str,
|
||||
) -> dict:
|
||||
base = f"repos/{repository}/pulls/{number}"
|
||||
pull = await fetch(base)
|
||||
head = pull.get("head") if isinstance(pull, dict) else None
|
||||
current_sha = head.get("sha") if isinstance(head, dict) else None
|
||||
if current_sha != expected_head_sha:
|
||||
raise StaleReviewError("Pull request changed while it was being reviewed")
|
||||
response = await _get_client().post(
|
||||
f"/api/v1/{base}/reviews",
|
||||
headers=_auth(),
|
||||
json={
|
||||
"body": body,
|
||||
"event": {
|
||||
"comment": "COMMENT",
|
||||
"approve": "APPROVE",
|
||||
"request_changes": "REQUEST_CHANGES",
|
||||
}[decision],
|
||||
"commit_id": current_sha,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
review = response.json()
|
||||
if not isinstance(review, dict):
|
||||
raise ValueError("Gitea review response was not an object")
|
||||
return {
|
||||
"id": review.get("id"),
|
||||
"state": review.get("state") or "COMMENT",
|
||||
"url": _safe_web_url(review.get("html_url")),
|
||||
}
|
||||
|
||||
|
||||
async def activity_events(user: dict | None = None) -> list[dict]:
|
||||
if user is None:
|
||||
user = await current_user()
|
||||
|
||||
+50
@@ -93,6 +93,19 @@ class NotificationReply(BaseModel):
|
||||
return value
|
||||
|
||||
|
||||
class PullReviewSubmission(BaseModel):
|
||||
decision: str
|
||||
body: str = Field(max_length=10_000)
|
||||
expected_head_sha: str
|
||||
|
||||
@field_validator("decision")
|
||||
@classmethod
|
||||
def validate_decision(cls, value: str) -> str:
|
||||
if value not in {"comment", "approve", "request_changes"}:
|
||||
raise ValueError("unsupported review decision")
|
||||
return value
|
||||
|
||||
|
||||
def _context_payload(user_data, repo_data, issues_data, prs_data) -> dict:
|
||||
user_model = User(
|
||||
id=user_data["id"],
|
||||
@@ -680,3 +693,40 @@ async def review_detail(owner: str, repo: str, number: int):
|
||||
{"error": "Pull request review details are temporarily unavailable"},
|
||||
status_code=503,
|
||||
)
|
||||
|
||||
|
||||
@app.post("/api/v1/repos/{owner}/{repo}/pulls/{number}/review", status_code=201)
|
||||
async def submit_review(
|
||||
submission: PullReviewSubmission, owner: str, repo: str, number: int
|
||||
):
|
||||
repository = f"{owner}/{repo}"
|
||||
|
||||
async def submit_requested_review():
|
||||
if not await is_requested_review(repository, number):
|
||||
raise HTTPException(status_code=404, detail="Review request not found")
|
||||
return await gitea_proxy.submit_pull_review(
|
||||
repository,
|
||||
number,
|
||||
submission.expected_head_sha,
|
||||
submission.decision,
|
||||
submission.body,
|
||||
)
|
||||
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
submit_requested_review(), timeout=REVIEW_DETAIL_TIMEOUT_SECONDS
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except gitea_proxy.StaleReviewError:
|
||||
return JSONResponse(
|
||||
{"error": "New commits were pushed. Refresh the review before submitting."},
|
||||
status_code=409,
|
||||
)
|
||||
except Exception:
|
||||
return JSONResponse(
|
||||
{"error": "The review could not be submitted. Your draft is safe; please retry."},
|
||||
status_code=503,
|
||||
headers={"Retry-After": "1"},
|
||||
)
|
||||
return JSONResponse(result, status_code=201)
|
||||
Reference in new issue
Block a user