airbyte-agent-google-drive 0.1.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. airbyte_agent_google_drive/__init__.py +151 -0
  2. airbyte_agent_google_drive/_vendored/__init__.py +1 -0
  3. airbyte_agent_google_drive/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_google_drive/_vendored/connector_sdk/auth_strategies.py +1120 -0
  5. airbyte_agent_google_drive/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_google_drive/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_google_drive/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_google_drive/_vendored/connector_sdk/connector_model_loader.py +965 -0
  9. airbyte_agent_google_drive/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_google_drive/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_google_drive/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_google_drive/_vendored/connector_sdk/executor/hosted_executor.py +196 -0
  13. airbyte_agent_google_drive/_vendored/connector_sdk/executor/local_executor.py +1633 -0
  14. airbyte_agent_google_drive/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_google_drive/_vendored/connector_sdk/extensions.py +693 -0
  16. airbyte_agent_google_drive/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_google_drive/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_google_drive/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_google_drive/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_google_drive/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_google_drive/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_google_drive/_vendored/connector_sdk/http/response.py +104 -0
  23. airbyte_agent_google_drive/_vendored/connector_sdk/http_client.py +686 -0
  24. airbyte_agent_google_drive/_vendored/connector_sdk/introspection.py +262 -0
  25. airbyte_agent_google_drive/_vendored/connector_sdk/logging/__init__.py +11 -0
  26. airbyte_agent_google_drive/_vendored/connector_sdk/logging/logger.py +264 -0
  27. airbyte_agent_google_drive/_vendored/connector_sdk/logging/types.py +92 -0
  28. airbyte_agent_google_drive/_vendored/connector_sdk/observability/__init__.py +11 -0
  29. airbyte_agent_google_drive/_vendored/connector_sdk/observability/config.py +179 -0
  30. airbyte_agent_google_drive/_vendored/connector_sdk/observability/models.py +19 -0
  31. airbyte_agent_google_drive/_vendored/connector_sdk/observability/redactor.py +81 -0
  32. airbyte_agent_google_drive/_vendored/connector_sdk/observability/session.py +103 -0
  33. airbyte_agent_google_drive/_vendored/connector_sdk/performance/__init__.py +6 -0
  34. airbyte_agent_google_drive/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  35. airbyte_agent_google_drive/_vendored/connector_sdk/performance/metrics.py +93 -0
  36. airbyte_agent_google_drive/_vendored/connector_sdk/schema/__init__.py +75 -0
  37. airbyte_agent_google_drive/_vendored/connector_sdk/schema/base.py +164 -0
  38. airbyte_agent_google_drive/_vendored/connector_sdk/schema/components.py +239 -0
  39. airbyte_agent_google_drive/_vendored/connector_sdk/schema/connector.py +120 -0
  40. airbyte_agent_google_drive/_vendored/connector_sdk/schema/extensions.py +230 -0
  41. airbyte_agent_google_drive/_vendored/connector_sdk/schema/operations.py +146 -0
  42. airbyte_agent_google_drive/_vendored/connector_sdk/schema/security.py +223 -0
  43. airbyte_agent_google_drive/_vendored/connector_sdk/secrets.py +182 -0
  44. airbyte_agent_google_drive/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  45. airbyte_agent_google_drive/_vendored/connector_sdk/telemetry/config.py +32 -0
  46. airbyte_agent_google_drive/_vendored/connector_sdk/telemetry/events.py +59 -0
  47. airbyte_agent_google_drive/_vendored/connector_sdk/telemetry/tracker.py +155 -0
  48. airbyte_agent_google_drive/_vendored/connector_sdk/types.py +245 -0
  49. airbyte_agent_google_drive/_vendored/connector_sdk/utils.py +60 -0
  50. airbyte_agent_google_drive/_vendored/connector_sdk/validation.py +822 -0
  51. airbyte_agent_google_drive/connector.py +1318 -0
  52. airbyte_agent_google_drive/connector_model.py +4966 -0
  53. airbyte_agent_google_drive/models.py +579 -0
  54. airbyte_agent_google_drive/types.py +141 -0
  55. airbyte_agent_google_drive-0.1.15.dist-info/METADATA +123 -0
  56. airbyte_agent_google_drive-0.1.15.dist-info/RECORD +57 -0
  57. airbyte_agent_google_drive-0.1.15.dist-info/WHEEL +4 -0
