1package gitlab
2
3import (
4 "time"
5
6 "github.com/xanzy/go-gitlab"
7)
8
9type issueIterator struct {
10 page int
11 index int
12 cache []*gitlab.Issue
13}
14
15type commentIterator struct {
16 page int
17 index int
18 cache []*gitlab.Note
19}
20
21type iterator struct {
22 // gitlab api v4 client
23 gc *gitlab.Client
24
25 // if since is given the iterator will query only the updated
26 // issues after this date
27 since time.Time
28
29 // project id
30 project string
31
32 // number of issues and notes to query at once
33 capacity int
34
35 // sticky error
36 err error
37
38 // issues iterator
39 issue *issueIterator
40
41 // comments iterator
42 comment *commentIterator
43}
44
45// NewIterator create a new iterator
46func NewIterator(projectID, token string, capacity int, since time.Time) *iterator {
47 return &iterator{
48 gc: buildClient(token),
49 project: projectID,
50 since: since,
51 capacity: capacity,
52 issue: &issueIterator{
53 index: -1,
54 page: 1,
55 },
56 comment: &commentIterator{
57 index: -1,
58 page: 1,
59 },
60 }
61}
62
63// Error return last encountered error
64func (i *iterator) Error() error {
65 return i.err
66}
67
68func (i *iterator) getIssues() ([]*gitlab.Issue, error) {
69 scope := "all"
70 issues, _, err := i.gc.Issues.ListProjectIssues(
71 i.project,
72 &gitlab.ListProjectIssuesOptions{
73 ListOptions: gitlab.ListOptions{
74 Page: i.issue.page,
75 PerPage: i.capacity,
76 },
77 Scope: &scope,
78 UpdatedAfter: &i.since,
79 },
80 )
81
82 return issues, err
83}
84
85func (i *iterator) NextIssue() bool {
86 // first query
87 if i.issue.cache == nil {
88 issues, err := i.getIssues()
89 if err != nil {
90 i.err = err
91 return false
92 }
93
94 // if repository doesn't have any issues
95 if len(issues) == 0 {
96 return false
97 }
98
99 i.issue.cache = issues
100 i.issue.index++
101 return true
102 }
103
104 if i.err != nil {
105 return false
106 }
107
108 // move cursor index
109 if i.issue.index < min(i.capacity, len(i.issue.cache)) {
110 i.issue.index++
111 return true
112 }
113
114 // query next issues
115 issues, err := i.getIssues()
116 if err != nil {
117 i.err = err
118 return false
119 }
120
121 // no more issues to query
122 if len(issues) == 0 {
123 return false
124 }
125
126 i.issue.page++
127 i.issue.index = 0
128 i.comment.index = 0
129
130 return true
131}
132
133func (i *iterator) IssueValue() *gitlab.Issue {
134 return i.issue.cache[i.issue.index]
135}
136
137func (i *iterator) getComments() ([]*gitlab.Note, error) {
138 notes, _, err := i.gc.Notes.ListIssueNotes(
139 i.project,
140 i.IssueValue().IID,
141 &gitlab.ListIssueNotesOptions{
142 ListOptions: gitlab.ListOptions{
143 Page: i.issue.page,
144 PerPage: i.capacity,
145 },
146 },
147 )
148
149 return notes, err
150}
151
152func (i *iterator) NextComment() bool {
153 if i.err != nil {
154 return false
155 }
156
157 if len(i.comment.cache) == 0 {
158 // query next issues
159 comments, err := i.getComments()
160 if err != nil {
161 i.err = err
162 return false
163 }
164
165 if len(comments) == 0 {
166 i.comment.index = 0
167 i.comment.page = 1
168 return false
169 }
170
171 i.comment.page++
172 i.comment.index = 0
173
174 return true
175 }
176
177 // move cursor index
178 if i.comment.index < min(i.capacity, len(i.comment.cache)) {
179 i.comment.index++
180 return true
181 }
182
183 // query next issues
184 comments, err := i.getComments()
185 if err != nil {
186 i.err = err
187 return false
188 }
189
190 if len(comments) == 0 {
191 i.comment.index = 0
192 i.comment.page = 1
193 return false
194 }
195
196 i.comment.page++
197 i.comment.index = 0
198
199 return false
200}
201
202func (i *iterator) CommentValue() *gitlab.Note {
203 return i.comment.cache[i.comment.index]
204}
205
206func min(a, b int) int {
207 if a > b {
208 return b
209 }
210
211 return a
212}