|
16 | 16 | UnknownException,
|
17 | 17 | User,
|
18 | 18 | )
|
| 19 | +from openhands.server.types import AppMode |
19 | 20 | from openhands.utils.import_utils import get_impl
|
20 | 21 |
|
21 | 22 |
|
@@ -99,40 +100,80 @@ async def get_user(self) -> User:
|
99 | 100 | email=response.get('email'),
|
100 | 101 | )
|
101 | 102 |
|
102 |
| - async def get_repositories( |
103 |
| - self, sort: str, installation_id: int | None |
104 |
| - ) -> list[Repository]: |
105 |
| - MAX_REPOS = 1000 |
106 |
| - PER_PAGE = 100 # Maximum allowed by GitHub API |
107 |
| - all_repos: list[dict] = [] |
| 103 | + |
| 104 | + async def _fetch_paginated_repos( |
| 105 | + self, url: str, params: dict, max_repos: int, extract_key: str | None = None |
| 106 | + ) -> list[dict]: |
| 107 | + """ |
| 108 | + Fetch repositories with pagination support. |
| 109 | +
|
| 110 | + Args: |
| 111 | + url: The API endpoint URL |
| 112 | + params: Query parameters for the request |
| 113 | + max_repos: Maximum number of repositories to fetch |
| 114 | + extract_key: If provided, extract repositories from this key in the response |
| 115 | +
|
| 116 | + Returns: |
| 117 | + List of repository dictionaries |
| 118 | + """ |
| 119 | + repos: list[dict] = [] |
108 | 120 | page = 1
|
109 | 121 |
|
110 |
| - while len(all_repos) < MAX_REPOS: |
111 |
| - params = {'page': str(page), 'per_page': str(PER_PAGE)} |
112 |
| - if installation_id: |
113 |
| - url = ( |
114 |
| - f'{self.BASE_URL}/user/installations/{installation_id}/repositories' |
115 |
| - ) |
116 |
| - response, headers = await self._fetch_data(url, params) |
117 |
| - response = response.get('repositories', []) |
118 |
| - else: |
119 |
| - url = f'{self.BASE_URL}/user/repos' |
120 |
| - params['sort'] = sort |
121 |
| - response, headers = await self._fetch_data(url, params) |
122 |
| - |
123 |
| - if not response: # No more repositories |
| 122 | + while len(repos) < max_repos: |
| 123 | + page_params = {**params, 'page': str(page)} |
| 124 | + response, headers = await self._fetch_data(url, page_params) |
| 125 | + |
| 126 | + # Extract repositories from response |
| 127 | + page_repos = response.get(extract_key, []) if extract_key else response |
| 128 | + |
| 129 | + if not page_repos: # No more repositories |
124 | 130 | break
|
125 | 131 |
|
126 |
| - all_repos.extend(response) |
| 132 | + repos.extend(page_repos) |
127 | 133 | page += 1
|
128 | 134 |
|
129 | 135 | # Check if we've reached the last page
|
130 | 136 | link_header = headers.get('Link', '')
|
131 | 137 | if 'rel="next"' not in link_header:
|
132 | 138 | break
|
133 | 139 |
|
134 |
| - # Trim to MAX_REPOS if needed and convert to Repository objects |
135 |
| - all_repos = all_repos[:MAX_REPOS] |
| 140 | + return repos[:max_repos] # Trim to max_repos if needed |
| 141 | + |
| 142 | + async def get_repositories(self, sort: str, app_mode: AppMode) -> list[Repository]: |
| 143 | + MAX_REPOS = 1000 |
| 144 | + PER_PAGE = 100 # Maximum allowed by GitHub API |
| 145 | + all_repos: list[dict] = [] |
| 146 | + |
| 147 | + if app_mode == AppMode.SAAS: |
| 148 | + # Get all installation IDs and fetch repos for each one |
| 149 | + installation_ids = await self.get_installation_ids() |
| 150 | + |
| 151 | + # Iterate through each installation ID |
| 152 | + for installation_id in installation_ids: |
| 153 | + params = {'per_page': str(PER_PAGE)} |
| 154 | + url = ( |
| 155 | + f'{self.BASE_URL}/user/installations/{installation_id}/repositories' |
| 156 | + ) |
| 157 | + |
| 158 | + # Fetch repositories for this installation |
| 159 | + installation_repos = await self._fetch_paginated_repos( |
| 160 | + url, params, MAX_REPOS - len(all_repos), extract_key='repositories' |
| 161 | + ) |
| 162 | + |
| 163 | + all_repos.extend(installation_repos) |
| 164 | + |
| 165 | + # If we've already reached MAX_REPOS, no need to check other installations |
| 166 | + if len(all_repos) >= MAX_REPOS: |
| 167 | + break |
| 168 | + else: |
| 169 | + # Original behavior for non-SaaS mode |
| 170 | + params = {'per_page': str(PER_PAGE), 'sort': sort} |
| 171 | + url = f'{self.BASE_URL}/user/repos' |
| 172 | + |
| 173 | + # Fetch user repositories |
| 174 | + all_repos = await self._fetch_paginated_repos(url, params, MAX_REPOS) |
| 175 | + |
| 176 | + # Convert to Repository objects |
136 | 177 | return [
|
137 | 178 | Repository(
|
138 | 179 | id=repo.get('id'),
|
|
0 commit comments