dmitri.shuralyov.com/service/change/...

githubapi: Add support for review inline comments.
dmitshur committed 6 years ago commit a8308642db459d9207a70575f35b455877cb007a
Collapse all
githubapi/githubapi.go
@@ -3,10 +3,11 @@ package githubapi

import (
	"context"
	"fmt"
	"log"
	"sort"
	"strings"

	"dmitri.shuralyov.com/changes"
	"github.com/google/go-github/github"
	"github.com/shurcooL/githubql"
@@ -331,26 +332,32 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch
								OID string
								URL string
							}
							MergeRefName string
						} `graphql:"...on MergedEvent"`
						PullRequestReview struct {
							Author    githubqlActor
							CreatedAt githubql.DateTime
							State     githubql.PullRequestReviewState
						} `graphql:"...on PullRequestReview"`
					}
				} `graphql:"timeline(first:100)"` // TODO: Pagination...

				// Need to use PullRequest.Reviews rather than PullRequest.Timeline.PullRequestReview,
				// because the latter is missing single-inline-reply reviews (as of 2018-02-08).
				Reviews struct {
					Nodes []struct {
						DatabaseID      uint64
						Author          githubqlActor
						PublishedAt     githubql.DateTime
						LastEditedAt    *githubql.DateTime
						Editor          *githubqlActor
						State           githubql.PullRequestReviewState
						Body            string
						ViewerCanUpdate bool
						Comments        struct {
							Nodes []struct {
								Path             string
								OriginalPosition int
								Body             string
							}
						} `graphql:"comments(first:100)"` // TODO: Pagination... Figure out how to make pagination across 2 resource types work...
					}
				} `graphql:"reviews(first:100)"` // TODO: Pagination... Figure out how to make pagination across 2 resource types work...
			} `graphql:"pullRequest(number:$prNumber)"`
		} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
	}
@@ -412,27 +419,40 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch
			Reactions: reactions,
			Editable:  comment.ViewerCanUpdate,
		})
	}
	for _, review := range q.Repository.PullRequest.Reviews.Nodes {
		if review.Body == "" {
			continue
		}
		var edited *changes.Edited
		if review.LastEditedAt != nil {
			edited = &changes.Edited{
				By: ghActor(*review.Editor),
				At: review.LastEditedAt.Time,
			}
		}
		timeline = append(timeline, changes.Comment{
		var cs []changes.InlineComment
		for _, comment := range review.Comments.Nodes {
			cs = append(cs, changes.InlineComment{
				File: comment.Path,
				Line: comment.OriginalPosition,
				Body: comment.Body,
			})
		}
		sort.Slice(cs, func(i, j int) bool {
			if cs[i].File == cs[j].File {
				return cs[i].Line < cs[j].Line
			}
			return cs[i].File < cs[j].File
		})
		timeline = append(timeline, changes.Review{
			ID:        review.DatabaseID,
			User:      ghActor(review.Author),
			CreatedAt: review.PublishedAt.Time,
			Edited:    edited,
			State:     ghPRReviewState(review.State),
			Body:      review.Body,
			Editable:  review.ViewerCanUpdate,
			Comments:  cs,
		})
	}
	for _, event := range q.Repository.PullRequest.Timeline.Nodes {
		e := changes.TimelineItem{
		//ID: 0, // TODO.
@@ -493,23 +513,10 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch
			e.Payload = changes.MergedEvent{
				CommitID:      event.MergedEvent.Commit.OID,
				CommitHTMLURL: event.MergedEvent.Commit.URL,
				RefName:       event.MergedEvent.MergeRefName,
			}
		case "PullRequestReview":
			switch event.PullRequestReview.State {
			case githubql.PullRequestReviewStateApproved:
				// TODO: Make this a thing that ListComments returns, etc. After all, it can have a non-empty body.
				e.Payload = changes.ApprovedEvent{}
			case githubql.PullRequestReviewStateChangesRequested:
				// TODO: Make this a thing that ListComments returns, etc. After all, it can have a non-empty body.
				e.Payload = changes.ChangesRequestedEvent{}
			default:
				continue
			}
			e.Actor = ghActor(event.PullRequestReview.Author)
			e.CreatedAt = event.PullRequestReview.CreatedAt.Time
		default:
			continue
		}
		timeline = append(timeline, e)
	}
@@ -634,10 +641,28 @@ func ghPRState(state githubql.PullRequestState) changes.State {
	default:
		panic("unreachable")
	}
}

// ghPRReviewState converts a GitHub PullRequestReviewState to changes.ReviewState.
func ghPRReviewState(state githubql.PullRequestReviewState) changes.ReviewState {
	switch state {
	case githubql.PullRequestReviewStateApproved:
		return changes.Approved
	case githubql.PullRequestReviewStateCommented:
		return changes.Commented
	case githubql.PullRequestReviewStateChangesRequested:
		return changes.ChangesRequested
	case githubql.PullRequestReviewStatePending:
		panic("PullRequestReviewStatePending not implemented") // TODO.
	case githubql.PullRequestReviewStateDismissed:
		panic("PullRequestReviewStateDismissed not implemented") // TODO.
	default:
		panic("unreachable")
	}
}

// ghColor converts a GitHub color hex string like "ff0000"
// into an issues.RGB value.
func ghColor(hex string) issues.RGB {
	var c issues.RGB
	fmt.Sscanf(hex, "%02x%02x%02x", &c.R, &c.G, &c.B)
timeline.go
@@ -104,9 +104,9 @@ type (
		CommitID      string
		CommitHTMLURL string // Optional.
		RefName       string
	}

	// TODO: Merge into Comment or so.
	// TODO: Remove, now that these have been merged into Review.
	ApprovedEvent         struct{}
	ChangesRequestedEvent struct{}
)