diff --git a/enterprise/integrations/github/data_collector.py b/enterprise/integrations/github/data_collector.py index d0844b814e..6d24399ddb 100644 --- a/enterprise/integrations/github/data_collector.py +++ b/enterprise/integrations/github/data_collector.py @@ -116,10 +116,8 @@ class GitHubDataCollector: return suffix - def _get_installation_access_token(self, installation_id: str) -> str: - token_data = self.github_integration.get_access_token( - installation_id # type: ignore[arg-type] - ) + def _get_installation_access_token(self, installation_id: int) -> str: + token_data = self.github_integration.get_access_token(installation_id) return token_data.token def _check_openhands_author(self, name, login) -> bool: @@ -134,7 +132,7 @@ class GitHubDataCollector: ) def _get_issue_comments( - self, installation_id: str, repo_name: str, issue_number: int, conversation_id + self, installation_id: int, repo_name: str, issue_number: int, conversation_id ) -> list[dict[str, Any]]: """ Retrieve all comments from an issue until a comment with conversation_id is found @@ -234,7 +232,7 @@ class GitHubDataCollector: f'[Github]: Saved issue #{issue_number} for {github_view.full_repo_name}' ) - def _get_pr_commits(self, installation_id: str, repo_name: str, pr_number: int): + def _get_pr_commits(self, installation_id: int, repo_name: str, pr_number: int): commits = [] installation_token = self._get_installation_access_token(installation_id) with Github(auth=Auth.Token(installation_token)) as github_client: @@ -431,7 +429,7 @@ class GitHubDataCollector: - Num openhands review comments """ pr_number = openhands_pr.pr_number - installation_id = openhands_pr.installation_id + installation_id = int(openhands_pr.installation_id) repo_id = openhands_pr.repo_id # Get installation token and create Github client diff --git a/enterprise/integrations/github/github_service.py b/enterprise/integrations/github/github_service.py index 4ea9e41626..6d1fb929a4 100644 --- a/enterprise/integrations/github/github_service.py +++ b/enterprise/integrations/github/github_service.py @@ -122,13 +122,37 @@ class SaaSGitHubService(GitHubService): raise Exception(f'No node_id found for repository {repo_id}') return node_id + async def _get_external_auth_id(self) -> str | None: + """Get or fetch external_auth_id from Keycloak token if not already set.""" + if self.external_auth_id: + return self.external_auth_id + + if self.external_auth_token: + try: + user_info = await self.token_manager.get_user_info( + self.external_auth_token.get_secret_value() + ) + self.external_auth_id = user_info.sub + logger.info( + f'Determined external_auth_id from Keycloak token: {self.external_auth_id}' + ) + return self.external_auth_id + except Exception as e: + logger.warning( + f'Could not determine external_auth_id from token: {e}', + exc_info=True, + ) + return None + async def get_paginated_repos(self, page, per_page, sort, installation_id): repositories = await super().get_paginated_repos( page, per_page, sort, installation_id ) - asyncio.create_task( - store_repositories_in_db(repositories, self.external_auth_id) - ) + external_auth_id = await self._get_external_auth_id() + if external_auth_id: + asyncio.create_task( + store_repositories_in_db(repositories, external_auth_id) + ) return repositories async def get_all_repositories( @@ -136,8 +160,10 @@ class SaaSGitHubService(GitHubService): ) -> list[Repository]: repositories = await super().get_all_repositories(sort, app_mode) # Schedule the background task without awaiting it - asyncio.create_task( - store_repositories_in_db(repositories, self.external_auth_id) - ) + external_auth_id = await self._get_external_auth_id() + if external_auth_id: + asyncio.create_task( + store_repositories_in_db(repositories, external_auth_id) + ) # Return repositories immediately return repositories diff --git a/enterprise/integrations/github/github_view.py b/enterprise/integrations/github/github_view.py index e8d6e525b3..97ad8a9b98 100644 --- a/enterprise/integrations/github/github_view.py +++ b/enterprise/integrations/github/github_view.py @@ -733,7 +733,7 @@ class GithubFactory: @staticmethod async def create_github_view_from_payload( message: Message, keycloak_user_id: str - ) -> ResolverViewInterface: + ) -> GithubViewType: """Create the appropriate class (GithubIssue or GithubPRComment) based on the payload. Also return metadata about the event (e.g., action type). """