Skip to content

Commit cb8f8d5

Browse files
style: ruff format (auto)
1 parent 1d2d788 commit cb8f8d5

9 files changed

Lines changed: 247 additions & 213 deletions

File tree

src/connectors/base.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,11 @@ def __init__(self, config: Dict[str, Any]):
5858
def get_client_id(self) -> str:
5959
"""Get the OAuth client ID from environment variable"""
6060
if not self.CLIENT_ID_ENV_VAR:
61-
raise NotImplementedError(
62-
f"{self.__class__.__name__} must define CLIENT_ID_ENV_VAR"
63-
)
61+
raise NotImplementedError(f"{self.__class__.__name__} must define CLIENT_ID_ENV_VAR")
6462

6563
client_id = os.getenv(self.CLIENT_ID_ENV_VAR)
6664
if not client_id:
67-
raise ValueError(
68-
f"Environment variable {self.CLIENT_ID_ENV_VAR} is not set"
69-
)
65+
raise ValueError(f"Environment variable {self.CLIENT_ID_ENV_VAR} is not set")
7066

7167
return client_id
7268

@@ -79,9 +75,7 @@ def get_client_secret(self) -> str:
7975

8076
secret = os.getenv(self.CLIENT_SECRET_ENV_VAR)
8177
if not secret:
82-
raise ValueError(
83-
f"Environment variable {self.CLIENT_SECRET_ENV_VAR} is not set"
84-
)
78+
raise ValueError(f"Environment variable {self.CLIENT_SECRET_ENV_VAR} is not set")
8579

8680
return secret
8781

@@ -96,7 +90,9 @@ async def setup_subscription(self) -> str:
9690
pass
9791

9892
@abstractmethod
99-
async def list_files(self, page_token: Optional[str] = None, max_files: Optional[int] = None) -> Dict[str, Any]:
93+
async def list_files(
94+
self, page_token: Optional[str] = None, max_files: Optional[int] = None
95+
) -> Dict[str, Any]:
10096
"""List all files. Returns files and next_page_token if any."""
10197
pass
10298

@@ -138,7 +134,7 @@ def is_authenticated(self) -> bool:
138134

139135
async def _detect_base_url(self) -> Optional[str]:
140136
"""Auto-detect base URL for the connector.
141-
137+
142138
Default implementation returns None.
143139
Subclasses (OneDrive, SharePoint) should override this method.
144140
"""

src/connectors/google_drive/connector.py

Lines changed: 39 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
logger = get_logger(__name__)
2323

24+
2425
# -------------------------
2526
# Config model
2627
# -------------------------
@@ -105,6 +106,7 @@ def __init__(self, config: Dict[str, Any]) -> None:
105106

106107
# Token file default - use data directory for persistence
107108
from config.paths import get_data_file
109+
108110
token_file = config.get("token_file") or get_data_file("google_drive_token.json")
109111
Path(token_file).parent.mkdir(parents=True, exist_ok=True)
110112

@@ -120,9 +122,7 @@ def __init__(self, config: Dict[str, Any]) -> None:
120122
)
121123

122124
# Normalize incoming IDs from any of the supported alias keys
123-
def _first_present_list(
124-
cfg: Dict[str, Any], keys: Iterable[str]
125-
) -> Optional[List[str]]:
125+
def _first_present_list(cfg: Dict[str, Any], keys: Iterable[str]) -> Optional[List[str]]:
126126
for k in keys:
127127
v = cfg.get(k)
128128
if v: # accept non-empty list
@@ -362,11 +362,16 @@ def _iter_selected_items(self) -> List[Dict[str, Any]]:
362362
folders_to_expand: List[str] = []
363363

364364
if self.cfg.file_ids:
365-
logger.debug("[GoogleDrive] _iter_selected_items: processing %d file_id(s)", len(self.cfg.file_ids))
365+
logger.debug(
366+
"[GoogleDrive] _iter_selected_items: processing %d file_id(s)",
367+
len(self.cfg.file_ids),
368+
)
366369
for fid in self.cfg.file_ids:
367370
meta = self._get_file_meta_by_id(fid)
368371
if not meta:
369-
logger.debug("[GoogleDrive] _iter_selected_items: no metadata for file_id=%s", fid)
372+
logger.debug(
373+
"[GoogleDrive] _iter_selected_items: no metadata for file_id=%s", fid
374+
)
370375
continue
371376

372377
if meta.get("mimeType") == "application/vnd.google-apps.folder":
@@ -384,7 +389,9 @@ def _iter_selected_items(self) -> List[Dict[str, Any]]:
384389
folders_to_expand.extend(self.cfg.folder_ids)
385390

386391
if folders_to_expand:
387-
logger.debug("[GoogleDrive] _iter_selected_items: expanding %d folder(s)", len(folders_to_expand))
392+
logger.debug(
393+
"[GoogleDrive] _iter_selected_items: expanding %d folder(s)", len(folders_to_expand)
394+
)
388395
folder_children = self._bfs_expand_folders(folders_to_expand)
389396
for meta in folder_children:
390397
meta = self._resolve_shortcut(meta)
@@ -400,11 +407,7 @@ def _iter_selected_items(self) -> List[Dict[str, Any]]:
400407
return []
401408

