google-api-client-wrapper 1.0.0__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 (39) hide show
  1. google_api_client_wrapper-1.0.0.dist-info/METADATA +103 -0
  2. google_api_client_wrapper-1.0.0.dist-info/RECORD +39 -0
  3. google_api_client_wrapper-1.0.0.dist-info/WHEEL +5 -0
  4. google_api_client_wrapper-1.0.0.dist-info/licenses/LICENSE +21 -0
  5. google_api_client_wrapper-1.0.0.dist-info/top_level.txt +1 -0
  6. google_client/__init__.py +6 -0
  7. google_client/services/__init__.py +13 -0
  8. google_client/services/calendar/__init__.py +14 -0
  9. google_client/services/calendar/api_service.py +454 -0
  10. google_client/services/calendar/constants.py +48 -0
  11. google_client/services/calendar/exceptions.py +35 -0
  12. google_client/services/calendar/query_builder.py +314 -0
  13. google_client/services/calendar/types.py +403 -0
  14. google_client/services/calendar/utils.py +338 -0
  15. google_client/services/drive/__init__.py +13 -0
  16. google_client/services/drive/api_service.py +1133 -0
  17. google_client/services/drive/constants.py +37 -0
  18. google_client/services/drive/exceptions.py +60 -0
  19. google_client/services/drive/query_builder.py +385 -0
  20. google_client/services/drive/types.py +242 -0
  21. google_client/services/drive/utils.py +392 -0
  22. google_client/services/gmail/__init__.py +16 -0
  23. google_client/services/gmail/api_service.py +715 -0
  24. google_client/services/gmail/constants.py +6 -0
  25. google_client/services/gmail/exceptions.py +45 -0
  26. google_client/services/gmail/query_builder.py +408 -0
  27. google_client/services/gmail/types.py +285 -0
  28. google_client/services/gmail/utils.py +426 -0
  29. google_client/services/tasks/__init__.py +12 -0
  30. google_client/services/tasks/api_service.py +561 -0
  31. google_client/services/tasks/constants.py +32 -0
  32. google_client/services/tasks/exceptions.py +35 -0
  33. google_client/services/tasks/query_builder.py +324 -0
  34. google_client/services/tasks/types.py +156 -0
  35. google_client/services/tasks/utils.py +224 -0
  36. google_client/user_client.py +208 -0
  37. google_client/utils/__init__.py +0 -0
  38. google_client/utils/datetime.py +144 -0
  39. google_client/utils/validation.py +71 -0
