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