@@ -247,19 +247,21 @@ func (s service) GetDiff(ctx context.Context, rs string, id uint64, opt *changes
}
return []byte(diff), nil
}
}
func (s service) ListComments(ctx context.Context, rs string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Comment, error) {
// TODO: Respect opt.Start and opt.Length, if given.
func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) {
repo, err := ghRepoSpec(rs)
if err != nil {
// TODO: Map to 400 Bad Request HTTP error.
return nil, err
}
var comments []issues.Comment
type event struct { // Common fields for all events.
Actor githubqlActor
CreatedAt githubql.DateTime
}
var q struct {
Repository struct {
PullRequest struct {
Author githubqlActor
PublishedAt githubql.DateTime
@@ -267,159 +269,10 @@ func (s service) ListComments(ctx context.Context, rs string, id uint64, opt *ch
Editor *githubqlActor
Body githubql.String
ReactionGroups reactionGroups
ViewerCanUpdate githubql.Boolean
// TODO: Combine with first page of Comments...
} `graphql:"pullRequest(number:$prNumber)"`
} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
}
variables := map[string]interface{}{
"repositoryOwner": githubql.String(repo.Owner),
"repositoryName": githubql.String(repo.Repo),
"prNumber": githubql.Int(id),
}
err = s.clV4.Query(ctx, &q, variables)
if err != nil {
return comments, err
}
pr := q.Repository.PullRequest
reactions, err := s.reactions(pr.ReactionGroups)
if err != nil {
return comments, err
}
var edited *issues.Edited
if pr.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*pr.Editor),
At: pr.LastEditedAt.Time,
}
}
comments = append(comments, issues.Comment{
ID: prDescriptionCommentID,
User: ghActor(pr.Author),
CreatedAt: pr.PublishedAt.Time,
Edited: edited,
Body: string(pr.Body),
Reactions: reactions,
Editable: bool(pr.ViewerCanUpdate),
})
{
var q struct {
Repository struct {
PullRequest struct {
Comments struct {
Nodes []struct {
DatabaseID githubql.Int
Author githubqlActor
PublishedAt githubql.DateTime
LastEditedAt *githubql.DateTime
Editor *githubqlActor
Body githubql.String
ReactionGroups reactionGroups
ViewerCanUpdate githubql.Boolean
}
PageInfo struct {
EndCursor githubql.String
HasNextPage githubql.Boolean
}
} `graphql:"comments(first:1,after:$commentsCursor)"` // TODO: Increase page size too 100 after done testing.
Reviews struct {
Nodes []struct {
DatabaseID uint64
Author githubqlActor
PublishedAt githubql.DateTime
LastEditedAt *githubql.DateTime
Editor *githubqlActor
Body string
ViewerCanUpdate bool
}
PageInfo struct {
EndCursor githubql.String
HasNextPage githubql.Boolean
}
} `graphql:"reviews(first:100)"` // TODO: Figure out how to make pagination across 2 resource types work...
} `graphql:"pullRequest(number:$prNumber)"`
} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
}
variables := map[string]interface{}{
"repositoryOwner": githubql.String(repo.Owner),
"repositoryName": githubql.String(repo.Repo),
"prNumber": githubql.Int(id),
"commentsCursor": (*githubql.String)(nil),
}
for {
err := s.clV4.Query(ctx, &q, variables)
if err != nil {
return comments, err
}
for _, comment := range q.Repository.PullRequest.Comments.Nodes {
reactions, err := s.reactions(comment.ReactionGroups)
if err != nil {
return comments, err
}
var edited *issues.Edited
if comment.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*comment.Editor),
At: comment.LastEditedAt.Time,
}
}
comments = append(comments, issues.Comment{
ID: uint64(comment.DatabaseID),
User: ghActor(comment.Author),
CreatedAt: comment.PublishedAt.Time,
Edited: edited,
Body: string(comment.Body),
Reactions: reactions,
Editable: bool(comment.ViewerCanUpdate),
})
}
if !q.Repository.PullRequest.Comments.PageInfo.HasNextPage {
break
}
variables["commentsCursor"] = githubql.NewString(q.Repository.PullRequest.Comments.PageInfo.EndCursor)
}
for _, review := range q.Repository.PullRequest.Reviews.Nodes {
if review.Body == "" {
continue
}
var edited *issues.Edited
if review.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*review.Editor),
At: review.LastEditedAt.Time,
}
}
comments = append(comments, issues.Comment{
ID: review.DatabaseID,
User: ghActor(review.Author),
CreatedAt: review.PublishedAt.Time,
Edited: edited,
Body: review.Body,
Editable: review.ViewerCanUpdate,
})
}
}
return comments, nil
}
func (s service) ListEvents(ctx context.Context, rs string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Event, error) {
repo, err := ghRepoSpec(rs)
if err != nil {
// TODO: Map to 400 Bad Request HTTP error.
return nil, err
}
type event struct { // Common fields for all events.
Actor githubqlActor
CreatedAt githubql.DateTime
}
var q struct {
Repository struct {
PullRequest struct {
Timeline struct {
Nodes []struct {
Typename string `graphql:"__typename"`
ClosedEvent struct {
event
@@ -466,11 +319,34 @@ func (s service) ListEvents(ctx context.Context, rs string, id uint64, opt *chan
Author githubqlActor
CreatedAt githubql.DateTime
State githubql.PullRequestReviewState
} `graphql:"...on PullRequestReview"`
}
} `graphql:"timeline(first:100)"` // TODO: Paginate?
} `graphql:"timeline(first:100)"` // TODO: Pagination...
Comments struct {
Nodes []struct {
DatabaseID githubql.Int
Author githubqlActor
PublishedAt githubql.DateTime
LastEditedAt *githubql.DateTime
Editor *githubqlActor
Body githubql.String
ReactionGroups reactionGroups
ViewerCanUpdate githubql.Boolean
}
} `graphql:"comments(first:100)"` // TODO: Pagination...
Reviews struct {
Nodes []struct {
DatabaseID uint64
Author githubqlActor
PublishedAt githubql.DateTime
LastEditedAt *githubql.DateTime
Editor *githubqlActor
Body string
ViewerCanUpdate bool
}
} `graphql:"reviews(first:100)"` // TODO: Figure out how to make pagination across 2 resource types work...
} `graphql:"pullRequest(number:$prNumber)"`
} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
}
variables := map[string]interface{}{
"repositoryOwner": githubql.String(repo.Owner),
@@ -479,11 +355,76 @@ func (s service) ListEvents(ctx context.Context, rs string, id uint64, opt *chan
}
err = s.clV4.Query(ctx, &q, variables)
if err != nil {
return nil, err
}
var events []issues.Event
var timeline []interface{}
{
pr := q.Repository.PullRequest
reactions, err := s.reactions(pr.ReactionGroups)
if err != nil {
return timeline, err
}
var edited *issues.Edited
if pr.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*pr.Editor),
At: pr.LastEditedAt.Time,
}
}
timeline = append(timeline, issues.Comment{
ID: prDescriptionCommentID,
User: ghActor(pr.Author),
CreatedAt: pr.PublishedAt.Time,
Edited: edited,
Body: string(pr.Body),
Reactions: reactions,
Editable: bool(pr.ViewerCanUpdate),
})
}
for _, comment := range q.Repository.PullRequest.Comments.Nodes {
reactions, err := s.reactions(comment.ReactionGroups)
if err != nil {
return timeline, err
}
var edited *issues.Edited
if comment.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*comment.Editor),
At: comment.LastEditedAt.Time,
}
}
timeline = append(timeline, issues.Comment{
ID: uint64(comment.DatabaseID),
User: ghActor(comment.Author),
CreatedAt: comment.PublishedAt.Time,
Edited: edited,
Body: string(comment.Body),
Reactions: reactions,
Editable: bool(comment.ViewerCanUpdate),
})
}
for _, review := range q.Repository.PullRequest.Reviews.Nodes {
if review.Body == "" {
continue
}
var edited *issues.Edited
if review.LastEditedAt != nil {
edited = &issues.Edited{
By: ghActor(*review.Editor),
At: review.LastEditedAt.Time,
}
}
timeline = append(timeline, issues.Comment{
ID: review.DatabaseID,
User: ghActor(review.Author),
CreatedAt: review.PublishedAt.Time,
Edited: edited,
Body: review.Body,
Editable: review.ViewerCanUpdate,
})
}
for _, event := range q.Repository.PullRequest.Timeline.Nodes {
e := issues.Event{
//ID: 0, // TODO.
Type: ghEventType(event.Typename),
}
@@ -540,26 +481,28 @@ func (s service) ListEvents(ctx context.Context, rs string, id uint64, opt *chan
e.Actor = ghActor(event.PullRequestReview.Author)
e.CreatedAt = event.PullRequestReview.CreatedAt.Time
default:
continue
}
events = append(events, e)
timeline = append(timeline, e)
}
// We can't just delegate pagination to GitHub because our events don't match up 1:1,
// We can't just delegate pagination to GitHub because our timeline items don't match up 1:1,
// we want to skip IssueComment in the timeline, etc.
// THINK: Or maybe they can be made to match up? Investigate.
if opt != nil {
start := opt.Start
if start > len(events) {
start = len(events)
if start > len(timeline) {
start = len(timeline)
}
end := opt.Start + opt.Length
if end > len(events) {
end = len(events)
if end > len(timeline) {
end = len(timeline)
}
events = events[start:end]
timeline = timeline[start:end]
}
return events, nil
return timeline, nil
}
type repoSpec struct {
Owner string
Repo string