@@ -82,17 +82,42 @@ func CollectEpics(taskCtx plugin.SubTaskContext) errors.Error {
8282 jql = buildJQL (* apiCollector .GetSince (), loc )
8383 }
8484
85- err = apiCollector .InitCollector (api.ApiCollectorArgs {
85+ // Choose API endpoint based on JIRA deployment type
86+ if data .JiraServerInfo .DeploymentType == models .DeploymentServer {
87+ logger .Info ("Using api/2/search for JIRA Server" )
88+ err = setupApiV2Collector (apiCollector , data , epicIterator , jql )
89+ } else {
90+ logger .Info ("Using api/3/search/jql for JIRA Cloud" )
91+ err = setupApiV3Collector (apiCollector , data , epicIterator , jql )
92+ }
93+ if err != nil {
94+ return err
95+ }
96+ return apiCollector .Execute ()
97+ }
98+
99+ // JIRA Server API v2 collector
100+ func setupApiV2Collector (apiCollector * api.StatefulApiCollector , data * JiraTaskData , epicIterator api.Iterator , jql string ) errors.Error {
101+ return apiCollector .InitCollector (api.ApiCollectorArgs {
86102 ApiClient : data .ApiClient ,
87103 PageSize : 100 ,
88104 Incremental : false ,
89105 UrlTemplate : "api/2/search" ,
90106 Query : func (reqData * api.RequestData ) (url.Values , errors.Error ) {
91107 query := url.Values {}
92108 epicKeys := []string {}
93- for _ , e := range reqData .Input .([]interface {}) {
94- epicKeys = append (epicKeys , * e .(* string ))
109+
110+ input , ok := reqData .Input .([]interface {})
111+ if ! ok {
112+ return nil , errors .Default .New ("invalid input type, expected []interface{}" )
95113 }
114+
115+ for _ , e := range input {
116+ if epicKey , ok := e .(* string ); ok && epicKey != nil {
117+ epicKeys = append (epicKeys , * epicKey )
118+ }
119+ }
120+
96121 localJQL := fmt .Sprintf ("issue in (%s) and %s" , strings .Join (epicKeys , "," ), jql )
97122 query .Set ("jql" , localJQL )
98123 query .Set ("startAt" , fmt .Sprintf ("%v" , reqData .Pager .Skip ))
@@ -117,13 +142,78 @@ func CollectEpics(taskCtx plugin.SubTaskContext) errors.Error {
117142 }
118143 return data .Issues , nil
119144 },
120- // Jira Server returns 400 if the epic is not found
121145 AfterResponse : ignoreHTTPStatus400 ,
122146 })
147+ }
148+
149+ // JIRA Cloud API v3 collector
150+ func setupApiV3Collector (apiCollector * api.StatefulApiCollector , data * JiraTaskData , epicIterator api.Iterator , jql string ) errors.Error {
151+ return apiCollector .InitCollector (api.ApiCollectorArgs {
152+ ApiClient : data .ApiClient ,
153+ PageSize : 100 ,
154+ Incremental : false ,
155+ UrlTemplate : "api/3/search/jql" ,
156+ GetNextPageCustomData : getNextPageCustomDataForV3 ,
157+ Query : func (reqData * api.RequestData ) (url.Values , errors.Error ) {
158+ query := url.Values {}
159+ epicKeys := []string {}
160+ for _ , e := range reqData .Input .([]interface {}) {
161+ epicKeys = append (epicKeys , * e .(* string ))
162+ }
163+ localJQL := fmt .Sprintf ("issue in (%s) and %s" , strings .Join (epicKeys , "," ), jql )
164+ query .Set ("jql" , localJQL )
165+ query .Set ("maxResults" , fmt .Sprintf ("%v" , reqData .Pager .Size ))
166+ query .Set ("expand" , "changelog" )
167+ query .Set ("fields" , "*all" )
168+
169+ if reqData .CustomData != nil {
170+ query .Set ("nextPageToken" , reqData .CustomData .(string ))
171+ }
172+
173+ return query , nil
174+ },
175+ Input : epicIterator ,
176+ ResponseParser : func (res * http.Response ) ([]json.RawMessage , errors.Error ) {
177+ var data struct {
178+ Issues []json.RawMessage `json:"issues"`
179+ }
180+ blob , err := io .ReadAll (res .Body )
181+ if err != nil {
182+ return nil , errors .Convert (err )
183+ }
184+ err = json .Unmarshal (blob , & data )
185+ if err != nil {
186+ return nil , errors .Convert (err )
187+ }
188+ return data .Issues , nil
189+ },
190+ AfterResponse : ignoreHTTPStatus400 ,
191+ })
192+ }
193+
194+ // Get next page token for API v3
195+ func getNextPageCustomDataForV3 (_ * api.RequestData , prevPageResponse * http.Response ) (interface {}, errors.Error ) {
196+ var response struct {
197+ NextPageToken string `json:"nextPageToken"`
198+ }
199+
200+ blob , err := io .ReadAll (prevPageResponse .Body )
123201 if err != nil {
124- return err
202+ return nil , errors . Convert ( err )
125203 }
126- return apiCollector .Execute ()
204+
205+ prevPageResponse .Body = io .NopCloser (strings .NewReader (string (blob )))
206+
207+ err = json .Unmarshal (blob , & response )
208+ if err != nil {
209+ return nil , errors .Convert (err )
210+ }
211+
212+ if response .NextPageToken == "" {
213+ return nil , api .ErrFinishCollect
214+ }
215+
216+ return response .NextPageToken , nil
127217}
128218
129219func GetEpicKeysIterator (db dal.Dal , data * JiraTaskData , batchSize int ) (api.Iterator , errors.Error ) {
0 commit comments