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) getNextLabelEvents() bool {
228	ctx, cancel := context.WithTimeout(i.ctx, defaultTimeout)
229	defer cancel()
230
231	labelEvents, _, err := i.gc.ResourceLabelEvents.ListIssueLabelEvents(
232		i.project,
233		i.IssueValue().IID,
234		&gitlab.ListLabelEventsOptions{
235			ListOptions: gitlab.ListOptions{
236				Page:    i.labelEvent.page,
237				PerPage: i.capacity,
238			},
239		},
240		gitlab.WithContext(ctx),
241	)
242
243	if err != nil {
244		i.err = err
245		return false
246	}
247
248	if len(labelEvents) == 0 {
249		i.labelEvent.page = 1
250		i.labelEvent.index = -1
251		i.labelEvent.cache = nil
252		return false
253	}
254
255	i.labelEvent.cache = labelEvents
256	i.labelEvent.page++
257	i.labelEvent.index = 0
258
259	sort.Sort(i.labelEvent)
260	return true
261}
262
263// because Gitlab
264func (i *iterator) NextLabelEvent() bool {
265	if i.err != nil {
266		return false
267	}
268
269	if i.ctx.Err() != nil {
270		return false
271	}
272
273	if len(i.labelEvent.cache) == 0 {
274		return i.getNextLabelEvents()
275	}
276
277	// move cursor index
278	if i.labelEvent.index < len(i.labelEvent.cache)-1 {
279		i.labelEvent.index++
280		return true
281	}
282
283	return i.getNextLabelEvents()
284}
285
286func (i *iterator) LabelEventValue() *gitlab.LabelEvent {
287	return i.labelEvent.cache[i.labelEvent.index]
288}