@@ -24,23 +24,23 @@ def list_issues(
2424 labels : list [str ] | None = None ,
2525 state : str = "opened" ,
2626 per_page : int = 100 ,
27+ max_pages : int = 200 ,
2728 ) -> list [dict [str , Any ]]:
2829 url = f"{ self .api_url } /projects/{ project_id } /issues"
29- params = {"state" : state , "per_page" : per_page }
30+ params : dict [ str , Any ] = {"state" : state }
3031 if labels :
3132 params ["labels" ] = "," .join (labels )
32- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
33- resp .raise_for_status ()
34- return resp .json ()
33+ return self ._fetch_paginated_list (url , params , per_page , max_pages )
3534
3635 def list_issue_notes (
37- self , project_id : int | str , issue_iid : int | str , per_page : int = 100 ,
36+ self ,
37+ project_id : int | str ,
38+ issue_iid : int | str ,
39+ per_page : int = 100 ,
40+ max_pages : int = 200 ,
3841 ) -> list [dict [str , Any ]]:
3942 url = f"{ self .api_url } /projects/{ project_id } /issues/{ issue_iid } /notes"
40- params = {"per_page" : per_page }
41- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
42- resp .raise_for_status ()
43- return resp .json ()
43+ return self ._fetch_paginated_list (url , {}, per_page , max_pages )
4444
4545 def add_issue_note (
4646 self , project_id : int | str , issue_iid : int | str , body : str ,
@@ -101,25 +101,25 @@ def list_merge_requests(
101101 assignee : str | None = None ,
102102 state : str = "opened" ,
103103 per_page : int = 100 ,
104+ max_pages : int = 200 ,
104105 ) -> list [dict [str , Any ]]:
105106 url = f"{ self .api_url } /projects/{ project_id } /merge_requests"
106- params = {"state" : state , "per_page" : per_page }
107+ params : dict [ str , Any ] = {"state" : state }
107108 if labels :
108109 params ["labels" ] = "," .join (labels )
109110 if assignee :
110111 params ["assignee_username" ] = assignee
111- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
112- resp .raise_for_status ()
113- return resp .json ()
112+ return self ._fetch_paginated_list (url , params , per_page , max_pages )
114113
115114 def list_merge_request_notes (
116- self , project_id : int | str , merge_request_iid : int | str , per_page : int = 100 ,
115+ self ,
116+ project_id : int | str ,
117+ merge_request_iid : int | str ,
118+ per_page : int = 100 ,
119+ max_pages : int = 200 ,
117120 ) -> list [dict [str , Any ]]:
118121 url = f"{ self .api_url } /projects/{ project_id } /merge_requests/{ merge_request_iid } /notes"
119- params = {"per_page" : per_page }
120- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
121- resp .raise_for_status ()
122- return resp .json ()
122+ return self ._fetch_paginated_list (url , {}, per_page , max_pages )
123123
124124 def add_merge_request_note (
125125 self , project_id : int | str , merge_request_iid : int | str , body : str ,
@@ -168,21 +168,79 @@ def get_merge_request(
168168 return resp .json ()
169169
170170 def search_issues (
171- self , query : str , state : str = "opened" , per_page : int = 200 ,
171+ self ,
172+ query : str ,
173+ state : str = "opened" ,
174+ per_page : int = 200 ,
175+ max_pages : int = 200 ,
172176 ) -> list [dict [str , Any ]]:
173177 url = f"{ self .api_url } /search"
174- params = {"scope" : "issues" , "search" : query , "state" : state , "per_page" : per_page }
175- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
176- resp .raise_for_status ()
177- return resp .json ()
178+ params : dict [str , Any ] = {"scope" : "issues" , "search" : query , "state" : state }
179+ return self ._fetch_paginated_list (url , params , per_page , max_pages )
178180
179181 def search_merge_requests (
180- self , query : str , state : str | None = None , per_page : int = 200 ,
182+ self ,
183+ query : str ,
184+ state : str | None = None ,
185+ per_page : int = 200 ,
186+ max_pages : int = 200 ,
181187 ) -> list [dict [str , Any ]]:
182188 url = f"{ self .api_url } /search"
183- params = {"scope" : "merge_requests" , "search" : query , "per_page" : per_page }
189+ params : dict [ str , Any ] = {"scope" : "merge_requests" , "search" : query }
184190 if state :
185191 params ["state" ] = state
186- resp = requests .get (url , headers = self .headers , params = params , timeout = 30 )
187- resp .raise_for_status ()
188- return resp .json ()
192+ return self ._fetch_paginated_list (url , params , per_page , max_pages )
193+
194+ def _fetch_paginated_list (
195+ self ,
196+ url : str ,
197+ params : dict [str , Any ],
198+ per_page : int ,
199+ max_pages : int ,
200+ ) -> list [dict [str , Any ]]:
201+ """GitLab APIからページング結果を全件取得するヘルパー."""
202+ items : list [dict [str , Any ]] = []
203+ page : int = 1
204+ visited_pages : set [int ] = set ()
205+
206+ # X-Next-Pageヘッダーとレスポンス件数を使って次ページを辿る
207+ while page not in visited_pages and page <= max_pages :
208+ visited_pages .add (page )
209+ page_params = dict (params )
210+ page_params ["per_page" ] = per_page
211+ page_params ["page" ] = page
212+
213+ resp = requests .get (url , headers = self .headers , params = page_params , timeout = 30 )
214+ resp .raise_for_status ()
215+ payload = resp .json ()
216+
217+ page_items : list [dict [str , Any ]]
218+ if isinstance (payload , list ):
219+ page_items = payload
220+ elif isinstance (payload , dict ) and isinstance (payload .get ("items" ), list ):
221+ page_items = payload ["items" ]
222+ else :
223+ break
224+
225+ if not page_items :
226+ break
227+
228+ items .extend (page_items )
229+
230+ next_page_header = resp .headers .get ("X-Next-Page" )
231+ if next_page_header :
232+ try :
233+ next_page = int (next_page_header )
234+ except ValueError :
235+ break
236+ if next_page <= 0 :
237+ break
238+ page = next_page
239+ continue
240+
241+ if len (page_items ) < per_page :
242+ break
243+
244+ page += 1
245+
246+ return items
0 commit comments