@@ -0,0 +1,37 @@
1
+
2
+ MAX_RESULTS_LIMIT = 1000
3
+ DEFAULT_MAX_RESULTS = 30
4
+ MAX_FILE_SIZE = 5368709120 # 5GB Drive API limit for resumable uploads
5
+ DEFAULT_CHUNK_SIZE = 1048576 # 1MB default chunk size for uploads
6
+
7
+ # Special folder IDs
8
+ SHARED_DRIVE_ROOT = "root"
9
+ TRASH_FOLDER = "trash"
10
+ SHARED_WITH_ME_FOLDER = "sharedWithMe"
11
+
12
+ # Common MIME types
13
+ FOLDER_MIME_TYPE = "application/vnd.google-apps.folder"
14
+ GOOGLE_DOCS_MIME_TYPE = "application/vnd.google-apps.document"
15
+ GOOGLE_SHEETS_MIME_TYPE = "application/vnd.google-apps.spreadsheet"
16
+ GOOGLE_SLIDES_MIME_TYPE = "application/vnd.google-apps.presentation"
17
+
18
+ MICROSOFT_WORD_MIME_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
19
+ MICROSOFT_EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
20
+ MICROSOFT_POWERPOINT_MIME_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
21
+
22
+ # File field selections for API calls
23
+ DEFAULT_FILE_FIELDS = "id,name,size,mimeType,createdTime,modifiedTime,parents,webViewLink,webContentLink,owners,permissions"
24
+ MINIMAL_FILE_FIELDS = "id,name,mimeType"
25
+ FULL_FILE_FIELDS = "*"
26
+
27
+ # Permission roles
28
+ PERMISSION_ROLE_READER = "reader"
29
+ PERMISSION_ROLE_WRITER = "writer"
30
+ PERMISSION_ROLE_COMMENTER = "commenter"
31
+ PERMISSION_ROLE_OWNER = "owner"
32
+
33
+ # Permission types
34
+ PERMISSION_TYPE_USER = "user"
35
+ PERMISSION_TYPE_GROUP = "group"
36
+ PERMISSION_TYPE_DOMAIN = "domain"
37
+ PERMISSION_TYPE_ANYONE = "anyone"
@@ -0,0 +1,60 @@
1
+
2
+
3
+ class DriveError(Exception):
4
+ """Base exception for Drive API errors."""
5
+ pass
6
+
7
+
8
+ class FileNotFoundError(DriveError):
9
+ """Raised when a file or folder is not found."""
10
+ pass
11
+
12
+
13
+ class FolderNotFoundError(DriveError):
14
+ """Raised when a folder is not found."""
15
+ pass
16
+
17
+
18
+ class PermissionDeniedError(DriveError):
19
+ """Raised when the user lacks permission for a Drive operation."""
20
+ pass
21
+
22
+
23
+ class DriveQuotaExceededError(DriveError):
24
+ """Raised when Drive storage quota is exceeded."""
25
+ pass
26
+
27
+
28
+ class FileTooLargeError(DriveError):
29
+ """Raised when a file exceeds size limits."""
30
+ pass
31
+
32
+
33
+ class InvalidFileTypeError(DriveError):
34
+ """Raised when an unsupported file type is used."""
35
+ pass
36
+
37
+
38
+ class UploadFailedError(DriveError):
39
+ """Raised when file upload fails."""
40
+ pass
41
+
42
+
43
+ class DownloadFailedError(DriveError):
44
+ """Raised when file download fails."""
45
+ pass
46
+
47
+
48
+ class SharingError(DriveError):
49
+ """Raised when file sharing operations fail."""
50
+ pass
51
+
52
+
53
+ class DrivePermissionError(DriveError):
54
+ """Raised when permission operations fail."""
55
+ pass
56
+
57
+
58
+ class InvalidQueryError(DriveError):
59
+ """Raised when a Drive search query is invalid."""
60
+ pass
@@ -0,0 +1,385 @@
1
+ from datetime import datetime, date
2
+ from typing import Optional, List, Union, TYPE_CHECKING
3
+
4
+ from .constants import FOLDER_MIME_TYPE, MAX_RESULTS_LIMIT, DEFAULT_MAX_RESULTS
5
+ from ...utils.datetime import convert_datetime_to_iso
6
+
7
+ if TYPE_CHECKING:
8
+ from .api_service import DriveItem, DriveFolder
9
+
10
+
11
+ class DriveQueryBuilder:
12
+ """
13
+ Builder pattern for constructing Drive queries with a fluent API.
14
+ Provides a clean, readable way to build complex file queries.
15
+
16
+ Example usage:
17
+ files = (user.drive.query()
18
+ .limit(50)
19
+ .in_folder("parent_folder_id")
20
+ .search("meeting")
21
+ .file_type("pdf")
22
+ .execute())
23
+ """
24
+
25
+ def __init__(self, api_service_class):
26
+ self._api_service = api_service_class
27
+ self._max_results: Optional[int] = DEFAULT_MAX_RESULTS
28
+ self._query_parts: List[str] = []
29
+ self._fields: Optional[str] = None
30
+ self._order_by: Optional[str] = None
31
+ self._page_token: Optional[str] = None
32
+
33
+ def limit(self, count: int) -> "DriveQueryBuilder":
34
+ """
35
+ Set the maximum number of files to retrieve.
36
+ Args:
37
+ count: Maximum number of files (1-1000)
38
+ Returns:
39
+ Self for method chaining
40
+ """
41
+ if count < 1 or count > MAX_RESULTS_LIMIT:
42
+ raise ValueError(f"Limit must be between 1 and {MAX_RESULTS_LIMIT}")
43
+ self._max_results = count
44
+ return self
45
+
46
+ def search(self, query: str) -> "DriveQueryBuilder":
47
+ """
48
+ Add a search term to the query (searches name and content).
49
+ Args:
50
+ query: Search term to add
51
+ Returns:
52
+ Self for method chaining
53
+ """
54
+ if query:
55
+ escaped_query = query.replace("'", "\'\'")
56
+ self._query_parts.append(f"fullText contains '{escaped_query}'")
57
+ return self
58
+
59
+ def name_contains(self, text: str) -> "DriveQueryBuilder":
60
+ """
61
+ Filter files whose name contains the specified text.
62
+ Args:
63
+ text: Text to search for in file names
64
+ Returns:
65
+ Self for method chaining
66
+ """
67
+ if text:
68
+ escaped_text = text.replace("'", "\'\'")
69
+ self._query_parts.append(f"name contains '{escaped_text}'")
70
+ return self
71
+
72
+ def name_equals(self, name: str) -> "DriveQueryBuilder":
73
+ """
74
+ Filter files with the exact name.
75
+ Args:
76
+ name: Exact name to match
77
+ Returns:
78
+ Self for method chaining
79
+ """
80
+ if name:
81
+ escaped_name = name.replace("'", "\'\'")
82
+ self._query_parts.append(f"name = '{escaped_name}'")
83
+ return self
84
+
85
+ def in_folder(self, folder: Union[str, "DriveFolder"]) -> "DriveQueryBuilder":
86
+ """
87
+ Filter files within a specific folder.
88
+ Args:
89
+ folder: DriveFolder object or folder ID string
90
+ Returns:
91
+ Self for method chaining
92
+ """
93
+ if folder:
94
+ # Handle both DriveFolder objects and string IDs for backwards compatibility
95
+ folder_id = folder.folder_id if hasattr(folder, 'folder_id') else folder
96
+ self._query_parts.append(f"'{folder_id}' in parents")
97
+ return self
98
+
99
+ def in_any_folder(self, folders: List[Union[str, "DriveFolder"]]) -> "DriveQueryBuilder":
100
+ """
101
+ Filter files within any of the specified folders.
102
+ Args:
103
+ folders: List of DriveFolder objects or folder ID strings
104
+ Returns:
105
+ Self for method chaining
106
+ """
107
+ if folders:
108
+ folder_ids = []
109
+ for folder in folders:
110
+ folder_id = folder.folder_id if hasattr(folder, 'folder_id') else folder
111
+ folder_ids.append(folder_id)
112
+ folder_conditions = [f"'{folder_id}' in parents" for folder_id in folder_ids]
113
+ combined_condition = " or ".join(folder_conditions)
114
+ self._query_parts.append(f"({combined_condition})")
115
+ return self
116
+
117
+ def not_in_folder(self, folder: Union[str, "DriveFolder"]) -> "DriveQueryBuilder":
118
+ """
119
+ Filter files NOT within a specific folder.
120
+ Args:
121
+ folder: DriveFolder object or folder ID string to exclude
122
+ Returns:
123
+ Self for method chaining
124
+ """
125
+ if folder:
126
+ folder_id = folder.folder_id if hasattr(folder, 'folder_id') else folder
127
+ self._query_parts.append(f"not '{folder_id}' in parents")
128
+ return self
129
+
130
+ def file_type(self, mime_type: str) -> "DriveQueryBuilder":
131
+ """
132
+ Filter files by MIME type.
133
+ Args:
134
+ mime_type: MIME type to filter by
135
+ Returns:
136
+ Self for method chaining
137
+ """
138
+ if mime_type:
139
+ self._query_parts.append(f"mimeType = '{mime_type}'")
140
+ return self
141
+
142
+ def folders_only(self) -> "DriveQueryBuilder":
143
+ """
144
+ Filter to show only folders.
145
+ Returns:
146
+ Self for method chaining
147
+ """
148
+ self._query_parts.append(f"mimeType = '{FOLDER_MIME_TYPE}'")
149
+ return self
150
+
151
+ def files_only(self) -> "DriveQueryBuilder":
152
+ """
153
+ Filter to show only files (exclude folders).
154
+ Returns:
155
+ Self for method chaining
156
+ """
157
+ self._query_parts.append(f"mimeType != '{FOLDER_MIME_TYPE}'")
158
+ return self
159
+
160
+ def folders_named(self, name: str) -> "DriveQueryBuilder":
161
+ """
162
+ Filter to show only folders with a specific name.
163
+ Args:
164
+ name: Folder name to match
165
+ Returns:
166
+ Self for method chaining
167
+ """
168
+ if name:
169
+ escaped_name = name.replace("'", "\'\'")
170
+ self._query_parts.append(f"mimeType = '{FOLDER_MIME_TYPE}' and name = '{escaped_name}'")
171
+ return self
172
+
173
+ def folders_containing(self, text: str) -> "DriveQueryBuilder":
174
+ """
175
+ Filter to show only folders whose name contains specific text.
176
+ Args:
177
+ text: Text to search for in folder names
178
+ Returns:
179
+ Self for method chaining
180
+ """
181
+ if text:
182
+ escaped_text = text.replace("'", "\'\'")
183
+ self._query_parts.append(f"mimeType = '{FOLDER_MIME_TYPE}' and name contains '{escaped_text}'")
184
+ return self
185
+
186
+ def shared_with_me(self) -> "DriveQueryBuilder":
187
+ """
188
+ Filter to show only files shared with the user.
189
+ Returns:
190
+ Self for method chaining
191
+ """
192
+ self._query_parts.append("sharedWithMe = true")
193
+ return self
194
+
195
+ def owned_by_me(self) -> "DriveQueryBuilder":
196
+ """
197
+ Filter to show only files owned by the user.
198
+ Returns:
199
+ Self for method chaining
200
+ """
201
+ self._query_parts.append("'me' in owners")
202
+ return self
203
+
204
+ def starred(self) -> "DriveQueryBuilder":
205
+ """
206
+ Filter to show only starred files.
207
+ Returns:
208
+ Self for method chaining
209
+ """
210
+ self._query_parts.append("starred = true")
211
+ return self
212
+
213
+ def trashed(self, include_trashed: bool = True) -> "DriveQueryBuilder":
214
+ """
215
+ Filter files based on trash status.
216
+ Args:
217
+ include_trashed: If True, show only trashed files. If False, exclude trashed files.
218
+ Returns:
219
+ Self for method chaining
220
+ """
221
+ if include_trashed:
222
+ self._query_parts.append("trashed = true")
223
+ else:
224
+ self._query_parts.append("trashed = false")
225
+ return self
226
+
227
+ def created_after(self, date_time: datetime) -> "DriveQueryBuilder":
228
+ """
229
+ Filter files created after the specified datetime.
230
+ Args:
231
+ date_time: Datetime to filter by
232
+ Returns:
233
+ Self for method chaining
234
+ """
235
+ if date_time:
236
+ iso_date = convert_datetime_to_iso(date_time)
237
+ self._query_parts.append(f"createdTime > '{iso_date}'")
238
+ return self
239
+
240
+ def created_before(self, date_time: datetime) -> "DriveQueryBuilder":
241
+ """
242
+ Filter files created before the specified datetime.
243
+ Args:
244
+ date_time: Datetime to filter by
245
+ Returns:
246
+ Self for method chaining
247
+ """
248
+ if date_time:
249
+ iso_date = convert_datetime_to_iso(date_time)
250
+ self._query_parts.append(f"createdTime < '{iso_date}'")
251
+ return self
252
+
253
+ def modified_after(self, date_time: datetime) -> "DriveQueryBuilder":
254
+ """
255
+ Filter files modified after the specified datetime.
256
+ Args:
257
+ date_time: Datetime to filter by
258
+ Returns:
259
+ Self for method chaining
260
+ """
261
+ if date_time:
262
+ iso_date = convert_datetime_to_iso(date_time)
263
+ self._query_parts.append(f"modifiedTime > '{iso_date}'")
264
+ return self
265
+
266
+ def modified_before(self, date_time: datetime) -> "DriveQueryBuilder":
267
+ """
268
+ Filter files modified before the specified datetime.
269
+ Args:
270
+ date_time: Datetime to filter by
271
+ Returns:
272
+ Self for method chaining
273
+ """
274
+ if date_time:
275
+ iso_date = convert_datetime_to_iso(date_time)
276
+ self._query_parts.append(f"modifiedTime < '{iso_date}'")
277
+ return self
278
+
279
+ def with_extension(self, extension: str) -> "DriveQueryBuilder":
280
+ """
281
+ Filter files by file extension.
282
+ Args:
283
+ extension: File extension (with or without dot)
284
+ Returns:
285
+ Self for method chaining
286
+ """
287
+ if extension:
288
+ # Ensure extension starts with dot
289
+ if not extension.startswith('.'):
290
+ extension = '.' + extension
291
+ self._query_parts.append(f"fileExtension = '{extension[1:]}'")
292
+ return self
293
+
294
+ def custom_query(self, query: str) -> "DriveQueryBuilder":
295
+ """
296
+ Add a custom query string.
297
+ Args:
298
+ query: Custom query string
299
+ Returns:
300
+ Self for method chaining
301
+ """
302
+ if query:
303
+ self._query_parts.append(query)
304
+ return self
305
+
306
+ def order_by(self, field: str, ascending: bool = True) -> "DriveQueryBuilder":
307
+ """
308
+ Set the order of results.
309
+ Args:
310
+ field: Field to order by (name, createdTime, modifiedTime, etc.)
311
+ ascending: If True, order ascending; if False, order descending
312
+ Returns:
313
+ Self for method chaining
314
+ """
315
+ direction = "asc" if ascending else "desc"
316
+ self._order_by = f"{field} {direction}"
317
+ return self
318
+
319
+ def order_by_name(self, ascending: bool = True) -> "DriveQueryBuilder":
320
+ """
321
+ Order results by name.
322
+ Args:
323
+ ascending: If True, order A-Z; if False, order Z-A
324
+ Returns:
325
+ Self for method chaining
326
+ """
327
+ return self.order_by("name", ascending)
328
+
329
+ def order_by_modified_time(self, ascending: bool = False) -> "DriveQueryBuilder":
330
+ """
331
+ Order results by modification time.
332
+ Args:
333
+ ascending: If True, oldest first; if False, newest first (default)
334
+ Returns:
335
+ Self for method chaining
336
+ """
337
+ return self.order_by("modifiedTime", ascending)
338
+
339
+ def order_by_created_time(self, ascending: bool = False) -> "DriveQueryBuilder":
340
+ """
341
+ Order results by creation time.
342
+ Args:
343
+ ascending: If True, oldest first; if False, newest first (default)
344
+ Returns:
345
+ Self for method chaining
346
+ """
347
+ return self.order_by("createdTime", ascending)
348
+
349
+ def fields(self, fields: str) -> "DriveQueryBuilder":
350
+ """
351
+ Set specific fields to retrieve from the API.
352
+ Args:
353
+ fields: Comma-separated list of fields
354
+ Returns:
355
+ Self for method chaining
356
+ """
357
+ self._fields = fields
358
+ return self
359
+
360
+ def _build_query(self) -> str:
361
+ """
362
+ Build the final query string.
363
+ Returns:
364
+ Combined query string
365
+ """
366
+ if not self._query_parts:
367
+ return ""
368
+
369
+ return " and ".join(f"({part})" for part in self._query_parts)
370
+
371
+ def execute(self) -> List["DriveItem"]:
372
+ """
373
+ Execute the query and return results.
374
+ Returns:
375
+ List of DriveItem objects matching the query
376
+ """
377
+ query = self._build_query()
378
+
379
+ return self._api_service.list(
380
+ query=query,
381
+ max_results=self._max_results,
382
+ order_by=self._order_by,
383
+ fields=self._fields,
384
+ page_token=self._page_token
385
+ )