@@ -1485,27 +1485,81 @@ def epic_issues(self, epic: str, fields: Union[str, list] = "*all", expand: Opti
14851485 params ["expand" ] = expand
14861486 return self .get (url , params = params )
14871487
1488- def bulk_issue (self , issue_list : list , fields : Union [str , list ] = "*all" ):
1489- """
1490- :param fields:
1491- :param list issue_list:
1492- :return:
1488+ def bulk_issue (
1489+ self ,
1490+ issue_list : list ,
1491+ fields : Union [str , list ] = "*all" ,
1492+ start : int = 0 ,
1493+ limit : Optional [int ] = None ,
1494+ fetch_all : bool = True ,
1495+ ):
1496+ """Return issues matching the supplied issue keys.
1497+
1498+ Jira applies a maximum page size to search results. By default this
1499+ method follows every page and returns all matching issues in the
1500+ response's ``issues`` list. Set ``fetch_all`` to ``False`` to retain
1501+ the single-page REST response.
1502+
1503+ :param issue_list: Issue keys to retrieve.
1504+ :param fields: Fields to return for each issue.
1505+ :param start: First Server/Data Center result offset.
1506+ :param limit: Requested page size; Jira may apply a lower system limit.
1507+ :param fetch_all: Whether to collect all available result pages.
1508+ :return: A tuple of the Jira search response and issue keys reported
1509+ as invalid by Jira.
14931510 """
14941511 jira_issue_regex = re .compile (r"\w+-\d+" )
1495- missing_issues = list ()
1496- matched_issue_keys = list ()
1512+ missing_issues = []
1513+ matched_issue_keys = []
14971514 for key in issue_list :
14981515 if re .match (jira_issue_regex , key ):
14991516 matched_issue_keys .append (key )
1500- jql = f"key in ({ ', ' .join (set (matched_issue_keys ))} )"
1501- query_result = self .jql (jql , fields = fields )
1517+ jql = f"key in ({ ', ' .join (dict .fromkeys (matched_issue_keys ))} )"
1518+ if self .cloud :
1519+ if start :
1520+ raise ValueError ("Jira Cloud does not support offset pagination; use enhanced_jql instead." )
1521+ query_result = self .enhanced_jql (jql , fields = fields , limit = limit )
1522+ else :
1523+ query_result = self .jql (jql , fields = fields , start = start , limit = limit )
1524+
15021525 if query_result and "errorMessages" in list (query_result .keys ()):
15031526 for message in query_result ["errorMessages" ]:
15041527 for key in issue_list :
15051528 if key in message :
15061529 missing_issues .append (key )
1507- issue_list .remove (key )
1508- query_result , missing_issues = self .bulk_issue (issue_list , fields )
1530+ remaining_issues = [key for key in issue_list if key not in missing_issues ]
1531+ if remaining_issues != issue_list :
1532+ query_result , nested_missing_issues = self .bulk_issue (
1533+ remaining_issues , fields , start = start , limit = limit , fetch_all = fetch_all
1534+ )
1535+ missing_issues .extend (nested_missing_issues )
1536+ return query_result , missing_issues
1537+
1538+ if not fetch_all or not query_result :
1539+ return query_result , missing_issues
1540+
1541+ issues = list (query_result .get ("issues" , []))
1542+ if self .cloud :
1543+ next_page_token = query_result .get ("nextPageToken" )
1544+ while not query_result .get ("isLast" , True ) and next_page_token :
1545+ query_result = self .enhanced_jql (jql , fields = fields , nextPageToken = next_page_token , limit = limit )
1546+ issues .extend (query_result .get ("issues" , []))
1547+ next_page_token = query_result .get ("nextPageToken" )
1548+ else :
1549+ next_start = start + len (issues )
1550+ total = query_result .get ("total" )
1551+ while issues and (total is None or next_start < total ):
1552+ page = self .jql (jql , fields = fields , start = next_start , limit = limit )
1553+ if not page :
1554+ break
1555+ page_issues = page .get ("issues" , [])
1556+ if not page_issues :
1557+ break
1558+ issues .extend (page_issues )
1559+ next_start += len (page_issues )
1560+ total = page .get ("total" , total )
1561+
1562+ query_result ["issues" ] = issues
15091563 return query_result , missing_issues
15101564
15111565 def issue_createmeta (self , project : str , expand : str = "projects.issuetypes.fields" ) -> T_resp_json :
0 commit comments