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