@@ -221,8 +221,26 @@ def _parse_datetime(self, value):
221221 except ValueError :
222222 raise SyncError (f"The date { value } could not be parsed." )
223223
224+ def _get_issue_count (self , instance , jql , headers , kwargs ):
225+ """Get approximate issue count for progress tracking."""
226+
227+ url = f"{ instance .jira_url } /rest/api/2/search/approximate-count"
228+ try :
229+ response = advocate .post (
230+ url ,
231+ headers = {** headers , "Content-Type" : "application/json" },
232+ json = {"jql" : jql },
233+ timeout = 10 ,
234+ ** kwargs ,
235+ )
236+ if response .ok :
237+ return response .json ().get ("count" , 0 )
238+ except (RequestException , UnacceptableAddressException , ConnectionError ):
239+ pass
240+ return 0
241+
224242 def _fetch_issues (self , instance , progress_builder : ChildProgressBuilder ):
225- headers = {"Content-Type " : "application/json" }
243+ headers = {"Accept " : "application/json" }
226244 kwargs = {}
227245
228246 if instance .jira_authentication == JIRA_ISSUES_DATA_SYNC_PERSONAL_ACCESS_TOKEN :
@@ -234,21 +252,35 @@ def _fetch_issues(self, instance, progress_builder: ChildProgressBuilder):
234252 )
235253
236254 issues = []
237- start_at = 0
238255 max_results = 50
239- progress = None
256+ next_page_token = None
257+
258+ if instance .jira_project_key :
259+ jql = f"project={ instance .jira_project_key } ORDER BY created DESC"
260+ else :
261+ jql = "created IS NOT EMPTY ORDER BY created DESC"
262+
263+ issue_count = self ._get_issue_count (instance , jql , headers , kwargs )
264+ page_count = math .ceil (issue_count / max_results ) if issue_count > 0 else 0
265+ progress = ChildProgressBuilder .build (
266+ progress_builder , child_total = page_count + 1
267+ )
268+ progress .increment (by = 1 )
269+
240270 try :
241271 while True :
242- url = (
243- f"{ instance .jira_url } "
244- + f"/rest/api/2/search"
245- + f"?startAt={ start_at } "
246- + f"&maxResults={ max_results } "
272+ url = f"{ instance .jira_url } /rest/api/2/search/jql"
273+ params = {
274+ "jql" : jql ,
275+ "maxResults" : max_results ,
276+ "fields" : "*all" ,
277+ }
278+ if next_page_token :
279+ params ["nextPageToken" ] = next_page_token
280+
281+ response = advocate .get (
282+ url , headers = headers , params = params , timeout = 10 , ** kwargs
247283 )
248- if instance .jira_project_key :
249- url += f"&jql=project={ instance .jira_project_key } "
250-
251- response = advocate .get (url , headers = headers , timeout = 10 , ** kwargs )
252284 if not response .ok :
253285 try :
254286 json = response .json ()
@@ -262,25 +294,18 @@ def _fetch_issues(self, instance, progress_builder: ChildProgressBuilder):
262294
263295 data = response .json ()
264296
265- # The response of any request gives us the total, allowing us to
266- # properly construct a progress bar.
267- if data ["total" ] and progress is None :
268- progress = ChildProgressBuilder .build (
269- progress_builder ,
270- child_total = math .ceil (data ["total" ] / max_results ),
271- )
272- if progress :
273- progress .increment (by = 1 )
297+ progress .increment (by = 1 )
274298
275- if len (data ["issues" ]) == 0 and start_at == 0 :
299+ if len (data ["issues" ]) == 0 and next_page_token is None :
276300 raise SyncError (
277301 "No issues found. This is usually because the authentication "
278302 "details are wrong."
279303 )
280304
281305 issues .extend (data ["issues" ])
282- start_at += max_results
283- if data ["total" ] <= start_at :
306+
307+ next_page_token = data .get ("nextPageToken" )
308+ if not next_page_token :
284309 break
285310 except (RequestException , UnacceptableAddressException , ConnectionError ) as e :
286311 raise SyncError (f"Error connecting to Jira: { str (e )} " )
0 commit comments