fix: post-then-cleanup flow, remove dead code, pagination
- PostReview now returns *Review (id + user login from response) - Delete flow: post first, then delete stale reviews by same user - No read:user scope needed (identity from POST response) - Removed GetAuthenticatedUser (requires scope we lack) - ListReviews: full pagination (loops until partial page) - envOrDefaultBool: case-insensitive, whitespace-trimmed - action.yml: document accepted boolean values - Tests updated for new PostReview signature
This commit is contained in:
+40
-39
@@ -129,9 +129,9 @@ func (c *Client) GetFileContentRef(ctx context.Context, owner, repo, filepath, r
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// PostReview submits a review to a PR.
|
||||
// PostReview submits a review to a PR and returns the created review.
|
||||
// event should be "APPROVED" or "REQUEST_CHANGES".
|
||||
func (c *Client) PostReview(ctx context.Context, owner, repo string, number int, event, body string) error {
|
||||
func (c *Client) PostReview(ctx context.Context, owner, repo string, number int, event, body string) (*Review, error) {
|
||||
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews", c.baseURL, owner, repo, number)
|
||||
|
||||
payload := struct {
|
||||
@@ -144,27 +144,36 @@ func (c *Client) PostReview(ctx context.Context, owner, repo string, number int,
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal review payload: %w", err)
|
||||
return nil, fmt.Errorf("marshal review payload: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create review request: %w", err)
|
||||
return nil, fmt.Errorf("create review request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "token "+c.token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("post review: %w", err)
|
||||
return nil, fmt.Errorf("post review: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("post review failed (status %d): %s", resp.StatusCode, string(respBody))
|
||||
return nil, fmt.Errorf("post review failed (status %d): %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
return nil
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read review response: %w", err)
|
||||
}
|
||||
var review Review
|
||||
if err := json.Unmarshal(respBody, &review); err != nil {
|
||||
return nil, fmt.Errorf("parse review response: %w", err)
|
||||
}
|
||||
return &review, nil
|
||||
}
|
||||
|
||||
func (c *Client) doGet(ctx context.Context, reqURL string) ([]byte, error) {
|
||||
@@ -277,41 +286,33 @@ type Review struct {
|
||||
Stale bool `json:"stale"`
|
||||
}
|
||||
|
||||
// GetAuthenticatedUser returns the login name of the token's owner.
|
||||
func (c *Client) GetAuthenticatedUser(ctx context.Context) (string, error) {
|
||||
reqURL := fmt.Sprintf("%s/api/v1/user", c.baseURL)
|
||||
body, err := c.doGet(ctx, reqURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get authenticated user: %w", err)
|
||||
}
|
||||
var user struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &user); err != nil {
|
||||
return "", fmt.Errorf("parse user response: %w", err)
|
||||
}
|
||||
if user.Login == "" {
|
||||
return "", fmt.Errorf("empty login in user response")
|
||||
}
|
||||
return user.Login, nil
|
||||
}
|
||||
|
||||
// ListReviews returns all reviews on a pull request.
|
||||
// Paginates through all pages to ensure no reviews are missed.
|
||||
func (c *Client) ListReviews(ctx context.Context, owner, repo string, number int) ([]Review, error) {
|
||||
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews",
|
||||
c.baseURL,
|
||||
url.PathEscape(owner),
|
||||
url.PathEscape(repo),
|
||||
number)
|
||||
body, err := c.doGet(ctx, reqURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list reviews: %w", err)
|
||||
const pageSize = 50
|
||||
var all []Review
|
||||
for page := 1; ; page++ {
|
||||
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews?limit=%d&page=%d",
|
||||
c.baseURL,
|
||||
url.PathEscape(owner),
|
||||
url.PathEscape(repo),
|
||||
number,
|
||||
pageSize,
|
||||
page)
|
||||
body, err := c.doGet(ctx, reqURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list reviews (page %d): %w", page, err)
|
||||
}
|
||||
var batch []Review
|
||||
if err := json.Unmarshal(body, &batch); err != nil {
|
||||
return nil, fmt.Errorf("parse reviews (page %d): %w", page, err)
|
||||
}
|
||||
all = append(all, batch...)
|
||||
if len(batch) < pageSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
var reviews []Review
|
||||
if err := json.Unmarshal(body, &reviews); err != nil {
|
||||
return nil, fmt.Errorf("parse reviews: %w", err)
|
||||
}
|
||||
return reviews, nil
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// DeleteReview deletes a review by ID. The token must belong to the review author.
|
||||
|
||||
Reference in New Issue
Block a user