@@ -1,7 +1,7 @@ // Package changes provides a changes service definition. package changes // Package change provides a change service definition. package change import ( "context" "time" @@ -17,11 +17,11 @@ type Service interface { Count(ctx context.Context, repo string, opt ListOptions) (uint64, error) // Get a change. Get(ctx context.Context, repo string, id uint64) (Change, error) // ListTimeline lists timeline items (changes.Comment, changes.Review, changes.TimelineItem) for specified change id. // ListTimeline lists timeline items (change.Comment, change.Review, change.TimelineItem) for specified change id. ListTimeline(ctx context.Context, repo string, id uint64, opt *ListTimelineOptions) ([]interface{}, error) // ListCommits lists change commits. ListCommits(ctx context.Context, repo string, id uint64) ([]Commit, error) // Get a change diff. GetDiff(ctx context.Context, repo string, id uint64, opt *GetDiffOptions) ([]byte, error)
@@ -4,48 +4,48 @@ import ( "context" "fmt" "os" "time" "dmitri.shuralyov.com/changes" "dmitri.shuralyov.com/service/change" "github.com/shurcooL/users" ) type Service struct{} var s = struct { changes []struct { changes.Change change.Change Timeline []interface{} Commits []changes.Commit Commits []change.Commit Diff []byte } }{ changes: []struct { changes.Change change.Change Timeline []interface{} Commits []changes.Commit Commits []change.Commit Diff []byte }{ { Change: changes.Change{ Change: change.Change{ ID: 1, State: changes.OpenState, State: change.OpenState, Title: "Initial implementation of woff2.", Labels: nil, Author: shurcool, CreatedAt: time.Now().UTC().Add(-5 * time.Minute), Replies: 0, Commits: 1, }, Timeline: []interface{}{ changes.Review{ change.Review{ User: users.User{Login: "Eric Grosse", AvatarURL: "https://lh6.googleusercontent.com/-_sdEtv2PRxk/AAAAAAAAAAI/AAAAAAAAAAA/aE1Q66Cuvb4/s100-p/photo.jpg"}, CreatedAt: time.Now().UTC().Add(-1 * time.Minute), State: changes.Approved, Comments: []changes.InlineComment{ State: change.Approved, Comments: []change.InlineComment{ { File: "LICENSE", Line: 26, Body: "Ok by me, but how was this chosen?", }, @@ -55,11 +55,11 @@ var s = struct { Body: "As someone who reads the server logs, my gut feeling is that 1 QPS of Lookup logs will give me sufficient data to tell me the system is working, without creating a big mess.", }, }, }, }, Commits: []changes.Commit{{ Commits: []change.Commit{{ SHA: "4a911c4a1eabcc20a66ccc5c983dede401da2796", Message: "Initial implementation of woff2.\n\nMaybe some additional details here.", Author: shurcool, AuthorTime: time.Now().UTC().Add(-10 * time.Minute), }}, @@ -67,34 +67,34 @@ var s = struct { }, }, } // List changes. func (*Service) List(ctx context.Context, repo string, opt changes.ListOptions) ([]changes.Change, error) { func (*Service) List(ctx context.Context, repo string, opt change.ListOptions) ([]change.Change, error) { if repo != "dmitri.shuralyov.com/font/woff2" { return nil, os.ErrNotExist } var cs []changes.Change var cs []change.Change for _, c := range s.changes { cs = append(cs, c.Change) } return cs, nil } // Count changes. func (*Service) Count(ctx context.Context, repo string, opt changes.ListOptions) (uint64, error) { func (*Service) Count(ctx context.Context, repo string, opt change.ListOptions) (uint64, error) { if repo != "dmitri.shuralyov.com/font/woff2" { return 0, os.ErrNotExist } var counts func(s changes.State) bool var counts func(s change.State) bool switch opt.Filter { case changes.FilterOpen: counts = func(s changes.State) bool { return s == changes.OpenState } case changes.FilterClosedMerged: counts = func(s changes.State) bool { return s == changes.ClosedState || s == changes.MergedState } case changes.FilterAll: counts = func(s changes.State) bool { return true } case change.FilterOpen: counts = func(s change.State) bool { return s == change.OpenState } case change.FilterClosedMerged: counts = func(s change.State) bool { return s == change.ClosedState || s == change.MergedState } case change.FilterAll: counts = func(s change.State) bool { return true } default: // TODO: Map to 400 Bad Request HTTP error. return 0, fmt.Errorf("opt.State has unsupported value %q", opt.Filter) } var count uint64 @@ -105,35 +105,35 @@ func (*Service) Count(ctx context.Context, repo string, opt changes.ListOptions) } return count, nil } // Get a change. func (*Service) Get(ctx context.Context, repo string, id uint64) (changes.Change, error) { func (*Service) Get(ctx context.Context, repo string, id uint64) (change.Change, error) { if repo != "dmitri.shuralyov.com/font/woff2" || id != 1 { return changes.Change{}, os.ErrNotExist return change.Change{}, os.ErrNotExist } return s.changes[0].Change, nil } // ListTimeline lists timeline items (changes.Comment, changes.TimelineItem) for specified change id. func (*Service) ListTimeline(ctx context.Context, repo string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) { // ListTimeline lists timeline items (change.Comment, change.Review, change.TimelineItem) for specified change id. func (*Service) ListTimeline(ctx context.Context, repo string, id uint64, opt *change.ListTimelineOptions) ([]interface{}, error) { if repo != "dmitri.shuralyov.com/font/woff2" || id != 1 { return nil, os.ErrNotExist } return s.changes[0].Timeline, nil } // ListCommits lists change commits. func (*Service) ListCommits(ctx context.Context, repo string, id uint64) ([]changes.Commit, error) { func (*Service) ListCommits(ctx context.Context, repo string, id uint64) ([]change.Commit, error) { if repo != "dmitri.shuralyov.com/font/woff2" || id != 1 { return nil, os.ErrNotExist } return s.changes[0].Commits, nil } // Get a change diff. func (*Service) GetDiff(ctx context.Context, repo string, id uint64, opt *changes.GetDiffOptions) ([]byte, error) { func (*Service) GetDiff(ctx context.Context, repo string, id uint64, opt *change.GetDiffOptions) ([]byte, error) { if repo != "dmitri.shuralyov.com/font/woff2" || id != 1 { return nil, os.ErrNotExist } return s.changes[0].Diff, nil }
@@ -1,6 +1,6 @@ // Package gerritapi implements a read-only changes.Service using Gerrit API client. // Package gerritapi implements a read-only change.Service using Gerrit API client. package gerritapi import ( "context" "fmt" @@ -8,18 +8,18 @@ import ( "sort" "strings" "time" "unicode" "dmitri.shuralyov.com/changes" "dmitri.shuralyov.com/service/change" "github.com/andygrunwald/go-gerrit" "github.com/shurcooL/users" ) // NewService creates a Gerrit-backed issues.Service using given Gerrit client. // client must be non-nil. func NewService(client *gerrit.Client) changes.Service { func NewService(client *gerrit.Client) change.Service { s := service{ cl: client, domain: client.BaseURL().Host, //users: users, } @@ -37,20 +37,20 @@ type service struct { //currentUser users.UserSpec //currentUserErr error } func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ([]changes.Change, error) { func (s service) List(ctx context.Context, rs string, opt change.ListOptions) ([]change.Change, error) { project := project(rs) var query string switch opt.Filter { case changes.FilterOpen: case change.FilterOpen: query = fmt.Sprintf("project:%s status:open", project) case changes.FilterClosedMerged: case change.FilterClosedMerged: // "status:closed" is equivalent to "(status:abandoned OR status:merged)". query = fmt.Sprintf("project:%s status:closed", project) case changes.FilterAll: case change.FilterAll: query = fmt.Sprintf("project:%s", project) } cs, _, err := s.cl.Changes.QueryChanges(&gerrit.QueryChangeOptions{ QueryOptions: gerrit.QueryOptions{ Query: []string{query}, @@ -61,16 +61,16 @@ func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ( }, }) if err != nil { return nil, err } var is []changes.Change var is []change.Change for _, chg := range *cs { if chg.Status == "DRAFT" { continue } is = append(is, changes.Change{ is = append(is, change.Change{ ID: uint64(chg.Number), State: state(chg.Status), Title: chg.Subject, //Labels: labels, // TODO. Author: s.gerritUser(chg.Owner), @@ -83,75 +83,75 @@ func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ( return is[i].CreatedAt.After(is[j].CreatedAt) }) return is, nil } func (s service) Count(_ context.Context, repo string, opt changes.ListOptions) (uint64, error) { func (s service) Count(_ context.Context, repo string, opt change.ListOptions) (uint64, error) { // TODO. return 0, nil } func (s service) Get(ctx context.Context, _ string, id uint64) (changes.Change, error) { func (s service) Get(ctx context.Context, _ string, id uint64) (change.Change, error) { chg, _, err := s.cl.Changes.GetChange(fmt.Sprint(id), &gerrit.ChangeOptions{ AdditionalFields: []string{"DETAILED_ACCOUNTS", "ALL_REVISIONS"}, }) if err != nil { return changes.Change{}, err return change.Change{}, err } if chg.Status == "DRAFT" { return changes.Change{}, os.ErrNotExist return change.Change{}, os.ErrNotExist } return changes.Change{ return change.Change{ ID: id, State: state(chg.Status), Title: chg.Subject, Author: s.gerritUser(chg.Owner), CreatedAt: time.Time(chg.Created), Commits: len(chg.Revisions), }, nil } func state(status string) changes.State { func state(status string) change.State { switch status { case "NEW": return changes.OpenState return change.OpenState case "ABANDONED": return changes.ClosedState return change.ClosedState case "MERGED": return changes.MergedState return change.MergedState case "DRAFT": panic("not sure how to deal with DRAFT status") default: panic("unreachable") } } func (s service) ListCommits(ctx context.Context, _ string, id uint64) ([]changes.Commit, error) { func (s service) ListCommits(ctx context.Context, _ string, id uint64) ([]change.Commit, error) { chg, _, err := s.cl.Changes.GetChange(fmt.Sprint(id), &gerrit.ChangeOptions{ AdditionalFields: []string{"DETAILED_ACCOUNTS", "ALL_REVISIONS"}, //AdditionalFields: []string{"ALL_REVISIONS", "ALL_COMMITS"}, // TODO: Consider using git committer/author instead... }) if err != nil { return nil, err } if chg.Status == "DRAFT" { return nil, os.ErrNotExist } commits := make([]changes.Commit, len(chg.Revisions)) commits := make([]change.Commit, len(chg.Revisions)) for sha, r := range chg.Revisions { commits[r.Number-1] = changes.Commit{ commits[r.Number-1] = change.Commit{ SHA: sha, Message: fmt.Sprintf("Patch Set %d", r.Number), // TODO: r.Uploader and r.Created describe the committer, not author. Author: s.gerritUser(r.Uploader), AuthorTime: time.Time(r.Created), } } return commits, nil } func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *changes.GetDiffOptions) ([]byte, error) { func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *change.GetDiffOptions) ([]byte, error) { switch opt { case nil: diff, _, err := s.cl.Changes.GetPatch(fmt.Sprint(id), "current", nil) if err != nil { return nil, err @@ -241,11 +241,11 @@ func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *changes. } return []byte(diff), nil } } func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) { func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *change.ListTimelineOptions) ([]interface{}, error) { // TODO: Pagination. Respect opt.Start and opt.Length, if given. chg, _, err := s.cl.Changes.GetChangeDetail(fmt.Sprint(id), nil) if err != nil { return nil, err @@ -254,11 +254,11 @@ func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *cha if err != nil { return nil, err } var timeline []interface{} { timeline = append(timeline, changes.Comment{ timeline = append(timeline, change.Comment{ ID: 0, User: s.gerritUser(chg.Owner), CreatedAt: time.Time(chg.Created), Body: "", // THINK: Include commit message or no? Editable: false, @@ -266,14 +266,14 @@ func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *cha } for idx, message := range chg.Messages { if strings.HasPrefix(message.Tag, "autogenerated:") { switch message.Tag[len("autogenerated:"):] { case "gerrit:merged": timeline = append(timeline, changes.TimelineItem{ timeline = append(timeline, change.TimelineItem{ Actor: s.gerritUser(message.Author), CreatedAt: time.Time(message.Date), Payload: changes.MergedEvent{ Payload: change.MergedEvent{ CommitID: message.Message[46:86], // TODO: Make safer. RefName: chg.Branch, }, }) } @@ -281,32 +281,32 @@ func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *cha } label, body, ok := parseMessage(message.Message) if !ok { continue } var state changes.ReviewState var state change.ReviewState switch label { default: state = changes.Commented state = change.Commented case "Code-Review+2": state = changes.Approved state = change.Approved case "Code-Review-2": state = changes.ChangesRequested state = change.ChangesRequested } var cs []changes.InlineComment var cs []change.InlineComment for file, comments := range *comments { for _, c := range comments { if time.Time(c.Updated).Equal(time.Time(message.Date)) { cs = append(cs, changes.InlineComment{ cs = append(cs, change.InlineComment{ File: file, Line: c.Line, Body: c.Message, }) } } } timeline = append(timeline, changes.Review{ timeline = append(timeline, change.Review{ ID: uint64(idx), // TODO: message.ID is not uint64; e.g., "bfba753d015916303152305cee7152ea7a112fe0". User: s.gerritUser(message.Author), CreatedAt: time.Time(message.Date), State: state, Body: body,
@@ -1,29 +1,29 @@ // Package githubapi implements a read-only changes.Service using GitHub API clients. // Package githubapi implements a read-only change.Service using GitHub API clients. package githubapi import ( "context" "fmt" "log" "sort" "strings" "dmitri.shuralyov.com/changes" "dmitri.shuralyov.com/service/change" "github.com/google/go-github/github" "github.com/shurcooL/githubql" "github.com/shurcooL/issues" "github.com/shurcooL/notifications" "github.com/shurcooL/reactions" "github.com/shurcooL/users" ghusers "github.com/shurcooL/users/githubapi" ) // NewService creates a GitHub-backed changes.Service using given GitHub clients. // NewService creates a GitHub-backed change.Service using given GitHub clients. // It uses notifications service, if not nil. At this time it infers the current user // from the client (its authentication info), and cannot be used to serve multiple users. func NewService(clientV3 *github.Client, clientV4 *githubql.Client, notifications notifications.ExternalService) (changes.Service, error) { func NewService(clientV3 *github.Client, clientV4 *githubql.Client, notifications notifications.ExternalService) (change.Service, error) { users, err := ghusers.NewService(clientV3) if err != nil { return nil, err } currentUser, err := users.GetAuthenticated(context.Background()) @@ -49,23 +49,23 @@ type service struct { } // We use 0 as a special ID for the comment that is the PR description. This comment is edited differently. const prDescriptionCommentID uint64 = 0 func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ([]changes.Change, error) { func (s service) List(ctx context.Context, rs string, opt change.ListOptions) ([]change.Change, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return nil, err } var states []githubql.PullRequestState switch opt.Filter { case changes.FilterOpen: case change.FilterOpen: states = []githubql.PullRequestState{githubql.PullRequestStateOpen} case changes.FilterClosedMerged: case change.FilterClosedMerged: states = []githubql.PullRequestState{githubql.PullRequestStateClosed, githubql.PullRequestStateMerged} case changes.FilterAll: case change.FilterAll: states = nil // No states to filter the PRs by. default: // TODO: Map to 400 Bad Request HTTP error. return nil, fmt.Errorf("opt.State has unsupported value %q", opt.Filter) } @@ -98,20 +98,20 @@ func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ( } err = s.clV4.Query(ctx, &q, variables) if err != nil { return nil, err } var is []changes.Change var is []change.Change for _, pr := range q.Repository.PullRequests.Nodes { var labels []issues.Label for _, l := range pr.Labels.Nodes { labels = append(labels, issues.Label{ Name: l.Name, Color: ghColor(l.Color), }) } is = append(is, changes.Change{ is = append(is, change.Change{ ID: pr.Number, State: ghPRState(pr.State), Title: pr.Title, Labels: labels, Author: ghActor(pr.Author), @@ -120,23 +120,23 @@ func (s service) List(ctx context.Context, rs string, opt changes.ListOptions) ( }) } return is, nil } func (s service) Count(ctx context.Context, rs string, opt changes.ListOptions) (uint64, error) { func (s service) Count(ctx context.Context, rs string, opt change.ListOptions) (uint64, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return 0, err } var states []githubql.PullRequestState switch opt.Filter { case changes.FilterOpen: case change.FilterOpen: states = []githubql.PullRequestState{githubql.PullRequestStateOpen} case changes.FilterClosedMerged: case change.FilterClosedMerged: states = []githubql.PullRequestState{githubql.PullRequestStateClosed, githubql.PullRequestStateMerged} case changes.FilterAll: case change.FilterAll: states = nil // No states to filter the PRs by. default: // TODO: Map to 400 Bad Request HTTP error. return 0, fmt.Errorf("opt.State has unsupported value %q", opt.Filter) } @@ -154,15 +154,15 @@ func (s service) Count(ctx context.Context, rs string, opt changes.ListOptions) } err = s.clV4.Query(ctx, &q, variables) return q.Repository.PullRequests.TotalCount, err } func (s service) Get(ctx context.Context, rs string, id uint64) (changes.Change, error) { func (s service) Get(ctx context.Context, rs string, id uint64) (change.Change, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return changes.Change{}, err return change.Change{}, err } var q struct { Repository struct { PullRequest struct { Number uint64 @@ -181,11 +181,11 @@ func (s service) Get(ctx context.Context, rs string, id uint64) (changes.Change, "repositoryName": githubql.String(repo.Repo), "prNumber": githubql.Int(id), } err = s.clV4.Query(ctx, &q, variables) if err != nil { return changes.Change{}, err return change.Change{}, err } if s.currentUser.ID != 0 { // Mark as read. err = s.markRead(ctx, rs, id) @@ -194,43 +194,43 @@ func (s service) Get(ctx context.Context, rs string, id uint64) (changes.Change, } } // TODO: Eliminate comment body properties from issues.Issue. It's missing increasingly more fields, like Edited, etc. pr := q.Repository.PullRequest return changes.Change{ return change.Change{ ID: pr.Number, State: ghPRState(pr.State), Title: pr.Title, Author: ghActor(pr.Author), CreatedAt: pr.CreatedAt.Time, Commits: pr.Commits.TotalCount, }, nil } func (s service) ListCommits(ctx context.Context, rs string, id uint64) ([]changes.Commit, error) { func (s service) ListCommits(ctx context.Context, rs string, id uint64) ([]change.Commit, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return nil, err } cs, _, err := s.clV3.PullRequests.ListCommits(ctx, repo.Owner, repo.Repo, int(id), nil) if err != nil { return nil, err } var commits []changes.Commit var commits []change.Commit for _, c := range cs { commits = append(commits, changes.Commit{ commits = append(commits, change.Commit{ SHA: *c.SHA, Message: *c.Commit.Message, Author: ghV3User(c.Author), AuthorTime: *c.Commit.Author.Date, }) } return commits, nil } func (s service) GetDiff(ctx context.Context, rs string, id uint64, opt *changes.GetDiffOptions) ([]byte, error) { func (s service) GetDiff(ctx context.Context, rs string, id uint64, opt *change.GetDiffOptions) ([]byte, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return nil, err } @@ -248,11 +248,11 @@ func (s service) GetDiff(ctx context.Context, rs string, id uint64, opt *changes } return []byte(diff), nil } } func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) { func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *change.ListTimelineOptions) ([]interface{}, error) { repo, err := ghRepoSpec(rs) if err != nil { // TODO: Map to 400 Bad Request HTTP error. return nil, err } @@ -379,18 +379,18 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch pr := q.Repository.PullRequest reactions, err := s.reactions(pr.ReactionGroups) if err != nil { return timeline, err } var edited *changes.Edited var edited *change.Edited if pr.LastEditedAt != nil { edited = &changes.Edited{ edited = &change.Edited{ By: ghActor(*pr.Editor), At: pr.LastEditedAt.Time, } } timeline = append(timeline, changes.Comment{ timeline = append(timeline, change.Comment{ ID: prDescriptionCommentID, User: ghActor(pr.Author), CreatedAt: pr.PublishedAt.Time, Edited: edited, Body: string(pr.Body), @@ -405,38 +405,38 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch comment := node.IssueComment reactions, err := s.reactions(comment.ReactionGroups) if err != nil { return timeline, err } var edited *changes.Edited var edited *change.Edited if comment.LastEditedAt != nil { edited = &changes.Edited{ edited = &change.Edited{ By: ghActor(*comment.Editor), At: comment.LastEditedAt.Time, } } timeline = append(timeline, changes.Comment{ timeline = append(timeline, change.Comment{ ID: comment.DatabaseID, User: ghActor(comment.Author), CreatedAt: comment.PublishedAt.Time, Edited: edited, Body: comment.Body, Reactions: reactions, Editable: comment.ViewerCanUpdate, }) } for _, review := range q.Repository.PullRequest.Reviews.Nodes { var edited *changes.Edited var edited *change.Edited if review.LastEditedAt != nil { edited = &changes.Edited{ edited = &change.Edited{ By: ghActor(*review.Editor), At: review.LastEditedAt.Time, } } var cs []changes.InlineComment var cs []change.InlineComment for _, comment := range review.Comments.Nodes { cs = append(cs, changes.InlineComment{ cs = append(cs, change.InlineComment{ File: comment.Path, Line: comment.OriginalPosition, Body: comment.Body, }) } @@ -444,11 +444,11 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch 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{ timeline = append(timeline, change.Review{ ID: review.DatabaseID, User: ghActor(review.Author), CreatedAt: review.PublishedAt.Time, Edited: edited, State: ghPRReviewState(review.State), @@ -456,71 +456,71 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch Editable: review.ViewerCanUpdate, Comments: cs, }) } for _, event := range q.Repository.PullRequest.Timeline.Nodes { e := changes.TimelineItem{ //ID: 0, // TODO. e := change.TimelineItem{ //ID: 0, // TODO. } switch event.Typename { case "ClosedEvent": e.Actor = ghActor(event.ClosedEvent.Actor) e.CreatedAt = event.ClosedEvent.CreatedAt.Time e.Payload = changes.ClosedEvent{} e.Payload = change.ClosedEvent{} case "ReopenedEvent": e.Actor = ghActor(event.ReopenedEvent.Actor) e.CreatedAt = event.ReopenedEvent.CreatedAt.Time e.Payload = changes.ReopenedEvent{} e.Payload = change.ReopenedEvent{} case "RenamedTitleEvent": e.Actor = ghActor(event.RenamedTitleEvent.Actor) e.CreatedAt = event.RenamedTitleEvent.CreatedAt.Time e.Payload = changes.RenamedEvent{ e.Payload = change.RenamedEvent{ From: event.RenamedTitleEvent.PreviousTitle, To: event.RenamedTitleEvent.CurrentTitle, } case "LabeledEvent": e.Actor = ghActor(event.LabeledEvent.Actor) e.CreatedAt = event.LabeledEvent.CreatedAt.Time e.Payload = changes.LabeledEvent{ e.Payload = change.LabeledEvent{ Label: issues.Label{ Name: event.LabeledEvent.Label.Name, Color: ghColor(event.LabeledEvent.Label.Color), }, } case "UnlabeledEvent": e.Actor = ghActor(event.UnlabeledEvent.Actor) e.CreatedAt = event.UnlabeledEvent.CreatedAt.Time e.Payload = changes.UnlabeledEvent{ e.Payload = change.UnlabeledEvent{ Label: issues.Label{ Name: event.UnlabeledEvent.Label.Name, Color: ghColor(event.UnlabeledEvent.Label.Color), }, } case "ReviewRequestedEvent": e.Actor = ghActor(event.ReviewRequestedEvent.Actor) e.CreatedAt = event.ReviewRequestedEvent.CreatedAt.Time e.Payload = changes.ReviewRequestedEvent{ e.Payload = change.ReviewRequestedEvent{ RequestedReviewer: ghActor(event.ReviewRequestedEvent.RequestedReviewer.githubqlActor), } case "ReviewRequestRemovedEvent": e.Actor = ghActor(event.ReviewRequestRemovedEvent.Actor) e.CreatedAt = event.ReviewRequestRemovedEvent.CreatedAt.Time e.Payload = changes.ReviewRequestRemovedEvent{ e.Payload = change.ReviewRequestRemovedEvent{ RequestedReviewer: ghActor(event.ReviewRequestRemovedEvent.RequestedReviewer.githubqlActor), } case "MergedEvent": e.Actor = ghActor(event.MergedEvent.Actor) e.CreatedAt = event.MergedEvent.CreatedAt.Time e.Payload = changes.MergedEvent{ e.Payload = change.MergedEvent{ CommitID: event.MergedEvent.Commit.OID, CommitHTMLURL: event.MergedEvent.Commit.URL, RefName: event.MergedEvent.MergeRefName, } case "HeadRefDeletedEvent": e.Actor = ghActor(event.HeadRefDeletedEvent.Actor) e.CreatedAt = event.HeadRefDeletedEvent.CreatedAt.Time e.Payload = changes.DeletedEvent{ e.Payload = change.DeletedEvent{ Type: "branch", Name: event.HeadRefDeletedEvent.HeadRefName, } // TODO: Wait for GitHub to add support. //case "CommentDeletedEvent": @@ -638,33 +638,33 @@ var ghost = users.User{ Login: "ghost", AvatarURL: "https://avatars3.githubusercontent.com/u/10137?v=4", HTMLURL: "https://github.com/ghost", } // ghPRState converts a GitHub PullRequestState to changes.State. func ghPRState(state githubql.PullRequestState) changes.State { // ghPRState converts a GitHub PullRequestState to change.State. func ghPRState(state githubql.PullRequestState) change.State { switch state { case githubql.PullRequestStateOpen: return changes.OpenState return change.OpenState case githubql.PullRequestStateClosed: return changes.ClosedState return change.ClosedState case githubql.PullRequestStateMerged: return changes.MergedState return change.MergedState default: panic("unreachable") } } // ghPRReviewState converts a GitHub PullRequestReviewState to changes.ReviewState. func ghPRReviewState(state githubql.PullRequestReviewState) changes.ReviewState { // ghPRReviewState converts a GitHub PullRequestReviewState to change.ReviewState. func ghPRReviewState(state githubql.PullRequestReviewState) change.ReviewState { switch state { case githubql.PullRequestReviewStateApproved: return changes.Approved return change.Approved case githubql.PullRequestReviewStateCommented: return changes.Commented return change.Commented case githubql.PullRequestReviewStateChangesRequested: return changes.ChangesRequested return change.ChangesRequested case githubql.PullRequestReviewStatePending: panic("PullRequestReviewStatePending not implemented") // TODO. case githubql.PullRequestReviewStateDismissed: panic("PullRequestReviewStateDismissed not implemented") // TODO. default:
@@ -1,53 +1,53 @@ // Package maintner implements a read-only changes.Service using // Package maintner implements a read-only change.Service using // a x/build/maintner corpus that serves Gerrit changes. package maintner import ( "context" "fmt" "log" "sort" "strings" "dmitri.shuralyov.com/changes" "dmitri.shuralyov.com/service/change" "github.com/shurcooL/users" "golang.org/x/build/maintner" ) // NewService creates an changes.Service backed with the given corpus. // NewService creates an change.Service backed with the given corpus. // However, it serves Gerrit changes, not GitHub issues. func NewService(corpus *maintner.Corpus) changes.Service { func NewService(corpus *maintner.Corpus) change.Service { return service{ c: corpus, } } type service struct { c *maintner.Corpus } func (s service) List(ctx context.Context, repo string, opt changes.ListOptions) ([]changes.Change, error) { func (s service) List(ctx context.Context, repo string, opt change.ListOptions) ([]change.Change, error) { // TODO: Pagination. Respect opt.Start and opt.Length, if given. var is []changes.Change var is []change.Change project := s.c.Gerrit().Project(serverProject(repo)) err := project.ForeachCLUnsorted(func(cl *maintner.GerritCL) error { if cl.Status == "" { log.Printf("empty status for CL %d\n", cl.Number) return nil } state := state(cl.Status) switch { case opt.Filter == changes.FilterOpen && state != changes.OpenState: case opt.Filter == change.FilterOpen && state != change.OpenState: return nil case opt.Filter == changes.FilterClosedMerged && !(state == changes.ClosedState || state == changes.MergedState): case opt.Filter == change.FilterClosedMerged && !(state == change.ClosedState || state == change.MergedState): return nil } is = append(is, changes.Change{ is = append(is, change.Change{ ID: uint64(cl.Number), State: state, Title: firstParagraph(cl.Commit.Msg), //Labels: labels, // TODO. Author: gerritUser(cl.Commit.Author), @@ -67,23 +67,23 @@ func (s service) List(ctx context.Context, repo string, opt changes.ListOptions) }) return is, nil } func (s service) Count(_ context.Context, repo string, opt changes.ListOptions) (uint64, error) { func (s service) Count(_ context.Context, repo string, opt change.ListOptions) (uint64, error) { var count uint64 project := s.c.Gerrit().Project(serverProject(repo)) err := project.ForeachCLUnsorted(func(cl *maintner.GerritCL) error { if cl.Status == "" { return nil } state := state(cl.Status) switch { case opt.Filter == changes.FilterOpen && state != changes.OpenState: case opt.Filter == change.FilterOpen && state != change.OpenState: return nil case opt.Filter == changes.FilterClosedMerged && !(state == changes.ClosedState || state == changes.MergedState): case opt.Filter == change.FilterClosedMerged && !(state == change.ClosedState || state == change.MergedState): return nil } count++ @@ -94,40 +94,40 @@ func (s service) Count(_ context.Context, repo string, opt changes.ListOptions) } return count, nil } func (s service) Get(ctx context.Context, _ string, id uint64) (changes.Change, error) { func (s service) Get(ctx context.Context, _ string, id uint64) (change.Change, error) { // TODO. return changes.Change{}, fmt.Errorf("Get: not implemented") return change.Change{}, fmt.Errorf("Get: not implemented") } func (s service) ListCommits(ctx context.Context, _ string, id uint64) ([]changes.Commit, error) { func (s service) ListCommits(ctx context.Context, _ string, id uint64) ([]change.Commit, error) { return nil, fmt.Errorf("ListCommits: not implemented") } func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *changes.GetDiffOptions) ([]byte, error) { func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *change.GetDiffOptions) ([]byte, error) { // TODO. return nil, fmt.Errorf("GetDiff: not implemented") } func state(status string) changes.State { func state(status string) change.State { switch status { case "new": return changes.OpenState return change.OpenState case "abandoned": return changes.ClosedState return change.ClosedState case "merged": return changes.MergedState return change.MergedState case "draft": panic("not sure how to deal with draft status") default: panic(fmt.Errorf("unrecognized status %q", status)) } } func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) { func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *change.ListTimelineOptions) ([]interface{}, error) { // TODO. return nil, fmt.Errorf("ListTimeline: not implemented") } func gerritUser(user *maintner.GitPerson) users.User {
@@ -1,6 +1,6 @@ package changes package change import ( "time" "github.com/shurcooL/issues"