@@ -0,0 +1,1318 @@
1
+ """
2
+ Google-Drive connector.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar, AsyncIterator, overload
9
+ try:
10
+ from typing import Literal
11
+ except ImportError:
12
+ from typing_extensions import Literal
13
+
14
+ from .connector_model import GoogleDriveConnectorModel
15
+ from ._vendored.connector_sdk.introspection import describe_entities, generate_tool_description
16
+ from .types import (
17
+ AboutGetParams,
18
+ ChangesListParams,
19
+ ChangesStartPageTokenGetParams,
20
+ CommentsGetParams,
21
+ CommentsListParams,
22
+ DrivesGetParams,
23
+ DrivesListParams,
24
+ FilesDownloadParams,
25
+ FilesExportDownloadParams,
26
+ FilesGetParams,
27
+ FilesListParams,
28
+ PermissionsGetParams,
29
+ PermissionsListParams,
30
+ RepliesGetParams,
31
+ RepliesListParams,
32
+ RevisionsGetParams,
33
+ RevisionsListParams,
34
+ )
35
+ if TYPE_CHECKING:
36
+ from .models import GoogleDriveAuthConfig
37
+ # Import response models and envelope models at runtime
38
+ from .models import (
39
+ GoogleDriveExecuteResult,
40
+ GoogleDriveExecuteResultWithMeta,
41
+ FilesListResult,
42
+ DrivesListResult,
43
+ PermissionsListResult,
44
+ CommentsListResult,
45
+ RepliesListResult,
46
+ RevisionsListResult,
47
+ ChangesListResult,
48
+ About,
49
+ Change,
50
+ Comment,
51
+ Drive,
52
+ File,
53
+ Permission,
54
+ Reply,
55
+ Revision,
56
+ StartPageToken,
57
+ )
58
+
59
+ # TypeVar for decorator type preservation
60
+ _F = TypeVar("_F", bound=Callable[..., Any])
61
+
62
+
63
+ class GoogleDriveConnector:
64
+ """
65
+ Type-safe Google-Drive API connector.
66
+
67
+ Auto-generated from OpenAPI specification with full type safety.
68
+ """
69
+
70
+ connector_name = "google-drive"
71
+ connector_version = "0.1.1"
72
+ vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
73
+
74
+ # Map of (entity, action) -> needs_envelope for envelope wrapping decision
75
+ _ENVELOPE_MAP = {
76
+ ("files", "list"): True,
77
+ ("files", "get"): None,
78
+ ("files", "download"): None,
79
+ ("files_export", "download"): None,
80
+ ("drives", "list"): True,
81
+ ("drives", "get"): None,
82
+ ("permissions", "list"): True,
83
+ ("permissions", "get"): None,
84
+ ("comments", "list"): True,
85
+ ("comments", "get"): None,
86
+ ("replies", "list"): True,
87
+ ("replies", "get"): None,
88
+ ("revisions", "list"): True,
89
+ ("revisions", "get"): None,
90
+ ("changes", "list"): True,
91
+ ("changes_start_page_token", "get"): None,
92
+ ("about", "get"): None,
93
+ }
94
+
95
+ # Map of (entity, action) -> {python_param_name: api_param_name}
96
+ # Used to convert snake_case TypedDict keys to API parameter names in execute()
97
+ _PARAM_MAP = {
98
+ ('files', 'list'): {'page_size': 'pageSize', 'page_token': 'pageToken', 'q': 'q', 'order_by': 'orderBy', 'fields': 'fields', 'spaces': 'spaces', 'corpora': 'corpora', 'drive_id': 'driveId', 'include_items_from_all_drives': 'includeItemsFromAllDrives', 'supports_all_drives': 'supportsAllDrives'},
99
+ ('files', 'get'): {'file_id': 'fileId', 'fields': 'fields', 'supports_all_drives': 'supportsAllDrives'},
100
+ ('files', 'download'): {'file_id': 'fileId', 'alt': 'alt', 'acknowledge_abuse': 'acknowledgeAbuse', 'supports_all_drives': 'supportsAllDrives', 'range_header': 'range_header'},
101
+ ('files_export', 'download'): {'file_id': 'fileId', 'mime_type': 'mimeType', 'range_header': 'range_header'},
102
+ ('drives', 'list'): {'page_size': 'pageSize', 'page_token': 'pageToken', 'q': 'q', 'use_domain_admin_access': 'useDomainAdminAccess'},
103
+ ('drives', 'get'): {'drive_id': 'driveId', 'use_domain_admin_access': 'useDomainAdminAccess'},
104
+ ('permissions', 'list'): {'file_id': 'fileId', 'page_size': 'pageSize', 'page_token': 'pageToken', 'supports_all_drives': 'supportsAllDrives', 'use_domain_admin_access': 'useDomainAdminAccess'},
105
+ ('permissions', 'get'): {'file_id': 'fileId', 'permission_id': 'permissionId', 'supports_all_drives': 'supportsAllDrives', 'use_domain_admin_access': 'useDomainAdminAccess'},
106
+ ('comments', 'list'): {'file_id': 'fileId', 'page_size': 'pageSize', 'page_token': 'pageToken', 'start_modified_time': 'startModifiedTime', 'include_deleted': 'includeDeleted', 'fields': 'fields'},
107
+ ('comments', 'get'): {'file_id': 'fileId', 'comment_id': 'commentId', 'include_deleted': 'includeDeleted', 'fields': 'fields'},
108
+ ('replies', 'list'): {'file_id': 'fileId', 'comment_id': 'commentId', 'page_size': 'pageSize', 'page_token': 'pageToken', 'include_deleted': 'includeDeleted', 'fields': 'fields'},
109
+ ('replies', 'get'): {'file_id': 'fileId', 'comment_id': 'commentId', 'reply_id': 'replyId', 'include_deleted': 'includeDeleted', 'fields': 'fields'},
110
+ ('revisions', 'list'): {'file_id': 'fileId', 'page_size': 'pageSize', 'page_token': 'pageToken'},
111
+ ('revisions', 'get'): {'file_id': 'fileId', 'revision_id': 'revisionId'},
112
+ ('changes', 'list'): {'page_token': 'pageToken', 'page_size': 'pageSize', 'drive_id': 'driveId', 'include_items_from_all_drives': 'includeItemsFromAllDrives', 'supports_all_drives': 'supportsAllDrives', 'spaces': 'spaces', 'include_removed': 'includeRemoved', 'restrict_to_my_drive': 'restrictToMyDrive'},
113
+ ('changes_start_page_token', 'get'): {'drive_id': 'driveId', 'supports_all_drives': 'supportsAllDrives'},
114
+ ('about', 'get'): {'fields': 'fields'},
115
+ }
116
+
117
+ def __init__(
118
+ self,
119
+ auth_config: GoogleDriveAuthConfig | None = None,
120
+ external_user_id: str | None = None,
121
+ airbyte_client_id: str | None = None,
122
+ airbyte_client_secret: str | None = None,
123
+ on_token_refresh: Any | None = None ):
124
+ """
125
+ Initialize a new google-drive connector instance.
126
+
127
+ Supports both local and hosted execution modes:
128
+ - Local mode: Provide `auth_config` for direct API calls
129
+ - Hosted mode: Provide `external_user_id`, `airbyte_client_id`, and `airbyte_client_secret` for hosted execution
130
+
131
+ Args:
132
+ auth_config: Typed authentication configuration (required for local mode)
133
+ external_user_id: External user ID (required for hosted mode)
134
+ airbyte_client_id: Airbyte OAuth client ID (required for hosted mode)
135
+ airbyte_client_secret: Airbyte OAuth client secret (required for hosted mode)
136
+ on_token_refresh: Optional callback for OAuth2 token refresh persistence.
137
+ Called with new_tokens dict when tokens are refreshed. Can be sync or async.
138
+ Example: lambda tokens: save_to_database(tokens)
139
+ Examples:
140
+ # Local mode (direct API calls)
141
+ connector = GoogleDriveConnector(auth_config=GoogleDriveAuthConfig(access_token="...", refresh_token="...", client_id="...", client_secret="..."))
142
+ # Hosted mode (executed on Airbyte cloud)
143
+ connector = GoogleDriveConnector(
144
+ external_user_id="user-123",
145
+ airbyte_client_id="client_abc123",
146
+ airbyte_client_secret="secret_xyz789"
147
+ )
148
+
149
+ # Local mode with OAuth2 token refresh callback
150
+ def save_tokens(new_tokens: dict) -> None:
151
+ # Persist updated tokens to your storage (file, database, etc.)
152
+ with open("tokens.json", "w") as f:
153
+ json.dump(new_tokens, f)
154
+
155
+ connector = GoogleDriveConnector(
156
+ auth_config=GoogleDriveAuthConfig(access_token="...", refresh_token="..."),
157
+ on_token_refresh=save_tokens
158
+ )
159
+ """
160
+ # Hosted mode: external_user_id, airbyte_client_id, and airbyte_client_secret provided
161
+ if external_user_id and airbyte_client_id and airbyte_client_secret:
162
+ from ._vendored.connector_sdk.executor import HostedExecutor
163
+ self._executor = HostedExecutor(
164
+ external_user_id=external_user_id,
165
+ airbyte_client_id=airbyte_client_id,
166
+ airbyte_client_secret=airbyte_client_secret,
167
+ connector_definition_id=str(GoogleDriveConnectorModel.id),
168
+ )
169
+ else:
170
+ # Local mode: auth_config required
171
+ if not auth_config:
172
+ raise ValueError(
173
+ "Either provide (external_user_id, airbyte_client_id, airbyte_client_secret) for hosted mode "
174
+ "or auth_config for local mode"
175
+ )
176
+
177
+ from ._vendored.connector_sdk.executor import LocalExecutor
178
+
179
+ # Build config_values dict from server variables
180
+ config_values = None
181
+
182
+ self._executor = LocalExecutor(
183
+ model=GoogleDriveConnectorModel,
184
+ auth_config=auth_config.model_dump() if auth_config else None,
185
+ config_values=config_values,
186
+ on_token_refresh=on_token_refresh
187
+ )
188
+
189
+ # Update base_url with server variables if provided
190
+
191
+ # Initialize entity query objects
192
+ self.files = FilesQuery(self)
193
+ self.files_export = FilesExportQuery(self)
194
+ self.drives = DrivesQuery(self)
195
+ self.permissions = PermissionsQuery(self)
196
+ self.comments = CommentsQuery(self)
197
+ self.replies = RepliesQuery(self)
198
+ self.revisions = RevisionsQuery(self)
199
+ self.changes = ChangesQuery(self)
200
+ self.changes_start_page_token = ChangesStartPageTokenQuery(self)
201
+ self.about = AboutQuery(self)
202
+
203
+ # ===== TYPED EXECUTE METHOD (Recommended Interface) =====
204
+
205
+ @overload
206
+ async def execute(
207
+ self,
208
+ entity: Literal["files"],
209
+ action: Literal["list"],
210
+ params: "FilesListParams"
211
+ ) -> "FilesListResult": ...
212
+
213
+ @overload
214
+ async def execute(
215
+ self,
216
+ entity: Literal["files"],
217
+ action: Literal["get"],
218
+ params: "FilesGetParams"
219
+ ) -> "File": ...
220
+
221
+ @overload
222
+ async def execute(
223
+ self,
224
+ entity: Literal["files"],
225
+ action: Literal["download"],
226
+ params: "FilesDownloadParams"
227
+ ) -> "AsyncIterator[bytes]": ...
228
+
229
+ @overload
230
+ async def execute(
231
+ self,
232
+ entity: Literal["files_export"],
233
+ action: Literal["download"],
234
+ params: "FilesExportDownloadParams"
235
+ ) -> "AsyncIterator[bytes]": ...
236
+
237
+ @overload
238
+ async def execute(
239
+ self,
240
+ entity: Literal["drives"],
241
+ action: Literal["list"],
242
+ params: "DrivesListParams"
243
+ ) -> "DrivesListResult": ...
244
+
245
+ @overload
246
+ async def execute(
247
+ self,
248
+ entity: Literal["drives"],
249
+ action: Literal["get"],
250
+ params: "DrivesGetParams"
251
+ ) -> "Drive": ...
252
+
253
+ @overload
254
+ async def execute(
255
+ self,
256
+ entity: Literal["permissions"],
257
+ action: Literal["list"],
258
+ params: "PermissionsListParams"
259
+ ) -> "PermissionsListResult": ...
260
+
261
+ @overload
262
+ async def execute(
263
+ self,
264
+ entity: Literal["permissions"],
265
+ action: Literal["get"],
266
+ params: "PermissionsGetParams"
267
+ ) -> "Permission": ...
268
+
269
+ @overload
270
+ async def execute(
271
+ self,
272
+ entity: Literal["comments"],
273
+ action: Literal["list"],
274
+ params: "CommentsListParams"
275
+ ) -> "CommentsListResult": ...
276
+
277
+ @overload
278
+ async def execute(
279
+ self,
280
+ entity: Literal["comments"],
281
+ action: Literal["get"],
282
+ params: "CommentsGetParams"
283
+ ) -> "Comment": ...
284
+
285
+ @overload
286
+ async def execute(
287
+ self,
288
+ entity: Literal["replies"],
289
+ action: Literal["list"],
290
+ params: "RepliesListParams"
291
+ ) -> "RepliesListResult": ...
292
+
293
+ @overload
294
+ async def execute(
295
+ self,
296
+ entity: Literal["replies"],
297
+ action: Literal["get"],
298
+ params: "RepliesGetParams"
299
+ ) -> "Reply": ...
300
+
301
+ @overload
302
+ async def execute(
303
+ self,
304
+ entity: Literal["revisions"],
305
+ action: Literal["list"],
306
+ params: "RevisionsListParams"
307
+ ) -> "RevisionsListResult": ...
308
+
309
+ @overload
310
+ async def execute(
311
+ self,
312
+ entity: Literal["revisions"],
313
+ action: Literal["get"],
314
+ params: "RevisionsGetParams"
315
+ ) -> "Revision": ...
316
+
317
+ @overload
318
+ async def execute(
319
+ self,
320
+ entity: Literal["changes"],
321
+ action: Literal["list"],
322
+ params: "ChangesListParams"
323
+ ) -> "ChangesListResult": ...
324
+
325
+ @overload
326
+ async def execute(
327
+ self,
328
+ entity: Literal["changes_start_page_token"],
329
+ action: Literal["get"],
330
+ params: "ChangesStartPageTokenGetParams"
331
+ ) -> "StartPageToken": ...
332
+
333
+ @overload
334
+ async def execute(
335
+ self,
336
+ entity: Literal["about"],
337
+ action: Literal["get"],
338
+ params: "AboutGetParams"
339
+ ) -> "About": ...
340
+
341
+
342
+ @overload
343
+ async def execute(
344
+ self,
345
+ entity: str,
346
+ action: str,
347
+ params: dict[str, Any]
348
+ ) -> GoogleDriveExecuteResult[Any] | GoogleDriveExecuteResultWithMeta[Any, Any] | Any: ...
349
+
350
+ async def execute(
351
+ self,
352
+ entity: str,
353
+ action: str,
354
+ params: dict[str, Any] | None = None
355
+ ) -> Any:
356
+ """
357
+ Execute an entity operation with full type safety.
358
+
359
+ This is the recommended interface for blessed connectors as it:
360
+ - Uses the same signature as non-blessed connectors
361
+ - Provides full IDE autocomplete for entity/action/params
362
+ - Makes migration from generic to blessed connectors seamless
363
+
364
+ Args:
365
+ entity: Entity name (e.g., "customers")
366
+ action: Operation action (e.g., "create", "get", "list")
367
+ params: Operation parameters (typed based on entity+action)
368
+
369
+ Returns:
370
+ Typed response based on the operation
371
+
372
+ Example:
373
+ customer = await connector.execute(
374
+ entity="customers",
375
+ action="get",
376
+ params={"id": "cus_123"}
377
+ )
378
+ """
379
+ from ._vendored.connector_sdk.executor import ExecutionConfig
380
+
381
+ # Remap parameter names from snake_case (TypedDict keys) to API parameter names
382
+ if params:
383
+ param_map = self._PARAM_MAP.get((entity, action), {})
384
+ if param_map:
385
+ params = {param_map.get(k, k): v for k, v in params.items()}
386
+
387
+ # Use ExecutionConfig for both local and hosted executors
388
+ config = ExecutionConfig(
389
+ entity=entity,
390
+ action=action,
391
+ params=params
392
+ )
393
+
394
+ result = await self._executor.execute(config)
395
+
396
+ if not result.success:
397
+ raise RuntimeError(f"Execution failed: {result.error}")
398
+
399
+ # Check if this operation has extractors configured
400
+ has_extractors = self._ENVELOPE_MAP.get((entity, action), False)
401
+
402
+ if has_extractors:
403
+ # With extractors - return Pydantic envelope with data and meta
404
+ if result.meta is not None:
405
+ return GoogleDriveExecuteResultWithMeta[Any, Any](
406
+ data=result.data,
407
+ meta=result.meta
408
+ )
409
+ else:
410
+ return GoogleDriveExecuteResult[Any](data=result.data)
411
+ else:
412
+ # No extractors - return raw response data
413
+ return result.data
414
+
415
+ # ===== INTROSPECTION METHODS =====
416
+
417
+ @classmethod
418
+ def describe(cls, func: _F) -> _F:
419
+ """
420
+ Decorator that populates a function's docstring with connector capabilities.
421
+
422
+ This class method can be used as a decorator to automatically generate
423
+ comprehensive documentation for AI tool functions.
424
+
425
+ Usage:
426
+ @mcp.tool()
427
+ @GoogleDriveConnector.describe
428
+ async def execute(entity: str, action: str, params: dict):
429
+ '''Execute operations.'''
430
+ ...
431
+
432
+ The decorated function's __doc__ will be updated with:
433
+ - Available entities and their actions
434
+ - Parameter signatures with required (*) and optional (?) markers
435
+ - Response structure documentation
436
+ - Example questions (if available in OpenAPI spec)
437
+
438
+ Args:
439
+ func: The function to decorate
440
+
441
+ Returns:
442
+ The same function with updated __doc__
443
+ """
444
+ description = generate_tool_description(GoogleDriveConnectorModel)
445
+
446
+ original_doc = func.__doc__ or ""
447
+ if original_doc.strip():
448
+ func.__doc__ = f"{original_doc.strip()}\n{description}"
449
+ else:
450
+ func.__doc__ = description
451
+
452
+ return func
453
+
454
+ def list_entities(self) -> list[dict[str, Any]]:
455
+ """
456
+ Get structured data about available entities, actions, and parameters.
457
+
458
+ Returns a list of entity descriptions with:
459
+ - entity_name: Name of the entity (e.g., "contacts", "deals")
460
+ - description: Entity description from the first endpoint
461
+ - available_actions: List of actions (e.g., ["list", "get", "create"])
462
+ - parameters: Dict mapping action -> list of parameter dicts
463
+
464
+ Example:
465
+ entities = connector.list_entities()
466
+ for entity in entities:
467
+ print(f"{entity['entity_name']}: {entity['available_actions']}")
468
+ """
469
+ return describe_entities(GoogleDriveConnectorModel)
470
+
471
+ def entity_schema(self, entity: str) -> dict[str, Any] | None:
472
+ """
473
+ Get the JSON schema for an entity.
474
+
475
+ Args:
476
+ entity: Entity name (e.g., "contacts", "companies")
477
+
478
+ Returns:
479
+ JSON schema dict describing the entity structure, or None if not found.
480
+
481
+ Example:
482
+ schema = connector.entity_schema("contacts")
483
+ if schema:
484
+ print(f"Contact properties: {list(schema.get('properties', {}).keys())}")
485
+ """
486
+ entity_def = next(
487
+ (e for e in GoogleDriveConnectorModel.entities if e.name == entity),
488
+ None
489
+ )
490
+ if entity_def is None:
491
+ logging.getLogger(__name__).warning(
492
+ f"Entity '{entity}' not found. Available entities: "
493
+ f"{[e.name for e in GoogleDriveConnectorModel.entities]}"
494
+ )
495
+ return entity_def.entity_schema if entity_def else None
496
+
497
+
498
+
499
+ class FilesQuery:
500
+ """
501
+ Query class for Files entity operations.
502
+ """
503
+
504
+ def __init__(self, connector: GoogleDriveConnector):
505
+ """Initialize query with connector reference."""
506
+ self._connector = connector
507
+
508
+ async def list(
509
+ self,
510
+ page_size: int | None = None,
511
+ page_token: str | None = None,
512
+ q: str | None = None,
513
+ order_by: str | None = None,
514
+ fields: str | None = None,
515
+ spaces: str | None = None,
516
+ corpora: str | None = None,
517
+ drive_id: str | None = None,
518
+ include_items_from_all_drives: bool | None = None,
519
+ supports_all_drives: bool | None = None,
520
+ **kwargs
521
+ ) -> FilesListResult:
522
+ """
523
+ Lists the user's files. Returns a paginated list of files.
524
+
525
+ Args:
526
+ page_size: Maximum number of files to return per page (1-1000)
527
+ page_token: Token for continuing a previous list request
528
+ q: Query string for searching files
529
+ order_by: Sort order (e.g., 'modifiedTime desc', 'name')
530
+ fields: Fields to include in the response
531
+ spaces: Comma-separated list of spaces to query (drive, appDataFolder)
532
+ corpora: Bodies of items to search (user, drive, allDrives)
533
+ drive_id: ID of the shared drive to search
534
+ include_items_from_all_drives: Whether to include items from all drives
535
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
536
+ **kwargs: Additional parameters
537
+
538
+ Returns:
539
+ FilesListResult
540
+ """
541
+ params = {k: v for k, v in {
542
+ "pageSize": page_size,
543
+ "pageToken": page_token,
544
+ "q": q,
545
+ "orderBy": order_by,
546
+ "fields": fields,
547
+ "spaces": spaces,
548
+ "corpora": corpora,
549
+ "driveId": drive_id,
550
+ "includeItemsFromAllDrives": include_items_from_all_drives,
551
+ "supportsAllDrives": supports_all_drives,
552
+ **kwargs
553
+ }.items() if v is not None}
554
+
555
+ result = await self._connector.execute("files", "list", params)
556
+ # Cast generic envelope to concrete typed result
557
+ return FilesListResult(
558
+ data=result.data,
559
+ meta=result.meta
560
+ )
561
+
562
+
563
+
564
+ async def get(
565
+ self,
566
+ file_id: str,
567
+ fields: str | None = None,
568
+ supports_all_drives: bool | None = None,
569
+ **kwargs
570
+ ) -> File:
571
+ """
572
+ Gets a file's metadata by ID
573
+
574
+ Args:
575
+ file_id: The ID of the file
576
+ fields: Fields to include in the response
577
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
578
+ **kwargs: Additional parameters
579
+
580
+ Returns:
581
+ File
582
+ """
583
+ params = {k: v for k, v in {
584
+ "fileId": file_id,
585
+ "fields": fields,
586
+ "supportsAllDrives": supports_all_drives,
587
+ **kwargs
588
+ }.items() if v is not None}
589
+
590
+ result = await self._connector.execute("files", "get", params)
591
+ return result
592
+
593
+
594
+
595
+ async def download(
596
+ self,
597
+ file_id: str,
598
+ alt: str,
599
+ acknowledge_abuse: bool | None = None,
600
+ supports_all_drives: bool | None = None,
601
+ range_header: str | None = None,
602
+ **kwargs
603
+ ) -> AsyncIterator[bytes]:
604
+ """
605
+ Downloads the binary content of a file. This works for non-Google Workspace files
606
+ (PDFs, images, zip files, etc.). For Google Docs, Sheets, Slides, or Drawings,
607
+ use the export action instead.
608
+
609
+
610
+ Args:
611
+ file_id: The ID of the file to download
612
+ alt: Must be set to 'media' to download file content
613
+ acknowledge_abuse: Whether the user is acknowledging the risk of downloading known malware or other abusive files
614
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
615
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
616
+ **kwargs: Additional parameters
617
+
618
+ Returns:
619
+ AsyncIterator[bytes]
620
+ """
621
+ params = {k: v for k, v in {
622
+ "fileId": file_id,
623
+ "alt": alt,
624
+ "acknowledgeAbuse": acknowledge_abuse,
625
+ "supportsAllDrives": supports_all_drives,
626
+ "range_header": range_header,
627
+ **kwargs
628
+ }.items() if v is not None}
629
+
630
+ result = await self._connector.execute("files", "download", params)
631
+ return result
632
+
633
+
634
+ async def download_local(
635
+ self,
636
+ fileId: str,
637
+ alt: str,
638
+ path: str,
639
+ acknowledgeAbuse: bool | None = None,
640
+ supportsAllDrives: bool | None = None,
641
+ range_header: str | None = None,
642
+ **kwargs
643
+ ) -> Path:
644
+ """
645
+ Downloads the binary content of a file. This works for non-Google Workspace files
646
+ (PDFs, images, zip files, etc.). For Google Docs, Sheets, Slides, or Drawings,
647
+ use the export action instead.
648
+ and save to file.
649
+
650
+ Args:
651
+ fileId: The ID of the file to download
652
+ alt: Must be set to 'media' to download file content
653
+ acknowledgeAbuse: Whether the user is acknowledging the risk of downloading known malware or other abusive files
654
+ supportsAllDrives: Whether the requesting application supports both My Drives and shared drives
655
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
656
+ path: File path to save downloaded content
657
+ **kwargs: Additional parameters
658
+
659
+ Returns:
660
+ str: Path to the downloaded file
661
+ """
662
+ from ._vendored.connector_sdk import save_download
663
+
664
+ # Get the async iterator
665
+ content_iterator = await self.download(
666
+ fileId=fileId,
667
+ alt=alt,
668
+ acknowledgeAbuse=acknowledgeAbuse,
669
+ supportsAllDrives=supportsAllDrives,
670
+ range_header=range_header,
671
+ **kwargs
672
+ )
673
+
674
+ return await save_download(content_iterator, path)
675
+
676
+
677
+ class FilesExportQuery:
678
+ """
679
+ Query class for FilesExport entity operations.
680
+ """
681
+
682
+ def __init__(self, connector: GoogleDriveConnector):
683
+ """Initialize query with connector reference."""
684
+ self._connector = connector
685
+
686
+ async def download(
687
+ self,
688
+ file_id: str,
689
+ mime_type: str,
690
+ range_header: str | None = None,
691
+ **kwargs
692
+ ) -> AsyncIterator[bytes]:
693
+ """
694
+ Exports a Google Workspace file (Docs, Sheets, Slides, Drawings) to a specified format.
695
+ Common export formats:
696
+ - application/pdf (all types)
697
+ - text/plain (Docs)
698
+ - text/csv (Sheets)
699
+ - application/vnd.openxmlformats-officedocument.wordprocessingml.document (Docs to .docx)
700
+ - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (Sheets to .xlsx)
701
+ - application/vnd.openxmlformats-officedocument.presentationml.presentation (Slides to .pptx)
702
+ Note: Export has a 10MB limit. For larger files, use the Drive UI.
703
+
704
+
705
+ Args:
706
+ file_id: The ID of the Google Workspace file to export
707
+ mime_type: The MIME type of the format to export to. Common values:
708
+ - application/pdf
709
+ - text/plain
710
+ - text/csv
711
+ - application/vnd.openxmlformats-officedocument.wordprocessingml.document
712
+ - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
713
+ - application/vnd.openxmlformats-officedocument.presentationml.presentation
714
+
715
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
716
+ **kwargs: Additional parameters
717
+
718
+ Returns:
719
+ AsyncIterator[bytes]
720
+ """
721
+ params = {k: v for k, v in {
722
+ "fileId": file_id,
723
+ "mimeType": mime_type,
724
+ "range_header": range_header,
725
+ **kwargs
726
+ }.items() if v is not None}
727
+
728
+ result = await self._connector.execute("files_export", "download", params)
729
+ return result
730
+
731
+
732
+ async def download_local(
733
+ self,
734
+ fileId: str,
735
+ mimeType: str,
736
+ path: str,
737
+ range_header: str | None = None,
738
+ **kwargs
739
+ ) -> Path:
740
+ """
741
+ Exports a Google Workspace file (Docs, Sheets, Slides, Drawings) to a specified format.
742
+ Common export formats:
743
+ - application/pdf (all types)
744
+ - text/plain (Docs)
745
+ - text/csv (Sheets)
746
+ - application/vnd.openxmlformats-officedocument.wordprocessingml.document (Docs to .docx)
747
+ - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (Sheets to .xlsx)
748
+ - application/vnd.openxmlformats-officedocument.presentationml.presentation (Slides to .pptx)
749
+ Note: Export has a 10MB limit. For larger files, use the Drive UI.
750
+ and save to file.
751
+
752
+ Args:
753
+ fileId: The ID of the Google Workspace file to export
754
+ mimeType: The MIME type of the format to export to. Common values:
755
+ - application/pdf
756
+ - text/plain
757
+ - text/csv
758
+ - application/vnd.openxmlformats-officedocument.wordprocessingml.document
759
+ - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
760
+ - application/vnd.openxmlformats-officedocument.presentationml.presentation
761
+
762
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
763
+ path: File path to save downloaded content
764
+ **kwargs: Additional parameters
765
+
766
+ Returns:
767
+ str: Path to the downloaded file
768
+ """
769
+ from ._vendored.connector_sdk import save_download
770
+
771
+ # Get the async iterator
772
+ content_iterator = await self.download(
773
+ fileId=fileId,
774
+ mimeType=mimeType,
775
+ range_header=range_header,
776
+ **kwargs
777
+ )
778
+
779
+ return await save_download(content_iterator, path)
780
+
781
+
782
+ class DrivesQuery:
783
+ """
784
+ Query class for Drives entity operations.
785
+ """
786
+
787
+ def __init__(self, connector: GoogleDriveConnector):
788
+ """Initialize query with connector reference."""
789
+ self._connector = connector
790
+
791
+ async def list(
792
+ self,
793
+ page_size: int | None = None,
794
+ page_token: str | None = None,
795
+ q: str | None = None,
796
+ use_domain_admin_access: bool | None = None,
797
+ **kwargs
798
+ ) -> DrivesListResult:
799
+ """
800
+ Lists the user's shared drives
801
+
802
+ Args:
803
+ page_size: Maximum number of shared drives to return (1-100)
804
+ page_token: Token for continuing a previous list request
805
+ q: Query string for searching shared drives
806
+ use_domain_admin_access: Issue the request as a domain administrator
807
+ **kwargs: Additional parameters
808
+
809
+ Returns:
810
+ DrivesListResult
811
+ """
812
+ params = {k: v for k, v in {
813
+ "pageSize": page_size,
814
+ "pageToken": page_token,
815
+ "q": q,
816
+ "useDomainAdminAccess": use_domain_admin_access,
817
+ **kwargs
818
+ }.items() if v is not None}
819
+
820
+ result = await self._connector.execute("drives", "list", params)
821
+ # Cast generic envelope to concrete typed result
822
+ return DrivesListResult(
823
+ data=result.data,
824
+ meta=result.meta
825
+ )
826
+
827
+
828
+
829
+ async def get(
830
+ self,
831
+ drive_id: str,
832
+ use_domain_admin_access: bool | None = None,
833
+ **kwargs
834
+ ) -> Drive:
835
+ """
836
+ Gets a shared drive's metadata by ID
837
+
838
+ Args:
839
+ drive_id: The ID of the shared drive
840
+ use_domain_admin_access: Issue the request as a domain administrator
841
+ **kwargs: Additional parameters
842
+
843
+ Returns:
844
+ Drive
845
+ """
846
+ params = {k: v for k, v in {
847
+ "driveId": drive_id,
848
+ "useDomainAdminAccess": use_domain_admin_access,
849
+ **kwargs
850
+ }.items() if v is not None}
851
+
852
+ result = await self._connector.execute("drives", "get", params)
853
+ return result
854
+
855
+
856
+
857
+ class PermissionsQuery:
858
+ """
859
+ Query class for Permissions entity operations.
860
+ """
861
+
862
+ def __init__(self, connector: GoogleDriveConnector):
863
+ """Initialize query with connector reference."""
864
+ self._connector = connector
865
+
866
+ async def list(
867
+ self,
868
+ file_id: str,
869
+ page_size: int | None = None,
870
+ page_token: str | None = None,
871
+ supports_all_drives: bool | None = None,
872
+ use_domain_admin_access: bool | None = None,
873
+ **kwargs
874
+ ) -> PermissionsListResult:
875
+ """
876
+ Lists a file's or shared drive's permissions
877
+
878
+ Args:
879
+ file_id: The ID of the file or shared drive
880
+ page_size: Maximum number of permissions to return (1-100)
881
+ page_token: Token for continuing a previous list request
882
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
883
+ use_domain_admin_access: Issue the request as a domain administrator
884
+ **kwargs: Additional parameters
885
+
886
+ Returns:
887
+ PermissionsListResult
888
+ """
889
+ params = {k: v for k, v in {
890
+ "fileId": file_id,
891
+ "pageSize": page_size,
892
+ "pageToken": page_token,
893
+ "supportsAllDrives": supports_all_drives,
894
+ "useDomainAdminAccess": use_domain_admin_access,
895
+ **kwargs
896
+ }.items() if v is not None}
897
+
898
+ result = await self._connector.execute("permissions", "list", params)
899
+ # Cast generic envelope to concrete typed result
900
+ return PermissionsListResult(
901
+ data=result.data,
902
+ meta=result.meta
903
+ )
904
+
905
+
906
+
907
+ async def get(
908
+ self,
909
+ file_id: str,
910
+ permission_id: str,
911
+ supports_all_drives: bool | None = None,
912
+ use_domain_admin_access: bool | None = None,
913
+ **kwargs
914
+ ) -> Permission:
915
+ """
916
+ Gets a permission by ID
917
+
918
+ Args:
919
+ file_id: The ID of the file
920
+ permission_id: The ID of the permission
921
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
922
+ use_domain_admin_access: Issue the request as a domain administrator
923
+ **kwargs: Additional parameters
924
+
925
+ Returns:
926
+ Permission
927
+ """
928
+ params = {k: v for k, v in {
929
+ "fileId": file_id,
930
+ "permissionId": permission_id,
931
+ "supportsAllDrives": supports_all_drives,
932
+ "useDomainAdminAccess": use_domain_admin_access,
933
+ **kwargs
934
+ }.items() if v is not None}
935
+
936
+ result = await self._connector.execute("permissions", "get", params)
937
+ return result
938
+
939
+
940
+
941
+ class CommentsQuery:
942
+ """
943
+ Query class for Comments entity operations.
944
+ """
945
+
946
+ def __init__(self, connector: GoogleDriveConnector):
947
+ """Initialize query with connector reference."""
948
+ self._connector = connector
949
+
950
+ async def list(
951
+ self,
952
+ file_id: str,
953
+ page_size: int | None = None,
954
+ page_token: str | None = None,
955
+ start_modified_time: str | None = None,
956
+ include_deleted: bool | None = None,
957
+ fields: str | None = None,
958
+ **kwargs
959
+ ) -> CommentsListResult:
960
+ """
961
+ Lists a file's comments
962
+
963
+ Args:
964
+ file_id: The ID of the file
965
+ page_size: Maximum number of comments to return (1-100)
966
+ page_token: Token for continuing a previous list request
967
+ start_modified_time: Minimum value of modifiedTime to filter by (RFC 3339)
968
+ include_deleted: Whether to include deleted comments
969
+ fields: Fields to include in the response (required for comments)
970
+ **kwargs: Additional parameters
971
+
972
+ Returns:
973
+ CommentsListResult
974
+ """
975
+ params = {k: v for k, v in {
976
+ "fileId": file_id,
977
+ "pageSize": page_size,
978
+ "pageToken": page_token,
979
+ "startModifiedTime": start_modified_time,
980
+ "includeDeleted": include_deleted,
981
+ "fields": fields,
982
+ **kwargs
983
+ }.items() if v is not None}
984
+
985
+ result = await self._connector.execute("comments", "list", params)
986
+ # Cast generic envelope to concrete typed result
987
+ return CommentsListResult(
988
+ data=result.data,
989
+ meta=result.meta
990
+ )
991
+
992
+
993
+
994
+ async def get(
995
+ self,
996
+ file_id: str,
997
+ comment_id: str,
998
+ include_deleted: bool | None = None,
999
+ fields: str | None = None,
1000
+ **kwargs
1001
+ ) -> Comment:
1002
+ """
1003
+ Gets a comment by ID
1004
+
1005
+ Args:
1006
+ file_id: The ID of the file
1007
+ comment_id: The ID of the comment
1008
+ include_deleted: Whether to return deleted comments
1009
+ fields: Fields to include in the response (required for comments)
1010
+ **kwargs: Additional parameters
1011
+
1012
+ Returns:
1013
+ Comment
1014
+ """
1015
+ params = {k: v for k, v in {
1016
+ "fileId": file_id,
1017
+ "commentId": comment_id,
1018
+ "includeDeleted": include_deleted,
1019
+ "fields": fields,
1020
+ **kwargs
1021
+ }.items() if v is not None}
1022
+
1023
+ result = await self._connector.execute("comments", "get", params)
1024
+ return result
1025
+
1026
+
1027
+
1028
+ class RepliesQuery:
1029
+ """
1030
+ Query class for Replies entity operations.
1031
+ """
1032
+
1033
+ def __init__(self, connector: GoogleDriveConnector):
1034
+ """Initialize query with connector reference."""
1035
+ self._connector = connector
1036
+
1037
+ async def list(
1038
+ self,
1039
+ file_id: str,
1040
+ comment_id: str,
1041
+ page_size: int | None = None,
1042
+ page_token: str | None = None,
1043
+ include_deleted: bool | None = None,
1044
+ fields: str | None = None,
1045
+ **kwargs
1046
+ ) -> RepliesListResult:
1047
+ """
1048
+ Lists a comment's replies
1049
+
1050
+ Args:
1051
+ file_id: The ID of the file
1052
+ comment_id: The ID of the comment
1053
+ page_size: Maximum number of replies to return (1-100)
1054
+ page_token: Token for continuing a previous list request
1055
+ include_deleted: Whether to include deleted replies
1056
+ fields: Fields to include in the response (required for replies)
1057
+ **kwargs: Additional parameters
1058
+
1059
+ Returns:
1060
+ RepliesListResult
1061
+ """
1062
+ params = {k: v for k, v in {
1063
+ "fileId": file_id,
1064
+ "commentId": comment_id,
1065
+ "pageSize": page_size,
1066
+ "pageToken": page_token,
1067
+ "includeDeleted": include_deleted,
1068
+ "fields": fields,
1069
+ **kwargs
1070
+ }.items() if v is not None}
1071
+
1072
+ result = await self._connector.execute("replies", "list", params)
1073
+ # Cast generic envelope to concrete typed result
1074
+ return RepliesListResult(
1075
+ data=result.data,
1076
+ meta=result.meta
1077
+ )
1078
+
1079
+
1080
+
1081
+ async def get(
1082
+ self,
1083
+ file_id: str,
1084
+ comment_id: str,
1085
+ reply_id: str,
1086
+ include_deleted: bool | None = None,
1087
+ fields: str | None = None,
1088
+ **kwargs
1089
+ ) -> Reply:
1090
+ """
1091
+ Gets a reply by ID
1092
+
1093
+ Args:
1094
+ file_id: The ID of the file
1095
+ comment_id: The ID of the comment
1096
+ reply_id: The ID of the reply
1097
+ include_deleted: Whether to return deleted replies
1098
+ fields: Fields to include in the response (required for replies)
1099
+ **kwargs: Additional parameters
1100
+
1101
+ Returns:
1102
+ Reply
1103
+ """
1104
+ params = {k: v for k, v in {
1105
+ "fileId": file_id,
1106
+ "commentId": comment_id,
1107
+ "replyId": reply_id,
1108
+ "includeDeleted": include_deleted,
1109
+ "fields": fields,
1110
+ **kwargs
1111
+ }.items() if v is not None}
1112
+
1113
+ result = await self._connector.execute("replies", "get", params)
1114
+ return result
1115
+
1116
+
1117
+
1118
+ class RevisionsQuery:
1119
+ """
1120
+ Query class for Revisions entity operations.
1121
+ """
1122
+
1123
+ def __init__(self, connector: GoogleDriveConnector):
1124
+ """Initialize query with connector reference."""
1125
+ self._connector = connector
1126
+
1127
+ async def list(
1128
+ self,
1129
+ file_id: str,
1130
+ page_size: int | None = None,
1131
+ page_token: str | None = None,
1132
+ **kwargs
1133
+ ) -> RevisionsListResult:
1134
+ """
1135
+ Lists a file's revisions
1136
+
1137
+ Args:
1138
+ file_id: The ID of the file
1139
+ page_size: Maximum number of revisions to return (1-1000)
1140
+ page_token: Token for continuing a previous list request
1141
+ **kwargs: Additional parameters
1142
+
1143
+ Returns:
1144
+ RevisionsListResult
1145
+ """
1146
+ params = {k: v for k, v in {
1147
+ "fileId": file_id,
1148
+ "pageSize": page_size,
1149
+ "pageToken": page_token,
1150
+ **kwargs
1151
+ }.items() if v is not None}
1152
+
1153
+ result = await self._connector.execute("revisions", "list", params)
1154
+ # Cast generic envelope to concrete typed result
1155
+ return RevisionsListResult(
1156
+ data=result.data,
1157
+ meta=result.meta
1158
+ )
1159
+
1160
+
1161
+
1162
+ async def get(
1163
+ self,
1164
+ file_id: str,
1165
+ revision_id: str,
1166
+ **kwargs
1167
+ ) -> Revision:
1168
+ """
1169
+ Gets a revision's metadata by ID
1170
+
1171
+ Args:
1172
+ file_id: The ID of the file
1173
+ revision_id: The ID of the revision
1174
+ **kwargs: Additional parameters
1175
+
1176
+ Returns:
1177
+ Revision
1178
+ """
1179
+ params = {k: v for k, v in {
1180
+ "fileId": file_id,
1181
+ "revisionId": revision_id,
1182
+ **kwargs
1183
+ }.items() if v is not None}
1184
+
1185
+ result = await self._connector.execute("revisions", "get", params)
1186
+ return result
1187
+
1188
+
1189
+
1190
+ class ChangesQuery:
1191
+ """
1192
+ Query class for Changes entity operations.
1193
+ """
1194
+
1195
+ def __init__(self, connector: GoogleDriveConnector):
1196
+ """Initialize query with connector reference."""
1197
+ self._connector = connector
1198
+
1199
+ async def list(
1200
+ self,
1201
+ page_token: str,
1202
+ page_size: int | None = None,
1203
+ drive_id: str | None = None,
1204
+ include_items_from_all_drives: bool | None = None,
1205
+ supports_all_drives: bool | None = None,
1206
+ spaces: str | None = None,
1207
+ include_removed: bool | None = None,
1208
+ restrict_to_my_drive: bool | None = None,
1209
+ **kwargs
1210
+ ) -> ChangesListResult:
1211
+ """
1212
+ Lists the changes for a user or shared drive
1213
+
1214
+ Args:
1215
+ page_token: Token for the page of changes to retrieve (from changes.getStartPageToken or previous response)
1216
+ page_size: Maximum number of changes to return (1-1000)
1217
+ drive_id: The shared drive from which changes are returned
1218
+ include_items_from_all_drives: Whether to include changes from all drives
1219
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
1220
+ spaces: Comma-separated list of spaces to query
1221
+ include_removed: Whether to include changes indicating that items have been removed
1222
+ restrict_to_my_drive: Whether to restrict the results to changes inside the My Drive hierarchy
1223
+ **kwargs: Additional parameters
1224
+
1225
+ Returns:
1226
+ ChangesListResult
1227
+ """
1228
+ params = {k: v for k, v in {
1229
+ "pageToken": page_token,
1230
+ "pageSize": page_size,
1231
+ "driveId": drive_id,
1232
+ "includeItemsFromAllDrives": include_items_from_all_drives,
1233
+ "supportsAllDrives": supports_all_drives,
1234
+ "spaces": spaces,
1235
+ "includeRemoved": include_removed,
1236
+ "restrictToMyDrive": restrict_to_my_drive,
1237
+ **kwargs
1238
+ }.items() if v is not None}
1239
+
1240
+ result = await self._connector.execute("changes", "list", params)
1241
+ # Cast generic envelope to concrete typed result
1242
+ return ChangesListResult(
1243
+ data=result.data,
1244
+ meta=result.meta
1245
+ )
1246
+
1247
+
1248
+
1249
+ class ChangesStartPageTokenQuery:
1250
+ """
1251
+ Query class for ChangesStartPageToken entity operations.
1252
+ """
1253
+
1254
+ def __init__(self, connector: GoogleDriveConnector):
1255
+ """Initialize query with connector reference."""
1256
+ self._connector = connector
1257
+
1258
+ async def get(
1259
+ self,
1260
+ drive_id: str | None = None,
1261
+ supports_all_drives: bool | None = None,
1262
+ **kwargs
1263
+ ) -> StartPageToken:
1264
+ """
1265
+ Gets the starting pageToken for listing future changes
1266
+
1267
+ Args:
1268
+ drive_id: The ID of the shared drive for which the starting pageToken is returned
1269
+ supports_all_drives: Whether the requesting application supports both My Drives and shared drives
1270
+ **kwargs: Additional parameters
1271
+
1272
+ Returns:
1273
+ StartPageToken
1274
+ """
1275
+ params = {k: v for k, v in {
1276
+ "driveId": drive_id,
1277
+ "supportsAllDrives": supports_all_drives,
1278
+ **kwargs
1279
+ }.items() if v is not None}
1280
+
1281
+ result = await self._connector.execute("changes_start_page_token", "get", params)
1282
+ return result
1283
+
1284
+
1285
+
1286
+ class AboutQuery:
1287
+ """
1288
+ Query class for About entity operations.
1289
+ """
1290
+
1291
+ def __init__(self, connector: GoogleDriveConnector):
1292
+ """Initialize query with connector reference."""
1293
+ self._connector = connector
1294
+
1295
+ async def get(
1296
+ self,
1297
+ fields: str | None = None,
1298
+ **kwargs
1299
+ ) -> About:
1300
+ """
1301
+ Gets information about the user, the user's Drive, and system capabilities
1302
+
1303
+ Args:
1304
+ fields: Fields to include in the response (use * for all fields)
1305
+ **kwargs: Additional parameters
1306
+
1307
+ Returns:
1308
+ About
1309
+ """
1310
+ params = {k: v for k, v in {
1311
+ "fields": fields,
1312
+ **kwargs
1313
+ }.items() if v is not None}
1314
+
1315
+ result = await self._connector.execute("about", "get", params)
1316
+ return result
1317
+
1318
+