github.com/google/go-github/v74@v74.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 }