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