Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions src/safe_outputs/submit_pr_review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,18 +168,31 @@ async fn fetch_authenticated_user_id(
Ok(Ok(user_id))
}

/// Shared context for ADO PR vote API calls.
struct PrVoteCtx<'a> {
client: &'a reqwest::Client,
base_url: &'a str,
encoded_repo: &'a str,
pull_request_id: i32,
token: &'a str,
}

/// Self-approval guard: returns `Some(failure)` when a positive vote targets a PR the
/// authenticated user created; returns `None` when the vote is allowed to proceed.
async fn check_self_approval(
client: &reqwest::Client,
base_url: &str,
encoded_repo: &str,
pull_request_id: i32,
ctx: &PrVoteCtx<'_>,
user_id: &str,
event: &str,
vote_value: i32,
token: &str,
) -> anyhow::Result<Option<ExecutionResult>> {
let PrVoteCtx {
client,
base_url,
encoded_repo,
pull_request_id,
token,
} = ctx;
let pull_request_id = *pull_request_id;
if vote_value <= 0 {
return Ok(None);
}
Expand Down Expand Up @@ -231,15 +244,19 @@ async fn check_self_approval(
/// PUTs the review vote to the ADO reviewers endpoint.
/// Returns `Err` on network errors, `Ok(Some(failure))` on HTTP errors, `Ok(None)` on success.
async fn submit_vote(
client: &reqwest::Client,
base_url: &str,
encoded_repo: &str,
ctx: &PrVoteCtx<'_>,
encoded_user_id: &str,
pull_request_id: i32,
event: &str,
vote_value: i32,
token: &str,
) -> anyhow::Result<Option<ExecutionResult>> {
let PrVoteCtx {
client,
base_url,
encoded_repo,
pull_request_id,
token,
} = ctx;
let pull_request_id = *pull_request_id;
let vote_url = format!(
"{}/{}/pullRequests/{}/reviewers/{}?api-version=7.1",
base_url, encoded_repo, pull_request_id, encoded_user_id
Expand Down Expand Up @@ -427,16 +444,20 @@ impl Executor for SubmitPrReviewResult {
Err(failure) => return Ok(failure),
};

let vote_ctx = PrVoteCtx {
client: &client,
base_url: &base_url,
encoded_repo: &encoded_repo,
pull_request_id: self.pull_request_id,
token,
};

// Self-approval guard: prevent the agent from approving PRs it created.
if let Some(failure) = check_self_approval(
&client,
&base_url,
&encoded_repo,
self.pull_request_id,
&vote_ctx,
&user_id,
&self.event,
vote_value,
token,
)
.await?
{
Expand All @@ -446,14 +467,10 @@ impl Executor for SubmitPrReviewResult {
// PUT vote to reviewers endpoint
let encoded_user_id = utf8_percent_encode(&user_id, PATH_SEGMENT).to_string();
if let Some(failure) = submit_vote(
&client,
&base_url,
&encoded_repo,
&vote_ctx,
&encoded_user_id,
self.pull_request_id,
&self.event,
vote_value,
token,
)
.await?
{
Expand Down