aboutsummaryrefslogtreecommitdiffstats
path: root/bridge/gitlab/iterator
diff options
context:
space:
mode:
authorMatthias Simon <matthias.simon@nokia.com>2021-02-14 20:35:03 +0100
committerMatthias Simon <matthias.simon@nokia.com>2021-04-23 09:02:48 +0200
commitaa4e225a80b37ce26f5f8c69041ee735f512b113 (patch)
treef34b7ff03f4cec4be4ed6eb3458d515452a92fe6 /bridge/gitlab/iterator
parenta8f3b55986982db5f7c3918acaba2c214c919d11 (diff)
downloadgit-bug-aa4e225a80b37ce26f5f8c69041ee735f512b113.tar.gz
gitlab: Add new iterator with state change events
Retrieving events is spread across various various Gitlab APIs. This makes importing and sorting Gitlab events by time quite complicated. This commit replaces the old iterators with a goroutine/channel-based iterator, which merges the individual Gitlab API streams into a single (sorted) event stream.
Diffstat (limited to 'bridge/gitlab/iterator')
-rw-r--r--bridge/gitlab/iterator/issue.go89
-rw-r--r--bridge/gitlab/iterator/iterator.go138
-rw-r--r--bridge/gitlab/iterator/labelEvent.go105
-rw-r--r--bridge/gitlab/iterator/note.go90
4 files changed, 0 insertions, 422 deletions
diff --git a/bridge/gitlab/iterator/issue.go b/bridge/gitlab/iterator/issue.go
deleted file mode 100644
index 9361b496..00000000
--- a/bridge/gitlab/iterator/issue.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package iterator
-
-import (
- "context"
-
- "github.com/xanzy/go-gitlab"
-)
-
-type issueIterator struct {
- page int
- lastPage bool
- index int
- cache []*gitlab.Issue
-}
-
-func newIssueIterator() *issueIterator {
- ii := &issueIterator{}
- ii.Reset()
- return ii
-}
-
-func (ii *issueIterator) Next(ctx context.Context, conf config) (bool, error) {
- // first query
- if ii.cache == nil {
- return ii.getNext(ctx, conf)
- }
-
- // move cursor index
- if ii.index < len(ii.cache)-1 {
- ii.index++
- return true, nil
- }
-
- return ii.getNext(ctx, conf)
-}
-
-func (ii *issueIterator) Value() *gitlab.Issue {
- return ii.cache[ii.index]
-}
-
-func (ii *issueIterator) getNext(ctx context.Context, conf config) (bool, error) {
- if ii.lastPage {
- return false, nil
- }
-
- ctx, cancel := context.WithTimeout(ctx, conf.timeout)
- defer cancel()
-
- issues, resp, err := conf.gc.Issues.ListProjectIssues(
- conf.project,
- &gitlab.ListProjectIssuesOptions{
- ListOptions: gitlab.ListOptions{
- Page: ii.page,
- PerPage: conf.capacity,
- },
- Scope: gitlab.String("all"),
- UpdatedAfter: &conf.since,
- Sort: gitlab.String("asc"),
- },
- gitlab.WithContext(ctx),
- )
-
- if err != nil {
- ii.Reset()
- return false, err
- }
-
- if resp.TotalPages == ii.page {
- ii.lastPage = true
- }
-
- // if repository doesn't have any issues
- if len(issues) == 0 {
- return false, nil
- }
-
- ii.cache = issues
- ii.index = 0
- ii.page++
-
- return true, nil
-}
-
-func (ii *issueIterator) Reset() {
- ii.index = -1
- ii.page = 1
- ii.lastPage = false
- ii.cache = nil
-}
diff --git a/bridge/gitlab/iterator/iterator.go b/bridge/gitlab/iterator/iterator.go
deleted file mode 100644
index ee2090b0..00000000
--- a/bridge/gitlab/iterator/iterator.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package iterator
-
-import (
- "context"
- "time"
-
- "github.com/xanzy/go-gitlab"
-)
-
-type Iterator struct {
- // shared context
- ctx context.Context
-
- // to pass to sub-iterators
- conf config
-
- // sticky error
- err error
-
- // issues iterator
- issue *issueIterator
-
- // notes iterator
- note *noteIterator
-
- // labelEvent iterator
- labelEvent *labelEventIterator
-}
-
-type config struct {
- // gitlab api v4 client
- gc *gitlab.Client
-
- timeout time.Duration
-
- // if since is given the iterator will query only the issues
- // updated after this date
- since time.Time
-
- // project id
- project string
-
- // number of issues and notes to query at once
- capacity int
-}
-
-// NewIterator create a new iterator
-func NewIterator(ctx context.Context, client *gitlab.Client, capacity int, projectID string, since time.Time) *Iterator {
- return &Iterator{
- ctx: ctx,
- conf: config{
- gc: client,
- timeout: 60 * time.Second,
- since: since,
- project: projectID,
- capacity: capacity,
- },
- issue: newIssueIterator(),
- note: newNoteIterator(),
- labelEvent: newLabelEventIterator(),
- }
-}
-
-// Error return last encountered error
-func (i *Iterator) Error() error {
- return i.err
-}
-
-func (i *Iterator) NextIssue() bool {
- if i.err != nil {
- return false
- }
-
- if i.ctx.Err() != nil {
- return false
- }
-
- more, err := i.issue.Next(i.ctx, i.conf)
- if err != nil {
- i.err = err
- return false
- }
-
- // Also reset the other sub iterators as they would
- // no longer be valid
- i.note.Reset(i.issue.Value().IID)
- i.labelEvent.Reset(i.issue.Value().IID)
-
- return more
-}
-
-func (i *Iterator) IssueValue() *gitlab.Issue {
- return i.issue.Value()
-}
-
-func (i *Iterator) NextNote() bool {
- if i.err != nil {
- return false
- }
-
- if i.ctx.Err() != nil {
- return false
- }
-
- more, err := i.note.Next(i.ctx, i.conf)
- if err != nil {
- i.err = err
- return false
- }
-
- return more
-}
-
-func (i *Iterator) NoteValue() *gitlab.Note {
- return i.note.Value()
-}
-
-func (i *Iterator) NextLabelEvent() bool {
- if i.err != nil {
- return false
- }
-
- if i.ctx.Err() != nil {
- return false
- }
-
- more, err := i.labelEvent.Next(i.ctx, i.conf)
- if err != nil {
- i.err = err
- return false
- }
-
- return more
-}
-
-func (i *Iterator) LabelEventValue() *gitlab.LabelEvent {
- return i.labelEvent.Value()
-}
diff --git a/bridge/gitlab/iterator/labelEvent.go b/bridge/gitlab/iterator/labelEvent.go
deleted file mode 100644
index 812e6646..00000000
--- a/bridge/gitlab/iterator/labelEvent.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package iterator
-
-import (
- "context"
- "sort"
-
- "github.com/xanzy/go-gitlab"
-)
-
-// Since Gitlab does not return the label events items in the correct order
-// we need to sort the list ourselves and stop relying on the pagination model
-// #BecauseGitlab
-type labelEventIterator struct {
- issue int
- index int
- cache []*gitlab.LabelEvent
-}
-
-func newLabelEventIterator() *labelEventIterator {
- lei := &labelEventIterator{}
- lei.Reset(-1)
- return lei
-}
-
-func (lei *labelEventIterator) Next(ctx context.Context, conf config) (bool, error) {
- // first query
- if lei.cache == nil {
- return lei.getNext(ctx, conf)
- }
-
- // move cursor index
- if lei.index < len(lei.cache)-1 {
- lei.index++
- return true, nil
- }
-
- return false, nil
-}
-
-func (lei *labelEventIterator) Value() *gitlab.LabelEvent {
- return lei.cache[lei.index]
-}
-
-func (lei *labelEventIterator) getNext(ctx context.Context, conf config) (bool, error) {
- ctx, cancel := context.WithTimeout(ctx, conf.timeout)
- defer cancel()
-
- // since order is not guaranteed we should query all label events
- // and sort them by ID
- page := 1
- for {
- labelEvents, resp, err := conf.gc.ResourceLabelEvents.ListIssueLabelEvents(
- conf.project,
- lei.issue,
- &gitlab.ListLabelEventsOptions{
- ListOptions: gitlab.ListOptions{
- Page: page,
- PerPage: conf.capacity,
- },
- },
- gitlab.WithContext(ctx),
- )
- if err != nil {
- lei.Reset(-1)
- return false, err
- }
-
- if len(labelEvents) == 0 {
- break
- }
-
- lei.cache = append(lei.cache, labelEvents...)
-
- if resp.TotalPages == page {
- break
- }
-
- page++
- }
-
- sort.Sort(lei)
- lei.index = 0
-
- return len(lei.cache) > 0, nil
-}
-
-func (lei *labelEventIterator) Reset(issue int) {
- lei.issue = issue
- lei.index = -1
- lei.cache = nil
-}
-
-// ORDERING
-
-func (lei *labelEventIterator) Len() int {
- return len(lei.cache)
-}
-
-func (lei *labelEventIterator) Swap(i, j int) {
- lei.cache[i], lei.cache[j] = lei.cache[j], lei.cache[i]
-}
-
-func (lei *labelEventIterator) Less(i, j int) bool {
- return lei.cache[i].ID < lei.cache[j].ID
-}
diff --git a/bridge/gitlab/iterator/note.go b/bridge/gitlab/iterator/note.go
deleted file mode 100644
index a1e0544c..00000000
--- a/bridge/gitlab/iterator/note.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package iterator
-
-import (
- "context"
-
- "github.com/xanzy/go-gitlab"
-)
-
-type noteIterator struct {
- issue int
- page int
- lastPage bool
- index int
- cache []*gitlab.Note
-}
-
-func newNoteIterator() *noteIterator {
- in := &noteIterator{}
- in.Reset(-1)
- return in
-}
-
-func (in *noteIterator) Next(ctx context.Context, conf config) (bool, error) {
- // first query
- if in.cache == nil {
- return in.getNext(ctx, conf)
- }
-
- // move cursor index
- if in.index < len(in.cache)-1 {
- in.index++
- return true, nil
- }
-
- return in.getNext(ctx, conf)
-}
-
-func (in *noteIterator) Value() *gitlab.Note {
- return in.cache[in.index]
-}
-
-func (in *noteIterator) getNext(ctx context.Context, conf config) (bool, error) {
- if in.lastPage {
- return false, nil
- }
-
- ctx, cancel := context.WithTimeout(ctx, conf.timeout)
- defer cancel()
-
- notes, resp, err := conf.gc.Notes.ListIssueNotes(
- conf.project,
- in.issue,
- &gitlab.ListIssueNotesOptions{
- ListOptions: gitlab.ListOptions{
- Page: in.page,
- PerPage: conf.capacity,
- },
- Sort: gitlab.String("asc"),
- OrderBy: gitlab.String("created_at"),
- },
- gitlab.WithContext(ctx),
- )
-
- if err != nil {
- in.Reset(-1)
- return false, err
- }
-
- if resp.TotalPages == in.page {
- in.lastPage = true
- }
-
- if len(notes) == 0 {
- return false, nil
- }
-
- in.cache = notes
- in.index = 0
- in.page++
-
- return true, nil
-}
-
-func (in *noteIterator) Reset(issue int) {
- in.issue = issue
- in.index = -1
- in.page = 1
- in.lastPage = false
- in.cache = nil
-}