402409
items = self._filter_by_mime(items)
403-
items = [
404-
m
405-
for m in items
406-
if m.get("mimeType") != "application/vnd.google-apps.folder"
407-
]
410+
items = [m for m in items if m.get("mimeType") != "application/vnd.google-apps.folder"]
408411

409412
if not items and (self.cfg.file_ids or self.cfg.folder_ids):
410413
logger.warning(
@@ -497,9 +500,7 @@ def _download_file_bytes(self, file_meta: Dict[str, Any]) -> bytes:
497500
export_mime,
498501
)
499502
# NOTE: export_media does not accept supportsAllDrives/includeItemsFromAllDrives
500-
request = self.service.files().export_media(
501-
fileId=file_id, mimeType=export_mime
502-
)
503+
request = self.service.files().export_media(fileId=file_id, mimeType=export_mime)
503504
else:
504505
# This is a regular uploaded file (PDF, image, video, etc.) - use get_media
505506
# Also handles non-exportable Google Apps files (Forms, Sites, Maps, etc.)
@@ -525,9 +526,7 @@ def _download_file_bytes(self, file_meta: Dict[str, Any]) -> bytes:
525526
f"Retrying with export_media (file might be a Google Doc)"
526527
)
527528
export_mime = "application/pdf"
528-
request = self.service.files().export_media(
529-
fileId=file_id, mimeType=export_mime
530-
)
529+
request = self.service.files().export_media(fileId=file_id, mimeType=export_mime)
531530
fh = io.BytesIO()
532531
downloader = MediaIoBaseDownload(fh, request, chunksize=1024 * 1024)
533532
done = False
@@ -601,7 +600,9 @@ async def list_files(
601600
"Google Drive service is not initialized. Please authenticate first."
602601
)
603602

604-
logger.debug("[GoogleDrive] list_files: entry (page_token=%s, max_files=%s)", page_token, max_files)
603+
logger.debug(
604+
"[GoogleDrive] list_files: entry (page_token=%s, max_files=%s)", page_token, max_files
605+
)
605606

