github.com/google/go-github/v64@v64.0.0/github/pulls_reviews.go (about)

     1  // Copyright 2016 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  )
    13  
    14  var ErrMixedCommentStyles = errors.New("cannot use both position and side/line form comments")
    15  
    16  // PullRequestReview represents a review of a pull request.
    17  type PullRequestReview struct {
    18  	ID             *int64     `json:"id,omitempty"`
    19  	NodeID         *string    `json:"node_id,omitempty"`
    20  	User           *User      `json:"user,omitempty"`
    21  	Body           *string    `json:"body,omitempty"`
    22  	SubmittedAt    *Timestamp `json:"submitted_at,omitempty"`
    23  	CommitID       *string    `json:"commit_id,omitempty"`
    24  	HTMLURL        *string    `json:"html_url,omitempty"`
    25  	PullRequestURL *string    `json:"pull_request_url,omitempty"`
    26  	State          *string    `json:"state,omitempty"`
    27  	// AuthorAssociation is the comment author's relationship to the issue's repository.
    28  	// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE".
    29  	AuthorAssociation *string `json:"author_association,omitempty"`
    30  }
    31  
    32  func (p PullRequestReview) String() string {
    33  	return Stringify(p)
    34  }
    35  
    36  // DraftReviewComment represents a comment part of the review.
    37  type DraftReviewComment struct {
    38  	Path     *string `json:"path,omitempty"`
    39  	Position *int    `json:"position,omitempty"`
    40  	Body     *string `json:"body,omitempty"`
    41  
    42  	// The new comfort-fade-preview fields
    43  	StartSide *string `json:"start_side,omitempty"`
    44  	Side      *string `json:"side,omitempty"`
    45  	StartLine *int    `json:"start_line,omitempty"`
    46  	Line      *int    `json:"line,omitempty"`
    47  }
    48  
    49  func (c DraftReviewComment) String() string {
    50  	return Stringify(c)
    51  }
    52  
    53  // PullRequestReviewRequest represents a request to create a review.
    54  type PullRequestReviewRequest struct {
    55  	NodeID   *string               `json:"node_id,omitempty"`
    56  	CommitID *string               `json:"commit_id,omitempty"`
    57  	Body     *string               `json:"body,omitempty"`
    58  	Event    *string               `json:"event,omitempty"`
    59  	Comments []*DraftReviewComment `json:"comments,omitempty"`
    60  }
    61  
    62  func (r PullRequestReviewRequest) String() string {
    63  	return Stringify(r)
    64  }
    65  
    66  func (r *PullRequestReviewRequest) isComfortFadePreview() (bool, error) {
    67  	var isCF *bool
    68  	for _, comment := range r.Comments {
    69  		if comment == nil {
    70  			continue
    71  		}
    72  		hasPos := comment.Position != nil
    73  		hasComfortFade := (comment.StartSide != nil) || (comment.Side != nil) ||
    74  			(comment.StartLine != nil) || (comment.Line != nil)
    75  
    76  		switch {
    77  		case hasPos && hasComfortFade:
    78  			return false, ErrMixedCommentStyles
    79  		case hasPos && isCF != nil && *isCF:
    80  			return false, ErrMixedCommentStyles
    81  		case hasComfortFade && isCF != nil && !*isCF:
    82  			return false, ErrMixedCommentStyles
    83  		}
    84  		isCF = &hasComfortFade
    85  	}
    86  	if isCF != nil {
    87  		return *isCF, nil
    88  	}
    89  	return false, nil
    90  }
    91  
    92  // PullRequestReviewDismissalRequest represents a request to dismiss a review.
    93  type PullRequestReviewDismissalRequest struct {
    94  	Message *string `json:"message,omitempty"`
    95  }
    96  
    97  func (r PullRequestReviewDismissalRequest) String() string {
    98  	return Stringify(r)
    99  }
   100  
   101  // ListReviews lists all reviews on the specified pull request.
   102  //
   103  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#list-reviews-for-a-pull-request
   104  //
   105  //meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews
   106  func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*PullRequestReview, *Response, error) {
   107  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number)
   108  	u, err := addOptions(u, opts)
   109  	if err != nil {
   110  		return nil, nil, err
   111  	}
   112  
   113  	req, err := s.client.NewRequest("GET", u, nil)
   114  	if err != nil {
   115  		return nil, nil, err
   116  	}
   117  
   118  	var reviews []*PullRequestReview
   119  	resp, err := s.client.Do(ctx, req, &reviews)
   120  	if err != nil {
   121  		return nil, resp, err
   122  	}
   123  
   124  	return reviews, resp, nil
   125  }
   126  
   127  // GetReview fetches the specified pull request review.
   128  //
   129  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#get-a-review-for-a-pull-request
   130  //
   131  //meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}
   132  func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) {
   133  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID)
   134  
   135  	req, err := s.client.NewRequest("GET", u, nil)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  
   140  	review := new(PullRequestReview)
   141  	resp, err := s.client.Do(ctx, req, review)
   142  	if err != nil {
   143  		return nil, resp, err
   144  	}
   145  
   146  	return review, resp, nil
   147  }
   148  
   149  // DeletePendingReview deletes the specified pull request pending review.
   150  //
   151  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#delete-a-pending-review-for-a-pull-request
   152  //
   153  //meta:operation DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}
   154  func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) {
   155  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID)
   156  
   157  	req, err := s.client.NewRequest("DELETE", u, nil)
   158  	if err != nil {
   159  		return nil, nil, err
   160  	}
   161  
   162  	review := new(PullRequestReview)
   163  	resp, err := s.client.Do(ctx, req, review)
   164  	if err != nil {
   165  		return nil, resp, err
   166  	}
   167  
   168  	return review, resp, nil
   169  }
   170  
   171  // ListReviewComments lists all the comments for the specified review.
   172  //
   173  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#list-comments-for-a-pull-request-review
   174  //
   175  //meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments
   176  func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opts *ListOptions) ([]*PullRequestComment, *Response, error) {
   177  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID)
   178  	u, err := addOptions(u, opts)
   179  	if err != nil {
   180  		return nil, nil, err
   181  	}
   182  
   183  	req, err := s.client.NewRequest("GET", u, nil)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  
   188  	var comments []*PullRequestComment
   189  	resp, err := s.client.Do(ctx, req, &comments)
   190  	if err != nil {
   191  		return nil, resp, err
   192  	}
   193  
   194  	return comments, resp, nil
   195  }
   196  
   197  // CreateReview creates a new review on the specified pull request.
   198  //
   199  // In order to use multi-line comments, you must use the "comfort fade" preview.
   200  // This replaces the use of the "Position" field in comments with 4 new fields:
   201  //
   202  //	[Start]Side, and [Start]Line.
   203  //
   204  // These new fields must be used for ALL comments (including single-line),
   205  // with the following restrictions (empirically observed, so subject to change).
   206  //
   207  // For single-line "comfort fade" comments, you must use:
   208  //
   209  //	Path:  &path,  // as before
   210  //	Body:  &body,  // as before
   211  //	Side:  &"RIGHT" (or "LEFT")
   212  //	Line:  &123,  // NOT THE SAME AS POSITION, this is an actual line number.
   213  //
   214  // If StartSide or StartLine is used with single-line comments, a 422 is returned.
   215  //
   216  // For multi-line "comfort fade" comments, you must use:
   217  //
   218  //	Path:      &path,  // as before
   219  //	Body:      &body,  // as before
   220  //	StartSide: &"RIGHT" (or "LEFT")
   221  //	Side:      &"RIGHT" (or "LEFT")
   222  //	StartLine: &120,
   223  //	Line:      &125,
   224  //
   225  // Suggested edits are made by commenting on the lines to replace, and including the
   226  // suggested edit in a block like this (it may be surrounded in non-suggestion markdown):
   227  //
   228  //	```suggestion
   229  //	Use this instead.
   230  //	It is waaaaaay better.
   231  //	```
   232  //
   233  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#create-a-review-for-a-pull-request
   234  //
   235  //meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews
   236  func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) {
   237  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number)
   238  
   239  	req, err := s.client.NewRequest("POST", u, review)
   240  	if err != nil {
   241  		return nil, nil, err
   242  	}
   243  
   244  	// Detect which style of review comment is being used.
   245  	if isCF, err := review.isComfortFadePreview(); err != nil {
   246  		return nil, nil, err
   247  	} else if isCF {
   248  		// If the review comments are using the comfort fade preview fields,
   249  		// then pass the comfort fade header.
   250  		req.Header.Set("Accept", mediaTypeMultiLineCommentsPreview)
   251  	}
   252  
   253  	r := new(PullRequestReview)
   254  	resp, err := s.client.Do(ctx, req, r)
   255  	if err != nil {
   256  		return nil, resp, err
   257  	}
   258  
   259  	return r, resp, nil
   260  }
   261  
   262  // UpdateReview updates the review summary on the specified pull request.
   263  //
   264  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#update-a-review-for-a-pull-request
   265  //
   266  //meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}
   267  func (s *PullRequestsService) UpdateReview(ctx context.Context, owner, repo string, number int, reviewID int64, body string) (*PullRequestReview, *Response, error) {
   268  	opts := &struct {
   269  		Body string `json:"body"`
   270  	}{Body: body}
   271  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID)
   272  
   273  	req, err := s.client.NewRequest("PUT", u, opts)
   274  	if err != nil {
   275  		return nil, nil, err
   276  	}
   277  
   278  	review := &PullRequestReview{}
   279  	resp, err := s.client.Do(ctx, req, review)
   280  	if err != nil {
   281  		return nil, resp, err
   282  	}
   283  
   284  	return review, resp, nil
   285  }
   286  
   287  // SubmitReview submits a specified review on the specified pull request.
   288  //
   289  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#submit-a-review-for-a-pull-request
   290  //
   291  //meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events
   292  func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) {
   293  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID)
   294  
   295  	req, err := s.client.NewRequest("POST", u, review)
   296  	if err != nil {
   297  		return nil, nil, err
   298  	}
   299  
   300  	r := new(PullRequestReview)
   301  	resp, err := s.client.Do(ctx, req, r)
   302  	if err != nil {
   303  		return nil, resp, err
   304  	}
   305  
   306  	return r, resp, nil
   307  }
   308  
   309  // DismissReview dismisses a specified review on the specified pull request.
   310  //
   311  // GitHub API docs: https://docs.github.com/rest/pulls/reviews#dismiss-a-review-for-a-pull-request
   312  //
   313  //meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals
   314  func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) {
   315  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID)
   316  
   317  	req, err := s.client.NewRequest("PUT", u, review)
   318  	if err != nil {
   319  		return nil, nil, err
   320  	}
   321  
   322  	r := new(PullRequestReview)
   323  	resp, err := s.client.Do(ctx, req, r)
   324  	if err != nil {
   325  		return nil, resp, err
   326  	}
   327  
   328  	return r, resp, nil
   329  }