From 856a1d141b12f451299a88ff5230067bed3385fe Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Wed, 19 Nov 2025 11:04:09 -0800 Subject: [PATCH 1/3] fix: Improve the Google Drive connector --- src/connectors/google_drive/connector.py | 53 +++++++++++++++++++++--- src/connectors/google_drive/oauth.py | 20 ++++++++- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/connectors/google_drive/connector.py b/src/connectors/google_drive/connector.py index 66b67519..afd8b8c2 100644 --- a/src/connectors/google_drive/connector.py +++ b/src/connectors/google_drive/connector.py @@ -166,6 +166,10 @@ class GoogleDriveConnector(BaseConnector): # ------------------------- # Helpers # ------------------------- + def _clear_shortcut_cache(self) -> None: + """Clear the shortcut resolution cache to prevent stale data.""" + self._shortcut_cache.clear() + @property def _drives_get_flags(self) -> Dict[str, Any]: """ @@ -208,6 +212,10 @@ class GoogleDriveConnector(BaseConnector): if target_id in self._shortcut_cache: return self._shortcut_cache[target_id] + if self.service is None: + logger.warning("Cannot resolve shortcut - service not initialized") + return file_obj + try: meta = ( self.service.files() @@ -231,6 +239,11 @@ class GoogleDriveConnector(BaseConnector): """ List immediate children of a folder. """ + if self.service is None: + raise RuntimeError( + "Google Drive service is not initialized. Please authenticate first." + ) + query = f"'{folder_id}' in parents and trashed = false" page_token = None results: List[Dict[str, Any]] = [] @@ -332,6 +345,9 @@ class GoogleDriveConnector(BaseConnector): - items inside folder_ids (with optional recursion) Shortcuts are resolved to their targets automatically. """ + # Clear shortcut cache to ensure fresh data + self._clear_shortcut_cache() + seen: Set[str] = set() items: List[Dict[str, Any]] = [] folders_to_expand: List[str] = [] @@ -374,6 +390,10 @@ class GoogleDriveConnector(BaseConnector): # - OR default to entire drive. # Here we choose to require explicit selection: if not self.cfg.file_ids and not self.cfg.folder_ids: + logger.warning( + "No file_ids or folder_ids specified - returning empty result. " + "Explicit selection is required." + ) return [] items = self._filter_by_mime(items) @@ -383,6 +403,16 @@ class GoogleDriveConnector(BaseConnector): for m in items if m.get("mimeType") != "application/vnd.google-apps.folder" ] + + # Log a warning if we ended up with no files after expansion/filtering + if not items and (self.cfg.file_ids or self.cfg.folder_ids): + logger.warning( + f"No files found after expanding and filtering. " + f"file_ids={self.cfg.file_ids}, folder_ids={self.cfg.folder_ids}. " + f"This could mean: (1) folders are empty, (2) all files were filtered by mime types, " + f"or (3) permissions prevent access to the files." + ) + return items # ------------------------- @@ -416,6 +446,11 @@ class GoogleDriveConnector(BaseConnector): Download bytes for a given file (exporting if Google-native). Raises ValueError if the item is a folder (folders cannot be downloaded). """ + if self.service is None: + raise RuntimeError( + "Google Drive service is not initialized. Please authenticate first." + ) + file_id = file_meta["id"] file_name = file_meta.get("name", "unknown") mime_type = file_meta.get("mimeType") or "" @@ -543,6 +578,12 @@ class GoogleDriveConnector(BaseConnector): - If page_token is None: return all files in one batch. - Otherwise: return {} and no next_page_token. """ + # Ensure service is initialized + if self.service is None: + raise RuntimeError( + "Google Drive service is not initialized. Please authenticate first." + ) + try: items = self._iter_selected_items() @@ -560,12 +601,12 @@ class GoogleDriveConnector(BaseConnector): "next_page_token": None, # no more pages } except Exception as e: - # Log the error - try: - logger.error(f"GoogleDriveConnector.list_files failed: {e}") - except Exception: - pass - return {"files": [], "next_page_token": None} + # Log the error and re-raise to surface authentication/permission issues + logger.error( + f"GoogleDriveConnector.list_files failed: {e}", + exc_info=True + ) + raise async def get_file_content(self, file_id: str) -> ConnectorDocument: """ diff --git a/src/connectors/google_drive/oauth.py b/src/connectors/google_drive/oauth.py index f23e4796..e9beb0a3 100644 --- a/src/connectors/google_drive/oauth.py +++ b/src/connectors/google_drive/oauth.py @@ -60,8 +60,24 @@ class GoogleDriveOAuth: # If credentials are expired, refresh them if self.creds and self.creds.expired and self.creds.refresh_token: - self.creds.refresh(Request()) - await self.save_credentials() + try: + self.creds.refresh(Request()) + await self.save_credentials() + except Exception as e: + # Refresh failed - likely refresh token expired or revoked + # Clear credentials and raise a clear error + self.creds = None + # Try to clean up the invalid token file + if os.path.exists(self.token_file): + try: + os.remove(self.token_file) + except Exception: + pass + raise ValueError( + f"Failed to refresh Google Drive credentials. " + f"The refresh token may have expired or been revoked. " + f"Please re-authenticate: {str(e)}" + ) from e return self.creds From 1cfa72d20e3c186fa0e662dae1f61ffe3ae5071b Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Wed, 19 Nov 2025 12:25:40 -0800 Subject: [PATCH 2/3] Clean up the errors in connectors --- frontend/app/auth/callback/page.tsx | 10 +-- frontend/app/upload/[provider]/page.tsx | 2 +- frontend/components/icons/one-drive-logo.tsx | 48 ++++++------ .../components/icons/share-point-logo.tsx | 74 +++++++++---------- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/frontend/app/auth/callback/page.tsx b/frontend/app/auth/callback/page.tsx index 83c0cf96..3bc359cb 100644 --- a/frontend/app/auth/callback/page.tsx +++ b/frontend/app/auth/callback/page.tsx @@ -119,9 +119,9 @@ function AuthCallbackContent() { localStorage.removeItem("connecting_connector_type"); localStorage.removeItem("auth_purpose"); - // Redirect to connectors page with success indicator + // Redirect to settings page with success indicator setTimeout(() => { - router.push("/connectors?oauth_success=true"); + router.push("/settings?oauth_success=true"); }, 2000); } } else { @@ -207,13 +207,13 @@ function AuthCallbackContent() { )} @@ -223,7 +223,7 @@ function AuthCallbackContent() {

{isAppAuth ? "Redirecting you to the app..." - : "Redirecting to connectors..."} + : "Redirecting to settings..."}

diff --git a/frontend/app/upload/[provider]/page.tsx b/frontend/app/upload/[provider]/page.tsx index 3074c777..544e3227 100644 --- a/frontend/app/upload/[provider]/page.tsx +++ b/frontend/app/upload/[provider]/page.tsx @@ -366,7 +366,7 @@ export default function UploadProviderPage() { Back - +