iterator.go

  1package gitlab
  2
  3import (
  4	"context"
  5	"sort"
  6	"time"
  7
  8	"github.com/xanzy/go-gitlab"
  9)
 10
 11type issueIterator struct {
 12	page  int
 13	index int
 14	cache []*gitlab.Issue
 15}
 16
 17type noteIterator struct {
 18	page  int
 19	index int
 20	cache []*gitlab.Note
 21}
 22
 23type labelEventIterator struct {
 24	page  int
 25	index int
 26	cache []*gitlab.LabelEvent
 27}
 28
 29func (l *labelEventIterator) Len() int {
 30	return len(l.cache)
 31}
 32
 33func (l *labelEventIterator) Swap(i, j int) {
 34	element := l.cache[i]
 35	l.cache[i] = l.cache[j]
 36	l.cache[j] = element
 37}
 38
 39func (l *labelEventIterator) Less(i, j int) bool {
 40	return l.cache[i].ID < l.cache[j].ID
 41}
 42
 43type iterator struct {
 44	// gitlab api v4 client
 45	gc *gitlab.Client
 46
 47	// if since is given the iterator will query only the issues
 48	// updated after this date
 49	since time.Time
 50
 51	// project id
 52	project string
 53
 54	// number of issues and notes to query at once
 55	capacity int
 56
 57	// shared context
 58	ctx context.Context
 59
 60	// sticky error
 61	err error
 62
 63	// issues iterator
 64	issue *issueIterator
 65
 66	// notes iterator
 67	note *noteIterator
 68
 69	// labelEvent iterator
 70	labelEvent *labelEventIterator
 71}
 72
 73// NewIterator create a new iterator
 74func NewIterator(ctx context.Context, capacity int, projectID, token string, since time.Time) *iterator {
 75	return &iterator{
 76		gc:       buildClient(token),
 77		project:  projectID,
 78		since:    since,
 79		capacity: capacity,
 80		ctx:      ctx,
 81		issue: &issueIterator{
 82			index: -1,
 83			page:  1,
 84		},
 85		note: &noteIterator{
 86			index: -1,
 87			page:  1,
 88		},
 89		labelEvent: &labelEventIterator{
 90			index: -1,
 91			page:  1,
 92		},
 93	}
 94}
 95
 96// Error return last encountered error
 97func (i *iterator) Error() error {
 98	return i.err
 99}
100
101func (i *iterator) getNextIssues() bool {
102	ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
103	defer cancel()
104
105	issues, _, err := i.gc.Issues.ListProjectIssues(
106		i.project,
107		&gitlab.ListProjectIssuesOptions{
108			ListOptions: gitlab.ListOptions{
109				Page:    i.issue.page,
110				PerPage: i.capacity,
111			},
112			Scope:        gitlab.String("all"),
113			UpdatedAfter: &i.since,
114			Sort:         gitlab.String("asc"),
115		},
116		gitlab.WithContext(ctx),
117	)
118
119	if err != nil {
120		i.err = err
121		return false
122	}
123
124	// if repository doesn't have any issues
125	if len(issues) == 0 {
126		return false
127	}
128
129	i.issue.cache = issues
130	i.issue.index = 0
131	i.issue.page++
132	i.note.index = -1
133	i.note.cache = nil
134
135	return true
136}
137
138func (i *iterator) NextIssue() bool {
139	if i.err != nil {
140		return false
141	}
142
143	if i.ctx.Err() != nil {
144		return false
145	}
146
147	// first query
148	if i.issue.cache == nil {
149		return i.getNextIssues()
150	}
151
152	// move cursor index
153	if i.issue.index < len(i.issue.cache)-1 {
154		i.issue.index++
155		return true
156	}
157
158	return i.getNextIssues()
159}
160
161func (i *iterator) IssueValue() *gitlab.Issue {
162	return i.issue.cache[i.issue.index]
163}
164
165func (i *iterator) getNextNotes() bool {
166	ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
167	defer cancel()
168
169	notes, _, err := i.gc.Notes.ListIssueNotes(
170		i.project,
171		i.IssueValue().IID,
172		&gitlab.ListIssueNotesOptions{
173			ListOptions: gitlab.ListOptions{
174				Page:    i.note.page,
175				PerPage: i.capacity,
176			},
177			Sort:    gitlab.String("asc"),
178			OrderBy: gitlab.String("created_at"),
179		},
180		gitlab.WithContext(ctx),
181	)
182
183	if err != nil {
184		i.err = err
185		return false
186	}
187
188	if len(notes) == 0 {
189		i.note.index = -1
190		i.note.page = 1
191		i.note.cache = nil
192		return false
193	}
194
195	i.note.cache = notes
196	i.note.page++
197	i.note.index = 0
198	return true
199}
200
201func (i *iterator) NextNote() bool {
202	if i.err != nil {
203		return false
204	}
205
206	if i.ctx.Err() != nil {
207		return false
208	}
209
210	if len(i.note.cache) == 0 {
211		return i.getNextNotes()
212	}
213
214	// move cursor index
215	if i.note.index < len(i.note.cache)-1 {
216		i.note.index++
217		return true
218	}
219
220	return i.getNextNotes()
221}
222
223func (i *iterator) NoteValue() *gitlab.Note {
224	return i.note.cache[i.note.index]
225}
226
227func (i *iterator) getLabelEvents() bool {
228	ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
229	defer cancel()
230
231	hasNextPage := true
232	for hasNextPage {
233		labelEvents, _, err := i.gc.ResourceLabelEvents.ListIssueLabelEvents(
234			i.project,
235			i.IssueValue().IID,
236			&gitlab.ListLabelEventsOptions{
237				ListOptions: gitlab.ListOptions{
238					Page:    i.labelEvent.page,
239					PerPage: i.capacity,
240				},
241			},
242			gitlab.WithContext(ctx),
243		)
244		if err != nil {
245			i.err = err
246			return false
247		}
248
249		i.labelEvent.page++
250		hasNextPage = len(labelEvents) != 0
251		i.labelEvent.cache = append(i.labelEvent.cache, labelEvents...)
252	}
253
254	i.labelEvent.page = 1
255	i.labelEvent.index = 0
256	sort.Sort(i.labelEvent)
257
258	// if the label events list is empty return false
259	return len(i.labelEvent.cache) != 0
260}
261
262// because Gitlab
263func (i *iterator) NextLabelEvent() bool {
264	if i.err != nil {
265		return false
266	}
267
268	if i.ctx.Err() != nil {
269		return false
270	}
271
272	if len(i.labelEvent.cache) == 0 {
273		return i.getLabelEvents()
274	}
275
276	// move cursor index
277	if i.labelEvent.index < len(i.labelEvent.cache)-1 {
278		i.labelEvent.index++
279		return true
280	}
281
282	return false
283}
284
285func (i *iterator) LabelEventValue() *gitlab.LabelEvent {
286	return i.labelEvent.cache[i.labelEvent.index]
287}