github.com/google/go-github/v52@v52.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/en/rest/pulls/reviews#list-reviews-for-a-pull-request 104 func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*PullRequestReview, *Response, error) { 105 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) 106 u, err := addOptions(u, opts) 107 if err != nil { 108 return nil, nil, err 109 } 110 111 req, err := s.client.NewRequest("GET", u, nil) 112 if err != nil { 113 return nil, nil, err 114 } 115 116 var reviews []*PullRequestReview 117 resp, err := s.client.Do(ctx, req, &reviews) 118 if err != nil { 119 return nil, resp, err 120 } 121 122 return reviews, resp, nil 123 } 124 125 // GetReview fetches the specified pull request review. 126 // 127 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#get-a-review-for-a-pull-request 128 func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { 129 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) 130 131 req, err := s.client.NewRequest("GET", u, nil) 132 if err != nil { 133 return nil, nil, err 134 } 135 136 review := new(PullRequestReview) 137 resp, err := s.client.Do(ctx, req, review) 138 if err != nil { 139 return nil, resp, err 140 } 141 142 return review, resp, nil 143 } 144 145 // DeletePendingReview deletes the specified pull request pending review. 146 // 147 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#delete-a-pending-review-for-a-pull-request 148 func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { 149 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) 150 151 req, err := s.client.NewRequest("DELETE", u, nil) 152 if err != nil { 153 return nil, nil, err 154 } 155 156 review := new(PullRequestReview) 157 resp, err := s.client.Do(ctx, req, review) 158 if err != nil { 159 return nil, resp, err 160 } 161 162 return review, resp, nil 163 } 164 165 // ListReviewComments lists all the comments for the specified review. 166 // 167 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#list-comments-for-a-pull-request-review 168 func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opts *ListOptions) ([]*PullRequestComment, *Response, error) { 169 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) 170 u, err := addOptions(u, opts) 171 if err != nil { 172 return nil, nil, err 173 } 174 175 req, err := s.client.NewRequest("GET", u, nil) 176 if err != nil { 177 return nil, nil, err 178 } 179 180 var comments []*PullRequestComment 181 resp, err := s.client.Do(ctx, req, &comments) 182 if err != nil { 183 return nil, resp, err 184 } 185 186 return comments, resp, nil 187 } 188 189 // CreateReview creates a new review on the specified pull request. 190 // 191 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request 192 // 193 // In order to use multi-line comments, you must use the "comfort fade" preview. 194 // This replaces the use of the "Position" field in comments with 4 new fields: 195 // 196 // [Start]Side, and [Start]Line. 197 // 198 // These new fields must be used for ALL comments (including single-line), 199 // with the following restrictions (empirically observed, so subject to change). 200 // 201 // For single-line "comfort fade" comments, you must use: 202 // 203 // Path: &path, // as before 204 // Body: &body, // as before 205 // Side: &"RIGHT" (or "LEFT") 206 // Line: &123, // NOT THE SAME AS POSITION, this is an actual line number. 207 // 208 // If StartSide or StartLine is used with single-line comments, a 422 is returned. 209 // 210 // For multi-line "comfort fade" comments, you must use: 211 // 212 // Path: &path, // as before 213 // Body: &body, // as before 214 // StartSide: &"RIGHT" (or "LEFT") 215 // Side: &"RIGHT" (or "LEFT") 216 // StartLine: &120, 217 // Line: &125, 218 // 219 // Suggested edits are made by commenting on the lines to replace, and including the 220 // suggested edit in a block like this (it may be surrounded in non-suggestion markdown): 221 // 222 // ```suggestion 223 // Use this instead. 224 // It is waaaaaay better. 225 // ``` 226 func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { 227 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) 228 229 req, err := s.client.NewRequest("POST", u, review) 230 if err != nil { 231 return nil, nil, err 232 } 233 234 // Detect which style of review comment is being used. 235 if isCF, err := review.isComfortFadePreview(); err != nil { 236 return nil, nil, err 237 } else if isCF { 238 // If the review comments are using the comfort fade preview fields, 239 // then pass the comfort fade header. 240 req.Header.Set("Accept", mediaTypeMultiLineCommentsPreview) 241 } 242 243 r := new(PullRequestReview) 244 resp, err := s.client.Do(ctx, req, r) 245 if err != nil { 246 return nil, resp, err 247 } 248 249 return r, resp, nil 250 } 251 252 // UpdateReview updates the review summary on the specified pull request. 253 // 254 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#update-a-review-for-a-pull-request 255 func (s *PullRequestsService) UpdateReview(ctx context.Context, owner, repo string, number int, reviewID int64, body string) (*PullRequestReview, *Response, error) { 256 opts := &struct { 257 Body string `json:"body"` 258 }{Body: body} 259 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) 260 261 req, err := s.client.NewRequest("PUT", u, opts) 262 if err != nil { 263 return nil, nil, err 264 } 265 266 review := &PullRequestReview{} 267 resp, err := s.client.Do(ctx, req, review) 268 if err != nil { 269 return nil, resp, err 270 } 271 272 return review, resp, nil 273 } 274 275 // SubmitReview submits a specified review on the specified pull request. 276 // 277 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#submit-a-review-for-a-pull-request 278 func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { 279 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) 280 281 req, err := s.client.NewRequest("POST", u, review) 282 if err != nil { 283 return nil, nil, err 284 } 285 286 r := new(PullRequestReview) 287 resp, err := s.client.Do(ctx, req, r) 288 if err != nil { 289 return nil, resp, err 290 } 291 292 return r, resp, nil 293 } 294 295 // DismissReview dismisses a specified review on the specified pull request. 296 // 297 // GitHub API docs: https://docs.github.com/en/rest/pulls/reviews#dismiss-a-review-for-a-pull-request 298 func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { 299 u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) 300 301 req, err := s.client.NewRequest("PUT", u, review) 302 if err != nil { 303 return nil, nil, err 304 } 305 306 r := new(PullRequestReview) 307 resp, err := s.client.Do(ctx, req, r) 308 if err != nil { 309 return nil, resp, err 310 } 311 312 return r, resp, nil 313 }