aboutsummaryrefslogtreecommitdiffstats
path: root/bridge/github
diff options
context:
space:
mode:
authorAlexander Scharinger <rng.dynamics@gmail.com>2021-01-24 21:27:14 +0100
committerAlexander Scharinger <rng.dynamics@gmail.com>2021-01-24 21:37:44 +0100
commit2d6f34acadc9df4461127b994b0b01b98ff52d43 (patch)
tree2596d224b30c43c8a24235183460a3e4efc4826b /bridge/github
parent2c0cf105278389f2c9cd3ce875b64ae36731a1ab (diff)
downloadgit-bug-2d6f34acadc9df4461127b994b0b01b98ff52d43.tar.gz
Integrate new Github Bridge import
Diffstat (limited to 'bridge/github')
-rw-r--r--bridge/github/import.go2
-rw-r--r--bridge/github/import_query.go97
-rw-r--r--bridge/github/iterator.go959
3 files changed, 246 insertions, 812 deletions
diff --git a/bridge/github/import.go b/bridge/github/import.go
index 78e93436..e8a4d3cb 100644
--- a/bridge/github/import.go
+++ b/bridge/github/import.go
@@ -100,7 +100,7 @@ func (gi *githubImporter) ImportAll(ctx context.Context, repo *cache.RepoCache,
return out, nil
}
-func (gi *githubImporter) ensureIssue(repo *cache.RepoCache, issue issueTimeline) (*cache.BugCache, error) {
+func (gi *githubImporter) ensureIssue(repo *cache.RepoCache, issue issue) (*cache.BugCache, error) {
// ensure issue author
author, err := gi.ensurePerson(repo, issue.Author)
if err != nil {
diff --git a/bridge/github/import_query.go b/bridge/github/import_query.go
index ef26b3e8..52aef9e9 100644
--- a/bridge/github/import_query.go
+++ b/bridge/github/import_query.go
@@ -47,14 +47,9 @@ type userContentEdit struct {
}
type issueComment struct {
- authorEvent
- Body githubv4.String
- Url githubv4.URI
-
- UserContentEdits struct {
- Nodes []userContentEdit
- PageInfo pageInfo
- } `graphql:"userContentEdits(last: $commentEditLast, before: $commentEditBefore)"`
+ authorEvent // NOTE: contains Id
+ Body githubv4.String
+ Url githubv4.URI
}
type timelineItem struct {
@@ -96,86 +91,6 @@ type timelineItem struct {
} `graphql:"... on RenamedTitleEvent"`
}
-type issueTimeline struct {
- authorEvent
- Title string
- Number githubv4.Int
- Body githubv4.String
- Url githubv4.URI
-
- TimelineItems struct {
- Edges []struct {
- Cursor githubv4.String
- Node timelineItem
- }
- PageInfo pageInfo
- } `graphql:"timelineItems(first: $timelineFirst, after: $timelineAfter)"`
-
- UserContentEdits struct {
- Nodes []userContentEdit
- PageInfo pageInfo
- } `graphql:"userContentEdits(last: $issueEditLast, before: $issueEditBefore)"`
-}
-
-// Alex
-type timelineItemsQuery struct {
- Repository struct {
- Issue struct {
- TimelineItems struct {
- Edges []struct {
- Cursor githubv4.String
- Node timelineItem
- }
- PageInfo pageInfo
- } `graphql:"timelineItems(first: $timelineFirst, after: $timelineAfter)"`
- } `graphql:"issue(number: $issueNumber)"`
- } `graphql:"repository(owner: $owner, name: $name)"`
-}
-
-type issueEdit struct {
- UserContentEdits struct {
- Nodes []userContentEdit
- PageInfo pageInfo
- } `graphql:"userContentEdits(last: $issueEditLast, before: $issueEditBefore)"`
-}
-
-type issueTimelineQuery struct {
- Repository struct {
- Issues struct {
- Nodes []issueTimeline
- PageInfo pageInfo
- } `graphql:"issues(first: $issueFirst, after: $issueAfter, orderBy: {field: CREATED_AT, direction: ASC}, filterBy: {since: $issueSince})"`
- } `graphql:"repository(owner: $owner, name: $name)"`
-}
-
-type issueEditQuery struct {
- Repository struct {
- Issues struct {
- Nodes []issueEdit
- PageInfo pageInfo
- } `graphql:"issues(first: $issueFirst, after: $issueAfter, orderBy: {field: CREATED_AT, direction: ASC}, filterBy: {since: $issueSince})"`
- } `graphql:"repository(owner: $owner, name: $name)"`
-}
-
-type commentEditQuery struct {
- Repository struct {
- Issues struct {
- Nodes []struct {
- Timeline struct {
- Nodes []struct {
- IssueComment struct {
- UserContentEdits struct {
- Nodes []userContentEdit
- PageInfo pageInfo
- } `graphql:"userContentEdits(last: $commentEditLast, before: $commentEditBefore)"`
- } `graphql:"... on IssueComment"`
- }
- } `graphql:"timeline(first: $timelineFirst, after: $timelineAfter)"`
- }
- } `graphql:"issues(first: $issueFirst, after: $issueAfter, orderBy: {field: CREATED_AT, direction: ASC}, filterBy: {since: $issueSince})"`
- } `graphql:"repository(owner: $owner, name: $name)"`
-}
-
type ghostQuery struct {
User struct {
Login githubv4.String
@@ -209,7 +124,7 @@ type issueQuery struct {
Issues struct {
Nodes []issue
PageInfo pageInfo
- } `graphql:"issues(first: $issueFirst, after: $issueAfter, orderBy: {field: CREATED_AT, direction: ASC})"` //, filterBy: {since: $issueSince})"`
+ } `graphql:"issues(first: $issueFirst, after: $issueAfter, orderBy: {field: CREATED_AT, direction: ASC}), filterBy: {since: $issueSince})"`
} `graphql:"repository(owner: $owner, name: $name)"`
}
@@ -221,7 +136,7 @@ type issue struct {
Url githubv4.URI
}
-type issueEditQuery_A struct {
+type issueEditQuery struct {
Node struct {
Typename githubv4.String `graphql:"__typename"`
Issue struct {
@@ -246,7 +161,7 @@ type timelineQuery struct {
} `graphql:"node(id: $gqlNodeId)"`
}
-type commentEditQuery_A struct {
+type commentEditQuery struct {
Node struct {
Typename githubv4.String `graphql:"__typename"`
IssueComment struct {
diff --git a/bridge/github/iterator.go b/bridge/github/iterator.go
index 76c5cbfa..ab473a52 100644
--- a/bridge/github/iterator.go
+++ b/bridge/github/iterator.go
@@ -2,790 +2,342 @@ package github
import (
"context"
- "fmt"
"time"
-
- "github.com/pkg/errors"
+ "github.com/pkg/errors"
"github.com/shurcooL/githubv4"
)
-type iterator_A struct {
- gc *githubv4.Client
- since time.Time
- ctx context.Context
- err error
- issueIter issueIter
+type iterator struct {
+ gc *githubv4.Client
+ since time.Time
+ ctx context.Context
+ err error
+ issueIter issueIter
}
type issueIter struct {
- iterVars
- query issueQuery
- issueEditIter []issueEditIter
- timelineIter []timelineIter
+ iterVars
+ query issueQuery
+ issueEditIter []issueEditIter
+ timelineIter []timelineIter
}
type issueEditIter struct {
- iterVars
- query issueEditQuery_A
+ iterVars
+ query issueEditQuery
}
type timelineIter struct {
- iterVars
- query timelineQuery
- commentEditIter []commentEditIter
+ iterVars
+ query timelineQuery
+ commentEditIter []commentEditIter
}
-
type commentEditIter struct {
- iterVars
- query commentEditQuery_A
+ iterVars
+ query commentEditQuery
}
type iterVars struct {
- index int
- capacity int
- variables varmap
+ index int
+ capacity int
+ variables varmap
}
type varmap map[string]interface{}
+func newIterVars(capacity int) iterVars {
+ return iterVars{
+ index: -1,
+ capacity: capacity,
+ variables: varmap{},
+ }
+}
-func NewIterator_A(ctx context.Context, client *githubv4.Client, capacity int, owner, project string, since time.Time) *iterator_A {
- i := &iterator_A{
- gc: client,
- since: since,
- ctx: ctx,
- issueIter: issueIter{
- iterVars: newIterVars(capacity),
- },
- }
+func NewIterator(ctx context.Context, client *githubv4.Client, capacity int, owner, project string, since time.Time) *iterator {
+ i := &iterator{
+ gc: client,
+ since: since,
+ ctx: ctx,
+ issueIter: issueIter{
+ iterVars: newIterVars(capacity),
+ timelineIter: make([]timelineIter, capacity),
+ issueEditIter: make([]issueEditIter, capacity),
+ },
+ }
i.issueIter.variables.setOwnerProject(owner, project)
- for idx := range i.issueIter.issueEditIter {
- ie := &i.issueIter.issueEditIter[idx]
- ie.iterVars = newIterVars(capacity)
- }
- for i1 := range i.issueIter.timelineIter {
- tli := &i.issueIter.timelineIter[i1]
- tli.iterVars = newIterVars(capacity)
+ for idx := range i.issueIter.issueEditIter {
+ ie := &i.issueIter.issueEditIter[idx]
+ ie.iterVars = newIterVars(capacity)
+ }
+ for i1 := range i.issueIter.timelineIter {
+ tli := &i.issueIter.timelineIter[i1]
+ tli.iterVars = newIterVars(capacity)
+ tli.commentEditIter = make([]commentEditIter, capacity)
+ for i2 := range tli.commentEditIter {
+ cei := &tli.commentEditIter[i2]
+ cei.iterVars = newIterVars(capacity)
+ }
}
i.resetIssueVars()
return i
}
-func newIterVars(capacity int) iterVars {
- return iterVars{
- index: -1,
- capacity: capacity,
- variables: varmap{},
- }
-}
-
func (v *varmap) setOwnerProject(owner, project string) {
- (*v)["owner"] = githubv4.String(owner)
- (*v)["name"] = githubv4.String(project)
-}
-
-func (i *iterator_A) resetIssueVars() {
- vars := &i.issueIter.variables
- (*vars)["issueFirst"] = githubv4.Int(i.issueIter.capacity)
- (*vars)["issueAfter"] = (*githubv4.String)(nil)
- // I am not sure if the since variable should be used.
- //(*vars)["issueSince"] = githubv4.DateTime{Time: i.since}
- i.issueIter.query.Repository.Issues.PageInfo.HasNextPage = true
- i.issueIter.query.Repository.Issues.PageInfo.EndCursor = ""
+ (*v)["owner"] = githubv4.String(owner)
+ (*v)["name"] = githubv4.String(project)
}
-func (i *iterator_A) resetIssueEditVars() {
- for idx := range i.issueIter.issueEditIter {
- ie := &i.issueIter.issueEditIter[idx]
- ie.variables["issueEditLast"] = githubv4.Int(ie.capacity)
- ie.variables["issueEditBefore"] = (*githubv4.String)(nil)
- ie.query.Node.Issue.UserContentEdits.PageInfo.HasNextPage = true
- ie.query.Node.Issue.UserContentEdits.PageInfo.EndCursor = ""
- }
+func (i *iterator) resetIssueVars() {
+ vars := &i.issueIter.variables
+ (*vars)["issueFirst"] = githubv4.Int(i.issueIter.capacity)
+ (*vars)["issueAfter"] = (*githubv4.String)(nil)
+ // Only query issues after the given date. This varaible is used in the GraphQL query.
+ (*vars)["issueSince"] = githubv4.DateTime{Time: i.since}
+ i.issueIter.query.Repository.Issues.PageInfo.HasNextPage = true
+ i.issueIter.query.Repository.Issues.PageInfo.EndCursor = ""
}
-func (i *iterator_A) resetTimelineVars() {
- for idx := range i.issueIter.timelineIter {
- ip := &i.issueIter.timelineIter[idx]
- ip.variables["timelineFirst"] = githubv4.Int(ip.capacity)
- ip.variables["timelineAfter"] = (*githubv4.String)(nil)
- ip.query.Node.Issue.TimelineItems.PageInfo.HasNextPage = true
- ip.query.Node.Issue.TimelineItems.PageInfo.EndCursor = ""
- }
+func (i *iterator) resetIssueEditVars() {
+ for idx := range i.issueIter.issueEditIter {
+ ie := &i.issueIter.issueEditIter[idx]
+ ie.variables["issueEditLast"] = githubv4.Int(ie.capacity)
+ ie.variables["issueEditBefore"] = (*githubv4.String)(nil)
+ ie.query.Node.Issue.UserContentEdits.PageInfo.HasNextPage = true
+ ie.query.Node.Issue.UserContentEdits.PageInfo.EndCursor = ""
+ }
}
-func (i *iterator_A) currIssueItem() *issue {
- return &i.issueIter.query.Repository.Issues.Nodes[i.issueIter.index]
+func (i *iterator) resetTimelineVars() {
+ for idx := range i.issueIter.timelineIter {
+ ip := &i.issueIter.timelineIter[idx]
+ ip.variables["timelineFirst"] = githubv4.Int(ip.capacity)
+ ip.variables["timelineAfter"] = (*githubv4.String)(nil)
+ ip.query.Node.Issue.TimelineItems.PageInfo.HasNextPage = true
+ ip.query.Node.Issue.TimelineItems.PageInfo.EndCursor = ""
+ }
}
-func (i *iterator_A) currIssueEditIter() *issueEditIter {
- return &i.issueIter.issueEditIter[i.issueIter.index]
+func (i *iterator) resetCommentEditVars() {
+ for i1 := range i.issueIter.timelineIter {
+ for i2 := range i.issueIter.timelineIter[i1].commentEditIter {
+ ce := &i.issueIter.timelineIter[i1].commentEditIter[i2]
+ ce.variables["commentEditLast"] = githubv4.Int(ce.capacity)
+ ce.variables["commentEditBefore"] = (*githubv4.String)(nil)
+ ce.query.Node.IssueComment.UserContentEdits.PageInfo.HasNextPage = true
+ ce.query.Node.IssueComment.UserContentEdits.PageInfo.EndCursor = ""
+ }
+ }
}
-func (i *iterator_A) currTimelineIter() *timelineIter {
- return &i.issueIter.timelineIter[i.issueIter.index]
+// Error return last encountered error
+func (i *iterator) Error() error {
+ if i.err != nil {
+ return i.err
+ }
+ return i.ctx.Err() // might return nil
}
-func (i *iterator_A) currIssueGqlNodeId() githubv4.ID {
- return i.currIssueItem().Id
+func (i *iterator) HasError() bool {
+ return i.err != nil || i.ctx.Err() != nil
}
-func (i *iterator_A) currCommentEditIter() *commentEditIter {
- timelineIter := i.currTimelineIter()
- return &timelineIter.commentEditIter[timelineIter.index]
+func (i *iterator) currIssueItem() *issue {
+ return &i.issueIter.query.Repository.Issues.Nodes[i.issueIter.index]
}
-// Error return last encountered error
-func (i *iterator_A) Error() error {
- if i.err != nil {
- return i.err
- }
- return i.ctx.Err() // might return nil
-}
-
-func (i *iterator_A) HasError() bool {
- return i.err != nil || i.ctx.Err() != nil
-}
-
-func (i *iterator_A) NextIssue() bool {
- if i.HasError() {
- return false
- }
- index := &i.issueIter.index
- issues := &i.issueIter.query.Repository.Issues
- issueItems := &issues.Nodes
- if 0 <= *index && *index < len(*issueItems)-1 {
- *index += 1
- return true
- }
-
- if !issues.PageInfo.HasNextPage {
- return false
- }
- nextIssue := i.queryIssue()
- return nextIssue
-}
-
-func (i *iterator_A) IssueValue() issue {
- return *i.currIssueItem()
-}
-
-func (i *iterator_A) queryIssue() bool {
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
- if endCursor := i.issueIter.query.Repository.Issues.PageInfo.EndCursor; endCursor != "" {
- i.issueIter.variables["issueAfter"] = endCursor
- }
- if err := i.gc.Query(ctx, &i.issueIter.query, i.issueIter.variables); err != nil {
- i.err = err
- return false
- }
- i.resetIssueEditVars()
- i.resetTimelineVars()
- issueItems := &i.issueIter.query.Repository.Issues.Nodes
- if len(*issueItems) <= 0 {
- i.issueIter.index = -1
- return false
- }
- i.issueIter.index = 0
- return true
-}
-
-func (i *iterator_A) NextIssueEdit() bool {
- if i.HasError() {
- return false
- }
- ieIter := i.currIssueEditIter()
- ieIdx := &ieIter.index
- ieItems := ieIter.query.Node.Issue.UserContentEdits
- if 0 <= *ieIdx && *ieIdx < len(ieItems.Nodes)-1 {
- *ieIdx += 1
- return i.nextValidIssueEdit()
- }
- if !ieItems.PageInfo.HasNextPage {
- return false
- }
- querySucc := i.queryIssueEdit()
- if !querySucc {
- return false
- }
- return i.nextValidIssueEdit()
-}
-
-func (i *iterator_A) nextValidIssueEdit() bool {
- // issueEdit.Diff == nil happen if the event is older than early 2018, Github doesn't have the data before that.
- // Best we can do is to ignore the event.
- if issueEdit := i.IssueEditValue(); issueEdit.Diff == nil || string(*issueEdit.Diff) == "" {
- return i.NextIssueEdit()
- }
- return true
-}
-
-func (i *iterator_A) IssueEditValue() userContentEdit {
- iei := i.currIssueEditIter()
- return iei.query.Node.Issue.UserContentEdits.Nodes[iei.index]
-}
-
-func (i *iterator_A) queryIssueEdit() bool {
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
- iei := i.currIssueEditIter()
- if endCursor := iei.query.Node.Issue.UserContentEdits.PageInfo.EndCursor; endCursor != "" {
- iei.variables["issueEditBefore"] = endCursor
- }
- iei.variables["gqlNodeId"] = i.currIssueGqlNodeId()
- if err := i.gc.Query(ctx, &iei.query, iei.variables); err != nil {
- i.err = err
- return false
- }
- issueEditItems := iei.query.Node.Issue.UserContentEdits.Nodes
- if len(issueEditItems) <= 0 {
- iei.index = -1
- return false
- }
- // The UserContentEditConnection in the Github API serves its elements in reverse chronological
- // order. For our purpose we have to reverse the edits.
- reverseEdits(issueEditItems)
- iei.index = 0
- return true
-}
-
-func (i *iterator_A) NextTimelineItem() bool {
- if i.HasError() {
- return false
- }
- tlIter := &i.issueIter.timelineIter[i.issueIter.index]
- tlIdx := &tlIter.index
- tlItems := tlIter.query.Node.Issue.TimelineItems
- if 0 <= *tlIdx && *tlIdx < len(tlItems.Nodes)-1 {
- *tlIdx += 1
- return true
- }
- if !tlItems.PageInfo.HasNextPage {
- return false
- }
- nextTlItem := i.queryTimeline()
- return nextTlItem
-}
-
-func (i *iterator_A) TimelineItemValue() timelineItem {
- tli := i.currTimelineIter()
- return tli.query.Node.Issue.TimelineItems.Nodes[tli.index]
-}
-
-func (i *iterator_A) queryTimeline() bool {
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
- tli := i.currTimelineIter()
- if endCursor := tli.query.Node.Issue.TimelineItems.PageInfo.EndCursor; endCursor != "" {
- tli.variables["timelineAfter"] = endCursor
- }
- tli.variables["gqlNodeId"] = i.currIssueGqlNodeId()
- if err := i.gc.Query(ctx, &tli.query, tli.variables); err != nil {
- i.err = err
- return false
- }
- //i.resetCommentEditVars()
- timelineItems := &tli.query.Node.Issue.TimelineItems
- if len(timelineItems.Nodes) <= 0 {
- tli.index = -1
- return false
- }
- tli.index = 0
- return true
-}
-
-func (i *iterator_A) NextCommentEdit() bool {
- if i.HasError() {
- return false
- }
-
- tmlnVal := i.TimelineItemValue()
- if tmlnVal.Typename != "IssueComment" {
- // The timeline iterator does not point to a comment.
- i.err = errors.New("Call to NextCommentEdit() while timeline item is not a comment")
- return false
- }
-
- cei := i.currCommentEditIter()
- ceIdx := &cei.index
- ceItems := &cei.query.Node.IssueComment.UserContentEdits
- if 0 <= *ceIdx && *ceIdx < len(ceItems.Nodes)-1 {
- *ceIdx += 1
- return i.nextValidCommentEdit()
- }
- if !ceItems.PageInfo.HasNextPage {
- return false
- }
- querySucc := i.queryCommentEdit()
- if !querySucc {
- return false
- }
- return i.nextValidCommentEdit()
-}
-
-func (i *iterator_A) nextValidCommentEdit() bool {
- // if comment edit diff is a nil pointer or points to an empty string look for next value
- if commentEdit := i.CommentEditValue(); commentEdit.Diff == nil || string(*commentEdit.Diff) == "" {
- return i.NextCommentEdit()
- }
- return true
-}
-
-func (i *iterator_A) CommentEditValue() userContentEdit {
- cei := i.currCommentEditIter()
- return cei.query.Node.IssueComment.UserContentEdits.Nodes[cei.index]
-}
-
-
-func (i *iterator_A) queryCommentEdit() bool {
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
- cei := i.currCommentEditIter()
-
- if endCursor := cei.query.Node.IssueComment.UserContentEdits.PageInfo.EndCursor; endCursor != "" {
- cei.variables["commentEditBefore"] = endCursor
- }
- tmlnVal := i.TimelineItemValue()
- if tmlnVal.Typename != "IssueComment" {
- i.err = errors.New("Call to queryCommentEdit() while timeline item is not a comment")
- return false
- }
- cei.variables["gqlNodeId"] = tmlnVal.IssueComment.Id
- if err := i.gc.Query(ctx, &cei.query, cei.variables); err != nil {
- i.err = err
- return false
- }
- ceItems := cei.query.Node.IssueComment.UserContentEdits.Nodes
- if len(ceItems) <= 0 {
- cei.index = -1
- return false
- }
- // The UserContentEditConnection in the Github API serves its elements in reverse chronological
- // order. For our purpose we have to reverse the edits.
- reverseEdits(ceItems)
- cei.index = 0
- return true
-}
-
-
-type indexer struct{ index int }
-
-type issueEditIterator struct {
- index int
- query issueEditQuery
- variables map[string]interface{}
+func (i *iterator) currIssueEditIter() *issueEditIter {
+ return &i.issueIter.issueEditIter[i.issueIter.index]
}
-type commentEditIterator struct {
- index int
- query commentEditQuery
- variables map[string]interface{}
+func (i *iterator) currTimelineIter() *timelineIter {
+ return &i.issueIter.timelineIter[i.issueIter.index]
}
-type timelineIterator struct {
- index int
- query issueTimelineQuery
- variables map[string]interface{}
-
- issueEdit indexer
- commentEdit indexer
-
- // Alex: It would be really help clearity to get rid of this variable.
- // lastEndCursor cache the timeline end cursor for one iteration
- lastEndCursor githubv4.String
+func (i *iterator) currCommentEditIter() *commentEditIter {
+ timelineIter := i.currTimelineIter()
+ return &timelineIter.commentEditIter[timelineIter.index]
}
-type iterator struct {
- // github graphql client
- gc *githubv4.Client
-
- // if since is given the iterator will query only the updated
- // and created issues after this date
- since time.Time
-
- // number of timelines/userEditcontent/issueEdit to query
- // at a time, more capacity = more used memory = less queries
- // to make
- capacity int
-
- // shared context used for all graphql queries
- ctx context.Context
-
- // sticky error
- err error
-
- // timeline iterator
- timeline timelineIterator
-
- // issue edit iterator
- issueEdit issueEditIterator
-
- // comment edit iterator
- commentEdit commentEditIterator
+func (i *iterator) currIssueGqlNodeId() githubv4.ID {
+ return i.currIssueItem().Id
}
-// NewIterator create and initialize a new iterator
-func NewIterator(ctx context.Context, client *githubv4.Client, capacity int, owner, project string, since time.Time) *iterator {
- i := &iterator{
- gc: client,
- since: since,
- capacity: capacity,
- ctx: ctx,
- timeline: timelineIterator{
- index: -1,
- issueEdit: indexer{-1},
- commentEdit: indexer{-1},
- variables: map[string]interface{}{
- "owner": githubv4.String(owner),
- "name": githubv4.String(project),
- },
- },
- commentEdit: commentEditIterator{
- index: -1,
- variables: map[string]interface{}{
- "owner": githubv4.String(owner),
- "name": githubv4.String(project),
- },
- },
- issueEdit: issueEditIterator{
- index: -1,
- variables: map[string]interface{}{
- "owner": githubv4.String(owner),
- "name": githubv4.String(project),
- },
- },
+func (i *iterator) NextIssue() bool {
+ if i.HasError() {
+ return false
+ }
+ index := &i.issueIter.index
+ issues := &i.issueIter.query.Repository.Issues
+ issueItems := &issues.Nodes
+ if 0 <= *index && *index < len(*issueItems)-1 {
+ *index += 1
+ return true
}
- i.initTimelineQueryVariables()
- return i
-}
-
-// init issue timeline variables
-func (i *iterator) initTimelineQueryVariables() {
- i.timeline.variables["issueFirst"] = githubv4.Int(1) // each query one single issue only
- i.timeline.variables["issueAfter"] = (*githubv4.String)(nil)
- i.timeline.variables["issueSince"] = githubv4.DateTime{Time: i.since}
- i.timeline.variables["timelineFirst"] = githubv4.Int(i.capacity)
- i.timeline.variables["timelineAfter"] = (*githubv4.String)(nil)
- // Fun fact, github provide the comment edition in reverse chronological
- // order, because haha. Look at me, I'm dying of laughter.
- i.timeline.variables["issueEditLast"] = githubv4.Int(i.capacity)
- i.timeline.variables["issueEditBefore"] = (*githubv4.String)(nil)
- i.timeline.variables["commentEditLast"] = githubv4.Int(i.capacity)
- i.timeline.variables["commentEditBefore"] = (*githubv4.String)(nil)
-}
-
-// init issue edit variables
-func (i *iterator) initIssueEditQueryVariables() {
- i.issueEdit.variables["issueFirst"] = githubv4.Int(1)
- i.issueEdit.variables["issueAfter"] = i.timeline.variables["issueAfter"]
- i.issueEdit.variables["issueSince"] = githubv4.DateTime{Time: i.since}
- i.issueEdit.variables["issueEditLast"] = githubv4.Int(i.capacity)
- i.issueEdit.variables["issueEditBefore"] = (*githubv4.String)(nil)
-}
-
-// init issue comment variables
-func (i *iterator) initCommentEditQueryVariables() {
- i.commentEdit.variables["issueFirst"] = githubv4.Int(1)
- i.commentEdit.variables["issueAfter"] = i.timeline.variables["issueAfter"]
- i.commentEdit.variables["issueSince"] = githubv4.DateTime{Time: i.since}
- i.commentEdit.variables["timelineFirst"] = githubv4.Int(1)
- i.commentEdit.variables["timelineAfter"] = (*githubv4.String)(nil)
- i.commentEdit.variables["commentEditLast"] = githubv4.Int(i.capacity)
- i.commentEdit.variables["commentEditBefore"] = (*githubv4.String)(nil)
-}
-
-// reverse UserContentEdits arrays in both of the issue and
-// comment timelines
-func (i *iterator) reverseTimelineEditNodes() {
- node := i.timeline.query.Repository.Issues.Nodes[0]
- reverseEdits(node.UserContentEdits.Nodes)
- for index, ce := range node.TimelineItems.Edges {
- if ce.Node.Typename == "IssueComment" && len(node.TimelineItems.Edges) != 0 {
- reverseEdits(node.TimelineItems.Edges[index].Node.IssueComment.UserContentEdits.Nodes)
- }
+ if !issues.PageInfo.HasNextPage {
+ return false
}
+ nextIssue := i.queryIssue()
+ return nextIssue
}
-// Error return last encountered error
-func (i *iterator) Error() error {
- return i.err
+func (i *iterator) IssueValue() issue {
+ return *i.currIssueItem()
}
func (i *iterator) queryIssue() bool {
ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
defer cancel()
-
- if err := i.gc.Query(ctx, &i.timeline.query, i.timeline.variables); err != nil {
+ if endCursor := i.issueIter.query.Repository.Issues.PageInfo.EndCursor; endCursor != "" {
+ i.issueIter.variables["issueAfter"] = endCursor
+ }
+ if err := i.gc.Query(ctx, &i.issueIter.query, i.issueIter.variables); err != nil {
i.err = err
return false
}
-
- issues := i.timeline.query.Repository.Issues.Nodes
- if len(issues) == 0 {
+ i.resetIssueEditVars()
+ i.resetTimelineVars()
+ issueItems := &i.issueIter.query.Repository.Issues.Nodes
+ if len(*issueItems) <= 0 {
+ i.issueIter.index = -1
return false
}
-
- i.reverseTimelineEditNodes()
+ i.issueIter.index = 0
return true
}
-// NextIssue try to query the next issue and return true. Only one issue is
-// queried at each call.
-func (i *iterator) NextIssue() bool {
- if i.err != nil {
+func (i *iterator) NextIssueEdit() bool {
+ if i.HasError() {
return false
}
-
- if i.ctx.Err() != nil {
- return false
+ ieIter := i.currIssueEditIter()
+ ieIdx := &ieIter.index
+ ieItems := ieIter.query.Node.Issue.UserContentEdits
+ if 0 <= *ieIdx && *ieIdx < len(ieItems.Nodes)-1 {
+ *ieIdx += 1
+ return i.nextValidIssueEdit()
}
-
- // if $issueAfter variable is nil we can directly make the first query
- if i.timeline.variables["issueAfter"] == (*githubv4.String)(nil) {
- nextIssue := i.queryIssue()
- // prevent from infinite loop by setting a non nil cursor
- issues := i.timeline.query.Repository.Issues
- i.timeline.variables["issueAfter"] = issues.PageInfo.EndCursor
- return nextIssue
+ if !ieItems.PageInfo.HasNextPage {
+ return false
}
-
- issues := i.timeline.query.Repository.Issues
- if !issues.PageInfo.HasNextPage {
+ querySucc := i.queryIssueEdit()
+ if !querySucc {
return false
}
+ return i.nextValidIssueEdit()
+}
- // if we have more issues, query them
- i.timeline.variables["timelineAfter"] = (*githubv4.String)(nil)
- i.timeline.index = -1
-
- timelineEndCursor := issues.Nodes[0].TimelineItems.PageInfo.EndCursor
- // store cursor for future use
- i.timeline.lastEndCursor = timelineEndCursor
-
- // query issue block
- nextIssue := i.queryIssue()
- i.timeline.variables["issueAfter"] = issues.PageInfo.EndCursor
-
- return nextIssue
+func (i *iterator) nextValidIssueEdit() bool {
+ // issueEdit.Diff == nil happen if the event is older than early 2018, Github doesn't have the data before that.
+ // Best we can do is to ignore the event.
+ if issueEdit := i.IssueEditValue(); issueEdit.Diff == nil || string(*issueEdit.Diff) == "" {
+ return i.NextIssueEdit()
+ }
+ return true
}
-// IssueValue return the actual issue value
-func (i *iterator) IssueValue() issueTimeline {
- issues := i.timeline.query.Repository.Issues
- return issues.Nodes[0]
+func (i *iterator) IssueEditValue() userContentEdit {
+ iei := i.currIssueEditIter()
+ return iei.query.Node.Issue.UserContentEdits.Nodes[iei.index]
}
-// NextTimelineItem return true if there is a next timeline item and increments the index by one.
-// It is used iterates over all the timeline items. Extra queries are made if it is necessary.
-func (i *iterator) NextTimelineItem() bool {
- if i.err != nil {
- return false
+func (i *iterator) queryIssueEdit() bool {
+ ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
+ defer cancel()
+ iei := i.currIssueEditIter()
+ if endCursor := iei.query.Node.Issue.UserContentEdits.PageInfo.EndCursor; endCursor != "" {
+ iei.variables["issueEditBefore"] = endCursor
}
-
- if i.ctx.Err() != nil {
+ iei.variables["gqlNodeId"] = i.currIssueGqlNodeId()
+ if err := i.gc.Query(ctx, &iei.query, iei.variables); err != nil {
+ i.err = err
return false
}
-
- timelineItems := i.timeline.query.Repository.Issues.Nodes[0].TimelineItems
- // after NextIssue call it's good to check wether we have some timelineItems items or not
- // Alex: Correct?
- if len(timelineItems.Edges) == 0 {
+ issueEditItems := iei.query.Node.Issue.UserContentEdits.Nodes
+ if len(issueEditItems) <= 0 {
+ iei.index = -1
return false
}
+ // The UserContentEditConnection in the Github API serves its elements in reverse chronological
+ // order. For our purpose we have to reverse the edits.
+ reverseEdits(issueEditItems)
+ iei.index = 0
+ return true
+}
- if i.timeline.index < len(timelineItems.Edges)-1 {
- i.timeline.index++
- return true
- }
-
- if !timelineItems.PageInfo.HasNextPage {
+func (i *iterator) NextTimelineItem() bool {
+ if i.HasError() {
return false
}
-
- i.timeline.lastEndCursor = timelineItems.PageInfo.EndCursor
-
- // more timelines, query them
- i.timeline.variables["timelineAfter"] = timelineItems.PageInfo.EndCursor
- // HACK
- var query timelineItemsQuery
- // var variables map[string]interface{}
- variables := make(map[string]interface{})
- variables["owner"] = i.timeline.variables["owner"]
- variables["name"] = i.timeline.variables["name"]
- variables["issueNumber"] = i.timeline.query.Repository.Issues.Nodes[0].Number
- fmt.Println("### Alex using issue number ", i.timeline.query.Repository.Issues.Nodes[0].Number)
- variables["timelineFirst"] = i.timeline.variables["timelineFirst"]
- variables["timelineAfter"] = i.timeline.variables["timelineAfter"]
- variables["commentEditLast"] = i.timeline.variables["commentEditLast"]
- variables["commentEditBefore"] = i.timeline.variables["commentEditBefore"]
-
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
-
- // if err := i.gc.Query(ctx, &i.timeline.query, i.timeline.variables); err != nil {
- if err := i.gc.Query(ctx, &query, variables); err != nil {
- i.err = err
- return false
+ tlIter := &i.issueIter.timelineIter[i.issueIter.index]
+ tlIdx := &tlIter.index
+ tlItems := tlIter.query.Node.Issue.TimelineItems
+ if 0 <= *tlIdx && *tlIdx < len(tlItems.Nodes)-1 {
+ *tlIdx += 1
+ return true
}
- // HACK
- fmt.Println("### Alex after the query")
- i.timeline.variables["timelineFirst"] = variables["timelineFirst"]
- i.timeline.variables["timelineAfter"] = variables["timelineAfter"]
- i.timeline.variables["commentEditLast"] = variables["commentEditLast"]
- i.timeline.variables["commentEditBefore"] = variables["commentEditBefore"]
- i.timeline.query.Repository.Issues.Nodes[0].TimelineItems = query.Repository.Issue.TimelineItems
-
- timelineItems = i.timeline.query.Repository.Issues.Nodes[0].TimelineItems
- // (in case github returns something weird) just for safety: better return a false than a panic
- if len(timelineItems.Edges) == 0 {
+ if !tlItems.PageInfo.HasNextPage {
return false
}
-
- i.reverseTimelineEditNodes()
- i.timeline.index = 0
- return true
+ nextTlItem := i.queryTimeline()
+ return nextTlItem
}
-// TimelineItemValue return the actual timeline item value
func (i *iterator) TimelineItemValue() timelineItem {
- timelineItems := i.timeline.query.Repository.Issues.Nodes[0].TimelineItems
- return timelineItems.Edges[i.timeline.index].Node
+ tli := i.currTimelineIter()
+ return tli.query.Node.Issue.TimelineItems.Nodes[tli.index]
}
-func (i *iterator) queryIssueEdit() bool {
+func (i *iterator) queryTimeline() bool {
ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
defer cancel()
-
- if err := i.gc.Query(ctx, &i.issueEdit.query, i.issueEdit.variables); err != nil {
+ tli := i.currTimelineIter()
+ if endCursor := tli.query.Node.Issue.TimelineItems.PageInfo.EndCursor; endCursor != "" {
+ tli.variables["timelineAfter"] = endCursor
+ }
+ tli.variables["gqlNodeId"] = i.currIssueGqlNodeId()
+ if err := i.gc.Query(ctx, &tli.query, tli.variables); err != nil {
i.err = err
- //i.timeline.issueEdit.index = -1
return false
}
-
- issueEdits := i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits
- // reverse issue edits because github
- reverseEdits(issueEdits.Nodes)
-
- // this is not supposed to happen
- if len(issueEdits.Nodes) == 0 {
- i.timeline.issueEdit.index = -1
+ i.resetCommentEditVars()
+ timelineItems := &tli.query.Node.Issue.TimelineItems
+ if len(timelineItems.Nodes) <= 0 {
+ tli.index = -1
return false
}
-
- i.issueEdit.index = 0
- i.timeline.issueEdit.index = -2
- return i.nextValidIssueEdit()
-}
-
-func (i *iterator) nextValidIssueEdit() bool {
- // issueEdit.Diff == nil happen if the event is older than early 2018, Github doesn't have the data before that.
- // Best we can do is to ignore the event.
- if issueEdit := i.IssueEditValue(); issueEdit.Diff == nil || string(*issueEdit.Diff) == "" {
- return i.NextIssueEdit()
- }
+ tli.index = 0
return true
}
-// NextIssueEdit return true if there is a next issue edit and increments the index by one.
-// It is used iterates over all the issue edits. Extra queries are made if it is necessary.
-func (i *iterator) NextIssueEdit() bool {
- if i.err != nil {
- return false
- }
-
- if i.ctx.Err() != nil {
- return false
- }
-
- // this mean we looped over all available issue edits in the timeline.
- // now we have to use i.issueEditQuery
- if i.timeline.issueEdit.index == -2 {
- issueEdits := i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits
- if i.issueEdit.index < len(issueEdits.Nodes)-1 {
- i.issueEdit.index++
- return i.nextValidIssueEdit()
- }
-
- if !issueEdits.PageInfo.HasPreviousPage {
- i.timeline.issueEdit.index = -1
- i.issueEdit.index = -1
- return false
- }
-
- // if there is more edits, query them
- i.issueEdit.variables["issueEditBefore"] = issueEdits.PageInfo.StartCursor
- return i.queryIssueEdit()
- }
-
- issueEdits := i.timeline.query.Repository.Issues.Nodes[0].UserContentEdits
- // if there is no edit, the UserContentEdits given by github is empty. That
- // means that the original message is given by the issue message.
- //
- // if there is edits, the UserContentEdits given by github contains both the
- // original message and the following edits. The issue message give the last
- // version so we don't care about that.
- //
- // the tricky part: for an issue older than the UserContentEdits API, github
- // doesn't have the previous message version anymore and give an edition
- // with .Diff == nil. We have to filter them.
- if len(issueEdits.Nodes) == 0 {
+func (i *iterator) NextCommentEdit() bool {
+ if i.HasError() {
return false
}
- // loop over them timeline comment edits
- if i.timeline.issueEdit.index < len(issueEdits.Nodes)-1 {
- i.timeline.issueEdit.index++
- return i.nextValidIssueEdit()
- }
-
- if !issueEdits.PageInfo.HasPreviousPage {
- i.timeline.issueEdit.index = -1
+ tmlnVal := i.TimelineItemValue()
+ if tmlnVal.Typename != "IssueComment" {
+ // The timeline iterator does not point to a comment.
+ i.err = errors.New("Call to NextCommentEdit() while timeline item is not a comment")
return false
}
- // if there is more edits, query them
- i.initIssueEditQueryVariables()
- i.issueEdit.variables["issueEditBefore"] = issueEdits.PageInfo.StartCursor
- return i.queryIssueEdit()
-}
-
-// IssueEditValue return the actual issue edit value
-func (i *iterator) IssueEditValue() userContentEdit {
- // if we are using issue edit query
- if i.timeline.issueEdit.index == -2 {
- issueEdits := i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits
- return issueEdits.Nodes[i.issueEdit.index]
+ cei := i.currCommentEditIter()
+ ceIdx := &cei.index
+ ceItems := &cei.query.Node.IssueComment.UserContentEdits
+ if 0 <= *ceIdx && *ceIdx < len(ceItems.Nodes)-1 {
+ *ceIdx += 1
+ return i.nextValidCommentEdit()
}
-
- issueEdits := i.timeline.query.Repository.Issues.Nodes[0].UserContentEdits
- // else get it from timeline issue edit query
- return issueEdits.Nodes[i.timeline.issueEdit.index]
-}
-
-func (i *iterator) queryCommentEdit() bool {
- ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
- defer cancel()
-
- if err := i.gc.Query(ctx, &i.commentEdit.query, i.commentEdit.variables); err != nil {
- i.err = err
+ if !ceItems.PageInfo.HasNextPage {
return false
}
-
- commentEdits := i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits
- // this is not supposed to happen
- if len(commentEdits.Nodes) == 0 {
- i.timeline.commentEdit.index = -1
+ querySucc := i.queryCommentEdit()
+ if !querySucc {
return false
}
-
- reverseEdits(commentEdits.Nodes)
-
- i.commentEdit.index = 0
- i.timeline.commentEdit.index = -2
return i.nextValidCommentEdit()
}
@@ -797,72 +349,39 @@ func (i *iterator) nextValidCommentEdit() bool {
return true
}
-// NextCommentEdit return true if there is a next comment edit and increments the index by one.
-// It is used iterates over all the comment edits. Extra queries are made if it is necessary.
-func (i *iterator) NextCommentEdit() bool {
- if i.err != nil {
- return false
- }
-
- if i.ctx.Err() != nil {
- return false
- }
-
- // same as NextIssueEdit
- if i.timeline.commentEdit.index == -2 {
- commentEdits := i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits
- if i.commentEdit.index < len(commentEdits.Nodes)-1 {
- i.commentEdit.index++
- return i.nextValidCommentEdit()
- }
+func (i *iterator) CommentEditValue() userContentEdit {
+ cei := i.currCommentEditIter()
+ return cei.query.Node.IssueComment.UserContentEdits.Nodes[cei.index]
+}
- if !commentEdits.PageInfo.HasPreviousPage {
- i.timeline.commentEdit.index = -1
- i.commentEdit.index = -1
- return false
- }
+func (i *iterator) queryCommentEdit() bool {
+ ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
+ defer cancel()
+ cei := i.currCommentEditIter()
- // if there is more comment edits, query them
- i.commentEdit.variables["commentEditBefore"] = commentEdits.PageInfo.StartCursor
- return i.queryCommentEdit()
+ if endCursor := cei.query.Node.IssueComment.UserContentEdits.PageInfo.EndCursor; endCursor != "" {
+ cei.variables["commentEditBefore"] = endCursor
}
-
- commentEdits := i.timeline.query.Repository.Issues.Nodes[0].TimelineItems.Edges[i.timeline.index].Node.IssueComment
- // if there is no comment edits
- if len(commentEdits.UserContentEdits.Nodes) == 0 {
+ tmlnVal := i.TimelineItemValue()
+ if tmlnVal.Typename != "IssueComment" {
+ i.err = errors.New("Call to queryCommentEdit() while timeline item is not a comment")
return false
}
-
- // loop over them timeline comment edits
- if i.timeline.commentEdit.index < len(commentEdits.UserContentEdits.Nodes)-1 {
- i.timeline.commentEdit.index++
- return i.nextValidCommentEdit()
- }
-
- if !commentEdits.UserContentEdits.PageInfo.HasPreviousPage {
- i.timeline.commentEdit.index = -1
+ cei.variables["gqlNodeId"] = tmlnVal.IssueComment.Id
+ if err := i.gc.Query(ctx, &cei.query, cei.variables); err != nil {
+ i.err = err
return false
}
-
- i.initCommentEditQueryVariables()
- if i.timeline.index == 0 {
- i.commentEdit.variables["timelineAfter"] = i.timeline.lastEndCursor
- } else {
- i.commentEdit.variables["timelineAfter"] = i.timeline.query.Repository.Issues.Nodes[0].TimelineItems.Edges[i.timeline.index-1].Cursor
- }
-
- i.commentEdit.variables["commentEditBefore"] = commentEdits.UserContentEdits.PageInfo.StartCursor
-
- return i.queryCommentEdit()
-}
-
-// CommentEditValue return the actual comment edit value
-func (i *iterator) CommentEditValue() userContentEdit {
- if i.timeline.commentEdit.index == -2 {
- return i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.Nodes[i.commentEdit.index]
+ ceItems := cei.query.Node.IssueComment.UserContentEdits.Nodes
+ if len(ceItems) <= 0 {
+ cei.index = -1
+ return false
}
-
- return i.timeline.query.Repository.Issues.Nodes[0].TimelineItems.Edges[i.timeline.index].Node.IssueComment.UserContentEdits.Nodes[i.timeline.commentEdit.index]
+ // The UserContentEditConnection in the Github API serves its elements in reverse chronological
+ // order. For our purpose we have to reverse the edits.
+ reverseEdits(ceItems)
+ cei.index = 0
+ return true
}
func reverseEdits(edits []userContentEdit) {