606607
try:
607608
items = await asyncio.to_thread(self._iter_selected_items)
@@ -636,10 +637,14 @@ def _extract_google_drive_acl(self, file_meta: Dict) -> DocumentACL:
636637
"""
637638
try:
638639
# Fetch permissions (requires additional API call)
639-
permissions_list = self.service.permissions().list(
640-
fileId=file_meta["id"],
641-
fields="permissions(emailAddress,role,type,deleted,displayName)"
642-
).execute()
640+
permissions_list = (
641+
self.service.permissions()
642+
.list(
643+
fileId=file_meta["id"],
644+
fields="permissions(emailAddress,role,type,deleted,displayName)",
645+
)
646+
.execute()
647+
)
643648

644649
allowed_users = []
645650
allowed_groups = []
@@ -744,12 +749,12 @@ def parse_datetime(dt_str):
744749
metadata={
745750
"parents": meta.get("parents"),
746751
"driveId": meta.get("driveId"),
747-
"size": int(meta.get("size", 0))
748-
if str(meta.get("size", "")).isdigit()
749-
else None,
752+
"size": int(meta.get("size", 0)) if str(meta.get("size", "")).isdigit() else None,
750753
},
751754
)
752-
logger.debug("[GoogleDrive] get_file_content: done for file_id=%s (%d bytes)", file_id, len(blob))
755+
logger.debug(
756+
"[GoogleDrive] get_file_content: done for file_id=%s (%d bytes)", file_id, len(blob)
757+
)
753758
return doc
754759

755760
async def setup_subscription(self) -> str:
@@ -766,9 +771,7 @@ async def setup_subscription(self) -> str:
766771
# 1) Ensure we are authenticated and have a live Drive service
767772
ok = await self.authenticate()
768773
if not ok:
769-
raise RuntimeError(
770-
"GoogleDriveConnector.setup_subscription: not authenticated"
771-
)
774+
raise RuntimeError("GoogleDriveConnector.setup_subscription: not authenticated")
772775

773776
# 2) Resolve webhook address (no param in ABC, so pull from config/env)
774777
webhook_address = getattr(self.cfg, "webhook_address", None) or os.getenv(
@@ -824,9 +827,7 @@ async def setup_subscription(self) -> str:
824827
}
825828

826829
if not isinstance(channel_id, str) or not channel_id:
827-
raise RuntimeError(
828-
f"Drive watch returned invalid channel id: {channel_id!r}"
829-
)
830+
raise RuntimeError(f"Drive watch returned invalid channel id: {channel_id!r}")
830831

831832
return channel_id
832833

@@ -902,9 +903,7 @@ async def cleanup_subscription(self, subscription_id: str) -> bool:
902903
):
903904
self._active_channel = {}
904905

905-
if hasattr(self, "_subscriptions") and isinstance(
906-
self._subscriptions, dict
907-
):
906+
if hasattr(self, "_subscriptions") and isinstance(self._subscriptions, dict):
908907
self._subscriptions.pop(subscription_id, None)
909908

910909
return True
@@ -955,9 +954,7 @@ async def handle_webhook(self, payload: Dict[str, Any]) -> List[str]:
955954
except Exception as e:
956955
selected_ids = set()
957956
try:
958-
logger.error(
959-
f"handle_webhook: scope build failed, proceeding unfiltered: {e}"
960-
)
957+
logger.error(f"handle_webhook: scope build failed, proceeding unfiltered: {e}")
961958
except Exception:
962959
pass
963960

@@ -994,11 +991,7 @@ async def handle_webhook(self, payload: Dict[str, Any]) -> List[str]:
994991
# Filter to our selected scope if we have one; otherwise accept all
995992
if selected_ids and (rid not in selected_ids):
996993
# Shortcut target might be in scope even if the shortcut isn't
997-
tgt = (
998-
fobj.get("shortcutDetails", {}).get("targetId")
999-
if fobj
1000-
else None
1001-
)
994+
tgt = fobj.get("shortcutDetails", {}).get("targetId") if fobj else None
1002995
if not (tgt and tgt in selected_ids):
1003996
continue
1004997

@@ -1047,9 +1040,7 @@ def sync_once(self) -> None:
10471040
blob = self._download_file_bytes(meta)
10481041
except HttpError as e:
10491042
# Skip/record failures
1050-
logger.error(
1051-
f"Failed to download {meta.get('name')} ({meta.get('id')}): {e}"
1052-
)
1043+
logger.error(f"Failed to download {meta.get('name')} ({meta.get('id')}): {e}")
10531044
continue
10541045

10551046
from datetime import datetime
@@ -1095,9 +1086,7 @@ def parse_datetime(dt_str):
10951086
# -------------------------
10961087
def get_start_page_token(self) -> str:
10971088
# getStartPageToken accepts supportsAllDrives (not includeItemsFromAllDrives)
1098-
resp = (
1099-
self.service.changes().getStartPageToken(**self._drives_get_flags).execute()
1100-
)
1089+
resp = self.service.changes().getStartPageToken(**self._drives_get_flags).execute()
11011090
return resp["startPageToken"]
11021091

11031092
def poll_changes_and_sync(self) -> Optional[str]:
@@ -1136,10 +1125,7 @@ def poll_changes_and_sync(self) -> Optional[str]:
11361125
# Match scope
11371126
if fid not in selected_ids:
11381127
# also consider shortcut target
1139-
if (
1140-
file_obj.get("mimeType")
1141-
== "application/vnd.google-apps.shortcut"
1142-
):
1128+
if file_obj.get("mimeType") == "application/vnd.google-apps.shortcut":
11431129
tgt = file_obj.get("shortcutDetails", {}).get("targetId")
11441130
if tgt and tgt in selected_ids:
11451131
pass

src/connectors/google_drive/oauth.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ async def load_credentials(self) -> Optional[Credentials]:
6666
os.remove(self.token_file)
6767
return None
6868

69-
logger.debug("[GoogleDrive] load_credentials: token data loaded, creating Credentials object")
69+
logger.debug(
70+
"[GoogleDrive] load_credentials: token data loaded, creating Credentials object"
71+
)
7072

7173
self.creds = Credentials(
7274
token=token_data.get("token"),
@@ -135,13 +137,12 @@ async def save_credentials(self):
135137
# Add expiry if available
136138
if self.creds.expiry:
137139
token_data["expiry"] = self.creds.expiry.isoformat()
138-
140+
139141
from utils.encryption import write_encrypted_file
142+
140143
await write_encrypted_file(self.token_file, json.dumps(token_data))
141144

142-
def create_authorization_url(
143-
self, redirect_uri: str, state: Optional[str] = None
144-
) -> str:
145+
def create_authorization_url(self, redirect_uri: str, state: Optional[str] = None) -> str:
145146
"""Create authorization URL for OAuth flow"""
146147
# Create flow from client credentials directly
147148
client_config = {
@@ -153,9 +154,7 @@ def create_authorization_url(
153154
}
154155
}
155156

156-
flow = Flow.from_client_config(
157-
client_config, scopes=self.SCOPES, redirect_uri=redirect_uri
158-
)
157+
flow = Flow.from_client_config(client_config, scopes=self.SCOPES, redirect_uri=redirect_uri)
159158

160159
kwargs = {
161160
"access_type": "offline",
@@ -173,9 +172,7 @@ def create_authorization_url(
173172

174173
return auth_url
175174

176-
async def handle_authorization_callback(
177-
self, authorization_code: str, state: str
178-
) -> bool:
175+
async def handle_authorization_callback(self, authorization_code: str, state: str) -> bool:
179176
"""Handle OAuth callback and exchange code for tokens"""
180177
if not hasattr(self, "_flow") or self._flow_state != state:
181178
raise ValueError("Invalid OAuth state")

0 commit comments

Comments
 (0)