chub-dev 0.1.0 → 0.1.2-beta.0

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 (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1549 @@
1
+ ---
2
+ name: onedrive
3
+ description: "Microsoft OneDrive API coding guidelines for Python using the official Microsoft Graph SDK"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "1.48.0"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "microsoft,onedrive,storage,graph-api,files"
10
+ ---
11
+
12
+ # Microsoft OneDrive API Coding Guidelines (Python)
13
+
14
+ You are a **Microsoft OneDrive API coding expert**. Help me write correct, idiomatic Python code that accesses OneDrive files and folders using the official Microsoft Graph SDK.
15
+
16
+ Use **only official Microsoft sources** for behavior, fields, and constraints. This guide summarizes key patterns for Python applications.
17
+
18
+ > Ground truth: Microsoft Graph OneDrive API documentation on learn.microsoft.com.
19
+
20
+
21
+ ## Golden Rule: Use the Official Microsoft Graph SDK
22
+
23
+ **ALWAYS use `msgraph-sdk` version 1.48.0 or later** for OneDrive operations. This is the official Microsoft Graph Python SDK that provides access to OneDrive, SharePoint, and all other Microsoft Graph APIs.
24
+
25
+ **DO NOT use**:
26
+ - Deprecated `onedrivesdk` package (obsolete)
27
+ - Direct REST calls without the SDK (unless absolutely necessary)
28
+ - Unofficial third-party OneDrive libraries
29
+
30
+ **Install (Python):**
31
+ ```bash
32
+ pip install msgraph-sdk
33
+ pip install azure-identity
34
+ ```
35
+
36
+
37
+ ## Installation
38
+
39
+ ### Complete Setup for Python Applications
40
+
41
+ ```bash
42
+ # Core Microsoft Graph SDK
43
+ pip install msgraph-sdk
44
+
45
+ # Azure authentication library
46
+ pip install azure-identity
47
+
48
+ # For async support (recommended)
49
+ pip install aiohttp
50
+
51
+ # Environment variable management
52
+ pip install python-dotenv
53
+ ```
54
+
55
+ ### Using requirements.txt
56
+
57
+ ```text
58
+ msgraph-sdk>=1.48.0
59
+ azure-identity>=1.19.0
60
+ python-dotenv>=1.0.0
61
+ aiohttp>=3.9.0
62
+ ```
63
+
64
+ ### Using pyproject.toml (Poetry/UV)
65
+
66
+ ```toml
67
+ [project]
68
+ dependencies = [
69
+ "msgraph-sdk>=1.48.0",
70
+ "azure-identity>=1.19.0",
71
+ "python-dotenv>=1.0.0",
72
+ "aiohttp>=3.9.0"
73
+ ]
74
+ ```
75
+
76
+
77
+ ## Authentication
78
+
79
+ OneDrive access through Microsoft Graph requires OAuth 2.0 authentication. You need to register an application in Azure Active Directory (Azure AD) to obtain credentials.
80
+
81
+ ### Azure AD App Registration
82
+
83
+ 1. Go to [Azure Portal](https://portal.azure.com) → Azure Active Directory → App registrations
84
+ 2. Create a new registration
85
+ 3. Note your **Application (client) ID** and **Directory (tenant) ID**
86
+ 4. Create a client secret under "Certificates & secrets"
87
+ 5. Add API permissions: Microsoft Graph → **Files.Read**, **Files.ReadWrite**, **Files.Read.All**, **Files.ReadWrite.All**
88
+ 6. Grant admin consent for the permissions
89
+
90
+ ### Required Scopes
91
+
92
+ Common OneDrive permission scopes:
93
+
94
+ ```python
95
+ # Read-only access to user's files
96
+ SCOPES_READONLY = ['https://graph.microsoft.com/Files.Read']
97
+
98
+ # Read/write access to user's files
99
+ SCOPES_READWRITE = ['https://graph.microsoft.com/Files.ReadWrite']
100
+
101
+ # Read all files user can access (including shared)
102
+ SCOPES_READ_ALL = ['https://graph.microsoft.com/Files.Read.All']
103
+
104
+ # Full access to all files user can access
105
+ SCOPES_READWRITE_ALL = ['https://graph.microsoft.com/Files.ReadWrite.All']
106
+
107
+ # Application permissions (no user context, requires admin consent)
108
+ SCOPES_APP = ['https://graph.microsoft.com/.default']
109
+ ```
110
+
111
+
112
+ ## Initialization
113
+
114
+ ### Async Client with Client Credentials (Service/Daemon Apps)
115
+
116
+ For server-side applications using application permissions:
117
+
118
+ ```python
119
+ import asyncio
120
+ from azure.identity.aio import ClientSecretCredential
121
+ from msgraph import GraphServiceClient
122
+ from msgraph.generated.users.item.user_item_request_builder import UserItemRequestBuilder
123
+ import os
124
+ from dotenv import load_dotenv
125
+
126
+ load_dotenv()
127
+
128
+ # Environment variables
129
+ tenant_id = os.getenv('AZURE_TENANT_ID')
130
+ client_id = os.getenv('AZURE_CLIENT_ID')
131
+ client_secret = os.getenv('AZURE_CLIENT_SECRET')
132
+
133
+ # Create credential
134
+ credentials = ClientSecretCredential(
135
+ tenant_id=tenant_id,
136
+ client_id=client_id,
137
+ client_secret=client_secret
138
+ )
139
+
140
+ # Scopes for application permissions
141
+ scopes = ['https://graph.microsoft.com/.default']
142
+
143
+ # Initialize Microsoft Graph client
144
+ client = GraphServiceClient(credentials=credentials, scopes=scopes)
145
+
146
+ # Example: List files in user's OneDrive root
147
+ async def list_files(user_id: str):
148
+ result = await client.users.by_user_id(user_id).drive.root.children.get()
149
+ return result.value
150
+
151
+ async def main():
152
+ user_id = os.getenv('USER_ID')
153
+ files = await list_files(user_id)
154
+
155
+ for file in files:
156
+ print(f"{file.name} - {file.size} bytes")
157
+
158
+ if __name__ == '__main__':
159
+ asyncio.run(main())
160
+ ```
161
+
162
+ ### Sync Client with Client Credentials
163
+
164
+ For synchronous applications:
165
+
166
+ ```python
167
+ from azure.identity import ClientSecretCredential
168
+ from msgraph import GraphServiceClient
169
+ import os
170
+
171
+ credentials = ClientSecretCredential(
172
+ tenant_id=os.getenv('AZURE_TENANT_ID'),
173
+ client_id=os.getenv('AZURE_CLIENT_ID'),
174
+ client_secret=os.getenv('AZURE_CLIENT_SECRET')
175
+ )
176
+
177
+ scopes = ['https://graph.microsoft.com/.default']
178
+ client = GraphServiceClient(credentials=credentials, scopes=scopes)
179
+
180
+ # Use sync methods
181
+ def list_files_sync(user_id: str):
182
+ # Note: The SDK is primarily async, synchronous usage requires running in event loop
183
+ import asyncio
184
+ loop = asyncio.get_event_loop()
185
+ result = loop.run_until_complete(
186
+ client.users.by_user_id(user_id).drive.root.children.get()
187
+ )
188
+ return result.value
189
+ ```
190
+
191
+ ### Async Client with Device Code Flow (Interactive)
192
+
193
+ For CLI applications that need user interaction:
194
+
195
+ ```python
196
+ from azure.identity.aio import DeviceCodeCredential
197
+ from msgraph import GraphServiceClient
198
+ import os
199
+
200
+ async def init_graph_client():
201
+ credentials = DeviceCodeCredential(
202
+ client_id=os.getenv('AZURE_CLIENT_ID'),
203
+ tenant_id=os.getenv('AZURE_TENANT_ID')
204
+ )
205
+
206
+ scopes = ['Files.ReadWrite', 'Files.Read.All']
207
+ client = GraphServiceClient(credentials=credentials, scopes=scopes)
208
+
209
+ return client
210
+
211
+ async def main():
212
+ client = await init_graph_client()
213
+
214
+ # List files in current user's drive
215
+ result = await client.me.drive.root.children.get()
216
+
217
+ for item in result.value:
218
+ print(f"{item.name}")
219
+
220
+ asyncio.run(main())
221
+ ```
222
+
223
+ ### Async Client with Interactive Browser
224
+
225
+ For desktop applications with interactive login:
226
+
227
+ ```python
228
+ from azure.identity.aio import InteractiveBrowserCredential
229
+ from msgraph import GraphServiceClient
230
+ import os
231
+
232
+ async def init_graph_client():
233
+ credentials = InteractiveBrowserCredential(
234
+ client_id=os.getenv('AZURE_CLIENT_ID'),
235
+ tenant_id=os.getenv('AZURE_TENANT_ID')
236
+ )
237
+
238
+ scopes = ['Files.ReadWrite']
239
+ client = GraphServiceClient(credentials=credentials, scopes=scopes)
240
+
241
+ return client
242
+ ```
243
+
244
+ ### Environment Variables Setup
245
+
246
+ Create a `.env` file:
247
+
248
+ ```bash
249
+ AZURE_TENANT_ID=your-tenant-id
250
+ AZURE_CLIENT_ID=your-client-id
251
+ AZURE_CLIENT_SECRET=your-client-secret
252
+ USER_ID=user@domain.com
253
+ ```
254
+
255
+ Load environment variables:
256
+
257
+ ```python
258
+ from dotenv import load_dotenv
259
+ import os
260
+
261
+ load_dotenv()
262
+
263
+ tenant_id = os.getenv('AZURE_TENANT_ID')
264
+ client_id = os.getenv('AZURE_CLIENT_ID')
265
+ client_secret = os.getenv('AZURE_CLIENT_SECRET')
266
+ ```
267
+
268
+
269
+ ## Core API Surfaces
270
+
271
+
272
+ ### 1. Listing Files and Folders
273
+
274
+ #### List Root Folder Contents
275
+
276
+ ```python
277
+ from msgraph import GraphServiceClient
278
+
279
+ async def list_root_files(client: GraphServiceClient):
280
+ # Current user's OneDrive root
281
+ result = await client.me.drive.root.children.get()
282
+
283
+ if result and result.value:
284
+ for item in result.value:
285
+ item_type = "Folder" if item.folder else "File"
286
+ print(f"{item_type}: {item.name} (ID: {item.id})")
287
+
288
+ return result.value
289
+ ```
290
+
291
+ #### List Specific Folder Contents
292
+
293
+ ```python
294
+ # By item ID
295
+ async def list_folder_by_id(client: GraphServiceClient, item_id: str):
296
+ result = await client.me.drive.items.by_drive_item_id(item_id).children.get()
297
+ return result.value
298
+
299
+ # By path
300
+ async def list_folder_by_path(client: GraphServiceClient, folder_path: str):
301
+ # Example: /Documents/Projects
302
+ result = await client.me.drive.root.item_with_path(folder_path).children.get()
303
+ return result.value
304
+
305
+ # With pagination
306
+ async def list_all_items_paginated(client: GraphServiceClient):
307
+ items = []
308
+ result = await client.me.drive.root.children.get()
309
+
310
+ while result:
311
+ if result.value:
312
+ items.extend(result.value)
313
+
314
+ # Check for next page
315
+ if hasattr(result, 'odata_next_link') and result.odata_next_link:
316
+ # Fetch next page
317
+ result = await client.me.drive.root.children.get()
318
+ else:
319
+ break
320
+
321
+ return items
322
+ ```
323
+
324
+ #### Advanced Listing with Query Parameters
325
+
326
+ ```python
327
+ from msgraph.generated.users.item.drive.root.children.children_request_builder import ChildrenRequestBuilder
328
+
329
+ async def list_with_filters(client: GraphServiceClient):
330
+ # Configure request
331
+ query_params = ChildrenRequestBuilder.ChildrenRequestBuilderGetQueryParameters(
332
+ select=['id', 'name', 'size', 'created_date_time', 'last_modified_date_time'],
333
+ filter='file ne null', # Only files
334
+ orderby=['name asc'],
335
+ top=10
336
+ )
337
+
338
+ request_config = ChildrenRequestBuilder.ChildrenRequestBuilderGetRequestConfiguration(
339
+ query_parameters=query_params
340
+ )
341
+
342
+ result = await client.me.drive.root.children.get(request_configuration=request_config)
343
+ return result.value
344
+
345
+ # Filter folders only
346
+ async def list_folders_only(client: GraphServiceClient):
347
+ query_params = ChildrenRequestBuilder.ChildrenRequestBuilderGetQueryParameters(
348
+ filter='folder ne null'
349
+ )
350
+
351
+ request_config = ChildrenRequestBuilder.ChildrenRequestBuilderGetRequestConfiguration(
352
+ query_parameters=query_params
353
+ )
354
+
355
+ result = await client.me.drive.root.children.get(request_configuration=request_config)
356
+ return result.value
357
+
358
+ # Order by last modified date
359
+ async def list_by_modified_date(client: GraphServiceClient):
360
+ query_params = ChildrenRequestBuilder.ChildrenRequestBuilderGetQueryParameters(
361
+ orderby=['lastModifiedDateTime desc'],
362
+ top=20
363
+ )
364
+
365
+ request_config = ChildrenRequestBuilder.ChildrenRequestBuilderGetRequestConfiguration(
366
+ query_parameters=query_params
367
+ )
368
+
369
+ result = await client.me.drive.root.children.get(request_configuration=request_config)
370
+ return result.value
371
+ ```
372
+
373
+ #### List All Drives
374
+
375
+ ```python
376
+ # List all drives accessible to user
377
+ async def list_drives(client: GraphServiceClient):
378
+ drives = await client.me.drives.get()
379
+
380
+ if drives and drives.value:
381
+ for drive in drives.value:
382
+ print(f"Drive: {drive.name} (ID: {drive.id})")
383
+ print(f" Type: {drive.drive_type}")
384
+ print(f" Owner: {drive.owner.user.display_name if drive.owner else 'N/A'}")
385
+
386
+ return drives.value
387
+
388
+ # Get default drive
389
+ async def get_default_drive(client: GraphServiceClient):
390
+ drive = await client.me.drive.get()
391
+ return drive
392
+
393
+ # Get specific drive
394
+ async def get_drive_by_id(client: GraphServiceClient, drive_id: str):
395
+ drive = await client.drives.by_drive_id(drive_id).get()
396
+ return drive
397
+ ```
398
+
399
+
400
+ ### 2. Getting File/Folder Metadata
401
+
402
+ #### Get Item Metadata
403
+
404
+ ```python
405
+ # By item ID
406
+ async def get_item_metadata(client: GraphServiceClient, item_id: str):
407
+ item = await client.me.drive.items.by_drive_item_id(item_id).get()
408
+
409
+ print(f"ID: {item.id}")
410
+ print(f"Name: {item.name}")
411
+ print(f"Size: {item.size} bytes")
412
+ print(f"Created: {item.created_date_time}")
413
+ print(f"Modified: {item.last_modified_date_time}")
414
+ print(f"Web URL: {item.web_url}")
415
+
416
+ if item.file:
417
+ print(f"MIME Type: {item.file.mime_type}")
418
+ if item.file.hashes:
419
+ print(f"SHA1 Hash: {item.file.hashes.sha1_hash}")
420
+
421
+ return item
422
+
423
+ # By path
424
+ async def get_item_by_path(client: GraphServiceClient, file_path: str):
425
+ # Example: /Documents/report.pdf
426
+ item = await client.me.drive.root.item_with_path(file_path).get()
427
+ return item
428
+ ```
429
+
430
+ #### Get File with Specific Properties
431
+
432
+ ```python
433
+ from msgraph.generated.users.item.drive.items.item.drive_item_item_request_builder import DriveItemItemRequestBuilder
434
+
435
+ async def get_item_with_select(client: GraphServiceClient, item_id: str):
436
+ query_params = DriveItemItemRequestBuilder.DriveItemItemRequestBuilderGetQueryParameters(
437
+ select=['id', 'name', 'size', 'file', 'createdDateTime']
438
+ )
439
+
440
+ request_config = DriveItemItemRequestBuilder.DriveItemItemRequestBuilderGetRequestConfiguration(
441
+ query_parameters=query_params
442
+ )
443
+
444
+ item = await client.me.drive.items.by_drive_item_id(item_id).get(
445
+ request_configuration=request_config
446
+ )
447
+
448
+ return item
449
+ ```
450
+
451
+
452
+ ### 3. Downloading Files
453
+
454
+ #### Simple Download (All File Sizes)
455
+
456
+ ```python
457
+ import aiofiles
458
+
459
+ async def download_file(client: GraphServiceClient, item_id: str, save_path: str):
460
+ # Get file content as stream
461
+ stream = await client.me.drive.items.by_drive_item_id(item_id).content.get()
462
+
463
+ # Save to disk
464
+ async with aiofiles.open(save_path, 'wb') as f:
465
+ await f.write(stream)
466
+
467
+ print(f"Downloaded to {save_path}")
468
+
469
+ # Download by path
470
+ async def download_file_by_path(client: GraphServiceClient, file_path: str, save_path: str):
471
+ # Example: /Documents/report.pdf
472
+ stream = await client.me.drive.root.item_with_path(file_path).content.get()
473
+
474
+ async with aiofiles.open(save_path, 'wb') as f:
475
+ await f.write(stream)
476
+ ```
477
+
478
+ #### Download Multiple Files
479
+
480
+ ```python
481
+ import os
482
+ import asyncio
483
+
484
+ async def download_folder(client: GraphServiceClient, folder_id: str, local_path: str):
485
+ # Create local directory
486
+ os.makedirs(local_path, exist_ok=True)
487
+
488
+ # Get folder contents
489
+ items = await client.me.drive.items.by_drive_item_id(folder_id).children.get()
490
+
491
+ if not items or not items.value:
492
+ return
493
+
494
+ for item in items.value:
495
+ if item.folder:
496
+ # Recursively download subfolder
497
+ subfolder_path = os.path.join(local_path, item.name)
498
+ await download_folder(client, item.id, subfolder_path)
499
+ elif item.file:
500
+ # Download file
501
+ print(f"Downloading {item.name}...")
502
+ file_path = os.path.join(local_path, item.name)
503
+
504
+ stream = await client.me.drive.items.by_drive_item_id(item.id).content.get()
505
+
506
+ async with aiofiles.open(file_path, 'wb') as f:
507
+ await f.write(stream)
508
+
509
+ print(f"Downloaded {item.name}")
510
+ ```
511
+
512
+
513
+ ### 4. Uploading Files
514
+
515
+ #### Simple Upload (Files < 4MB)
516
+
517
+ ```python
518
+ async def upload_small_file(client: GraphServiceClient, file_path: str, upload_path: str):
519
+ """
520
+ Upload a file smaller than 4MB
521
+
522
+ Args:
523
+ file_path: Local file path
524
+ upload_path: OneDrive path (e.g., '/Documents/file.pdf')
525
+ """
526
+ async with aiofiles.open(file_path, 'rb') as f:
527
+ content = await f.read()
528
+
529
+ # Upload to specific path
530
+ item = await client.me.drive.root.item_with_path(upload_path).content.put(content)
531
+
532
+ print(f"Uploaded: {item.name} (ID: {item.id})")
533
+ return item
534
+
535
+ # Upload to specific folder by ID
536
+ async def upload_to_folder(client: GraphServiceClient, file_path: str, folder_id: str, filename: str):
537
+ async with aiofiles.open(file_path, 'rb') as f:
538
+ content = await f.read()
539
+
540
+ item = await client.me.drive.items.by_drive_item_id(folder_id).item_with_path(filename).content.put(content)
541
+
542
+ return item
543
+ ```
544
+
545
+ #### Large File Upload (Files > 4MB) - Resumable Upload Session
546
+
547
+ ```python
548
+ import math
549
+ import aiohttp
550
+
551
+ async def upload_large_file(client: GraphServiceClient, file_path: str, upload_path: str):
552
+ """
553
+ Upload large files using resumable upload session
554
+
555
+ Args:
556
+ file_path: Local file path
557
+ upload_path: OneDrive path (e.g., '/Documents/video.mp4')
558
+ """
559
+ import os
560
+ from msgraph.generated.drives.item.items.item.create_upload_session.create_upload_session_post_request_body import CreateUploadSessionPostRequestBody
561
+ from msgraph.generated.models.drive_item_uploadable_properties import DriveItemUploadableProperties
562
+
563
+ file_size = os.path.getsize(file_path)
564
+
565
+ # Step 1: Create upload session
566
+ props = DriveItemUploadableProperties()
567
+ props.odata_type = "#microsoft.graph.driveItemUploadableProperties"
568
+ props.microsoft_graph_conflict_behavior = "rename" # or 'fail', 'replace'
569
+ props.name = os.path.basename(file_path)
570
+
571
+ request_body = CreateUploadSessionPostRequestBody()
572
+ request_body.item = props
573
+
574
+ upload_session = await client.me.drive.root.item_with_path(upload_path).create_upload_session.post(request_body)
575
+
576
+ # Step 2: Upload file in chunks
577
+ chunk_size = 320 * 1024 * 10 # 3.2 MB chunks (must be multiple of 320 KB)
578
+
579
+ async with aiofiles.open(file_path, 'rb') as f:
580
+ file_content = await f.read()
581
+
582
+ num_chunks = math.ceil(file_size / chunk_size)
583
+
584
+ for i in range(num_chunks):
585
+ start = i * chunk_size
586
+ end = min(start + chunk_size, file_size)
587
+ chunk = file_content[start:end]
588
+
589
+ content_range = f"bytes {start}-{end-1}/{file_size}"
590
+
591
+ headers = {
592
+ 'Content-Length': str(len(chunk)),
593
+ 'Content-Range': content_range
594
+ }
595
+
596
+ # Upload chunk
597
+ async with aiohttp.ClientSession() as session:
598
+ async with session.put(
599
+ upload_session.upload_url,
600
+ data=chunk,
601
+ headers=headers
602
+ ) as response:
603
+ if response.status in [200, 201, 202]:
604
+ progress = (end / file_size) * 100
605
+ print(f"Upload progress: {progress:.2f}%")
606
+ else:
607
+ error_text = await response.text()
608
+ raise Exception(f"Upload failed: {error_text}")
609
+
610
+ print("Upload complete!")
611
+ return upload_session
612
+
613
+ # Alternative: Using helper function for large uploads
614
+ async def upload_large_file_simple(client: GraphServiceClient, file_path: str, folder_id: str):
615
+ """
616
+ Simplified large file upload
617
+ """
618
+ import os
619
+ from msgraph.generated.drives.item.items.item.create_upload_session.create_upload_session_post_request_body import CreateUploadSessionPostRequestBody
620
+ from msgraph.generated.models.drive_item_uploadable_properties import DriveItemUploadableProperties
621
+
622
+ filename = os.path.basename(file_path)
623
+ file_size = os.path.getsize(file_path)
624
+
625
+ # Create upload session
626
+ props = DriveItemUploadableProperties()
627
+ props.microsoft_graph_conflict_behavior = "replace"
628
+ props.name = filename
629
+
630
+ request_body = CreateUploadSessionPostRequestBody()
631
+ request_body.item = props
632
+
633
+ upload_session = await client.me.drive.items.by_drive_item_id(folder_id).item_with_path(filename).create_upload_session.post(request_body)
634
+
635
+ # Upload in chunks
636
+ chunk_size = 10 * 1024 * 1024 # 10 MB
637
+
638
+ async with aiofiles.open(file_path, 'rb') as f:
639
+ offset = 0
640
+
641
+ while offset < file_size:
642
+ chunk = await f.read(chunk_size)
643
+ chunk_len = len(chunk)
644
+
645
+ content_range = f"bytes {offset}-{offset + chunk_len - 1}/{file_size}"
646
+
647
+ headers = {
648
+ 'Content-Length': str(chunk_len),
649
+ 'Content-Range': content_range
650
+ }
651
+
652
+ async with aiohttp.ClientSession() as session:
653
+ async with session.put(
654
+ upload_session.upload_url,
655
+ data=chunk,
656
+ headers=headers
657
+ ) as response:
658
+ if response.status not in [200, 201, 202]:
659
+ raise Exception(f"Upload failed: {await response.text()}")
660
+
661
+ offset += chunk_len
662
+ progress = (offset / file_size) * 100
663
+ print(f"Uploaded: {progress:.1f}%")
664
+
665
+ print("Upload complete!")
666
+ ```
667
+
668
+
669
+ ### 5. Creating Folders
670
+
671
+ ```python
672
+ from msgraph.generated.models.drive_item import DriveItem
673
+ from msgraph.generated.models.folder import Folder
674
+
675
+ async def create_folder(client: GraphServiceClient, folder_name: str, parent_id: str = None):
676
+ """
677
+ Create a new folder
678
+
679
+ Args:
680
+ folder_name: Name of the folder to create
681
+ parent_id: Parent folder ID (None for root)
682
+ """
683
+ drive_item = DriveItem()
684
+ drive_item.name = folder_name
685
+ drive_item.folder = Folder()
686
+ drive_item.microsoft_graph_conflict_behavior = "rename" # or 'fail', 'replace'
687
+
688
+ if parent_id:
689
+ folder = await client.me.drive.items.by_drive_item_id(parent_id).children.post(drive_item)
690
+ else:
691
+ folder = await client.me.drive.root.children.post(drive_item)
692
+
693
+ print(f"Created folder: {folder.name} (ID: {folder.id})")
694
+ return folder
695
+
696
+ # Create folder at specific path
697
+ async def create_folder_by_path(client: GraphServiceClient, folder_path: str):
698
+ """
699
+ Create folder at path (e.g., '/Documents/Projects/New Folder')
700
+ """
701
+ drive_item = DriveItem()
702
+ drive_item.name = os.path.basename(folder_path)
703
+ drive_item.folder = Folder()
704
+ drive_item.microsoft_graph_conflict_behavior = "fail"
705
+
706
+ parent_path = os.path.dirname(folder_path)
707
+
708
+ if parent_path and parent_path != '/':
709
+ folder = await client.me.drive.root.item_with_path(parent_path).children.post(drive_item)
710
+ else:
711
+ folder = await client.me.drive.root.children.post(drive_item)
712
+
713
+ return folder
714
+
715
+ # Create nested folder structure
716
+ async def create_nested_folders(client: GraphServiceClient, path_parts: list):
717
+ """
718
+ Create nested folders
719
+
720
+ Args:
721
+ path_parts: List of folder names ['Parent', 'Child', 'Grandchild']
722
+ """
723
+ parent_id = None
724
+
725
+ for folder_name in path_parts:
726
+ folder = await create_folder(client, folder_name, parent_id)
727
+ parent_id = folder.id
728
+
729
+ return parent_id
730
+ ```
731
+
732
+
733
+ ### 6. Searching Files
734
+
735
+ ```python
736
+ from msgraph.generated.users.item.drive.root.search_with_q.search_with_q_request_builder import SearchWithQRequestBuilder
737
+
738
+ async def search_files(client: GraphServiceClient, query: str):
739
+ """
740
+ Search for files and folders
741
+
742
+ Args:
743
+ query: Search query string
744
+ """
745
+ result = await client.me.drive.root.search_with_q(query).get()
746
+
747
+ if result and result.value:
748
+ for item in result.value:
749
+ item_type = "Folder" if item.folder else "File"
750
+ print(f"{item_type}: {item.name} - {item.web_url}")
751
+
752
+ return result.value
753
+
754
+ # Search in specific folder
755
+ async def search_in_folder(client: GraphServiceClient, folder_id: str, query: str):
756
+ result = await client.me.drive.items.by_drive_item_id(folder_id).search_with_q(query).get()
757
+ return result.value
758
+
759
+ # Search with filters
760
+ async def search_pdfs(client: GraphServiceClient):
761
+ """Search for PDF files only"""
762
+ result = await client.me.drive.root.search_with_q('.pdf').get()
763
+
764
+ # Additional filtering in code
765
+ pdf_files = [item for item in result.value if item.file and item.name.lower().endswith('.pdf')]
766
+
767
+ return pdf_files
768
+
769
+ # Advanced search with query parameters
770
+ async def advanced_search(client: GraphServiceClient, query: str):
771
+ query_params = SearchWithQRequestBuilder.SearchWithQRequestBuilderGetQueryParameters(
772
+ select=['id', 'name', 'size', 'webUrl'],
773
+ top=10
774
+ )
775
+
776
+ request_config = SearchWithQRequestBuilder.SearchWithQRequestBuilderGetRequestConfiguration(
777
+ query_parameters=query_params
778
+ )
779
+
780
+ result = await client.me.drive.root.search_with_q(query).get(request_configuration=request_config)
781
+ return result.value
782
+ ```
783
+
784
+
785
+ ### 7. Updating/Renaming Files and Folders
786
+
787
+ ```python
788
+ from msgraph.generated.models.drive_item import DriveItem
789
+ from msgraph.generated.models.item_reference import ItemReference
790
+
791
+ async def rename_item(client: GraphServiceClient, item_id: str, new_name: str):
792
+ """Rename a file or folder"""
793
+ drive_item = DriveItem()
794
+ drive_item.name = new_name
795
+
796
+ updated = await client.me.drive.items.by_drive_item_id(item_id).patch(drive_item)
797
+
798
+ print(f"Renamed to: {updated.name}")
799
+ return updated
800
+
801
+ # Update file metadata
802
+ async def update_metadata(client: GraphServiceClient, item_id: str, description: str):
803
+ drive_item = DriveItem()
804
+ drive_item.description = description
805
+
806
+ updated = await client.me.drive.items.by_drive_item_id(item_id).patch(drive_item)
807
+ return updated
808
+
809
+ # Move file to different folder
810
+ async def move_item(client: GraphServiceClient, item_id: str, new_parent_id: str):
811
+ """Move file or folder to new location"""
812
+ drive_item = DriveItem()
813
+
814
+ parent_ref = ItemReference()
815
+ parent_ref.id = new_parent_id
816
+ drive_item.parent_reference = parent_ref
817
+
818
+ moved = await client.me.drive.items.by_drive_item_id(item_id).patch(drive_item)
819
+
820
+ print(f"Moved {moved.name} to new location")
821
+ return moved
822
+
823
+ # Move and rename simultaneously
824
+ async def move_and_rename(client: GraphServiceClient, item_id: str, new_parent_id: str, new_name: str):
825
+ drive_item = DriveItem()
826
+ drive_item.name = new_name
827
+
828
+ parent_ref = ItemReference()
829
+ parent_ref.id = new_parent_id
830
+ drive_item.parent_reference = parent_ref
831
+
832
+ updated = await client.me.drive.items.by_drive_item_id(item_id).patch(drive_item)
833
+ return updated
834
+ ```
835
+
836
+
837
+ ### 8. Copying Files
838
+
839
+ ```python
840
+ import asyncio
841
+ from msgraph.generated.drives.item.items.item.copy.copy_post_request_body import CopyPostRequestBody
842
+ from msgraph.generated.models.item_reference import ItemReference
843
+
844
+ async def copy_file(client: GraphServiceClient, item_id: str, destination_folder_id: str, new_name: str = None):
845
+ """
846
+ Copy file to another location
847
+
848
+ Args:
849
+ item_id: Source file ID
850
+ destination_folder_id: Destination folder ID
851
+ new_name: Optional new name for copied file
852
+ """
853
+ request_body = CopyPostRequestBody()
854
+
855
+ parent_ref = ItemReference()
856
+ parent_ref.id = destination_folder_id
857
+ request_body.parent_reference = parent_ref
858
+
859
+ if new_name:
860
+ request_body.name = new_name
861
+
862
+ # Initiate copy operation
863
+ await client.me.drive.items.by_drive_item_id(item_id).copy.post(request_body)
864
+
865
+ # Copy is async operation; monitor using returned location header if needed
866
+ print(f"Copy operation initiated")
867
+
868
+ # Wait a bit for copy to complete
869
+ await asyncio.sleep(2)
870
+ ```
871
+
872
+
873
+ ### 9. Deleting Files and Folders
874
+
875
+ ```python
876
+ async def delete_item(client: GraphServiceClient, item_id: str):
877
+ """Delete a file or folder"""
878
+ await client.me.drive.items.by_drive_item_id(item_id).delete()
879
+ print(f"Deleted item: {item_id}")
880
+
881
+ # Delete by path
882
+ async def delete_by_path(client: GraphServiceClient, item_path: str):
883
+ """
884
+ Delete file or folder by path
885
+
886
+ Args:
887
+ item_path: Path like '/Documents/old-file.pdf'
888
+ """
889
+ await client.me.drive.root.item_with_path(item_path).delete()
890
+ print(f"Deleted: {item_path}")
891
+
892
+ # Safe delete with confirmation
893
+ async def safe_delete(client: GraphServiceClient, item_id: str):
894
+ """Delete with metadata check first"""
895
+ # Get item info first
896
+ item = await client.me.drive.items.by_drive_item_id(item_id).get()
897
+
898
+ print(f"About to delete: {item.name}")
899
+ print(f"Size: {item.size} bytes")
900
+ print(f"Modified: {item.last_modified_date_time}")
901
+
902
+ # Confirm and delete
903
+ await client.me.drive.items.by_drive_item_id(item_id).delete()
904
+ print("Deleted successfully")
905
+ ```
906
+
907
+
908
+ ### 10. Sharing and Permissions
909
+
910
+ #### Create Sharing Link
911
+
912
+ ```python
913
+ from msgraph.generated.drives.item.items.item.create_link.create_link_post_request_body import CreateLinkPostRequestBody
914
+
915
+ async def create_share_link(client: GraphServiceClient, item_id: str, link_type: str = "view", scope: str = "anonymous"):
916
+ """
917
+ Create a sharing link
918
+
919
+ Args:
920
+ item_id: File or folder ID
921
+ link_type: 'view', 'edit', or 'embed'
922
+ scope: 'anonymous' or 'organization'
923
+ """
924
+ request_body = CreateLinkPostRequestBody()
925
+ request_body.type = link_type
926
+ request_body.scope = scope
927
+
928
+ permission = await client.me.drive.items.by_drive_item_id(item_id).create_link.post(request_body)
929
+
930
+ print(f"Share link: {permission.link.web_url}")
931
+ return permission.link.web_url
932
+
933
+ # Create link with expiration
934
+ async def create_expiring_link(client: GraphServiceClient, item_id: str, expiration_date: str):
935
+ """
936
+ Create link with expiration
937
+
938
+ Args:
939
+ expiration_date: ISO 8601 format like '2025-12-31T23:59:59Z'
940
+ """
941
+ request_body = CreateLinkPostRequestBody()
942
+ request_body.type = "view"
943
+ request_body.scope = "anonymous"
944
+ request_body.expiration_date_time = expiration_date
945
+
946
+ permission = await client.me.drive.items.by_drive_item_id(item_id).create_link.post(request_body)
947
+ return permission.link.web_url
948
+
949
+ # Create password-protected link
950
+ async def create_protected_link(client: GraphServiceClient, item_id: str, password: str):
951
+ request_body = CreateLinkPostRequestBody()
952
+ request_body.type = "view"
953
+ request_body.scope = "anonymous"
954
+ request_body.password = password
955
+
956
+ permission = await client.me.drive.items.by_drive_item_id(item_id).create_link.post(request_body)
957
+ return permission.link.web_url
958
+ ```
959
+
960
+ #### Grant Permissions to Specific Users
961
+
962
+ ```python
963
+ from msgraph.generated.drives.item.items.item.invite.invite_post_request_body import InvitePostRequestBody
964
+ from msgraph.generated.models.drive_recipient import DriveRecipient
965
+
966
+ async def invite_users(client: GraphServiceClient, item_id: str, email_addresses: list, role: str = "read"):
967
+ """
968
+ Invite users to access a file
969
+
970
+ Args:
971
+ item_id: File or folder ID
972
+ email_addresses: List of email addresses
973
+ role: 'read' or 'write'
974
+ """
975
+ request_body = InvitePostRequestBody()
976
+ request_body.require_sign_in = True
977
+ request_body.send_invitation = True
978
+ request_body.roles = [role]
979
+ request_body.message = "I've shared a file with you"
980
+
981
+ recipients = []
982
+ for email in email_addresses:
983
+ recipient = DriveRecipient()
984
+ recipient.email = email
985
+ recipients.append(recipient)
986
+
987
+ request_body.recipients = recipients
988
+
989
+ permissions = await client.me.drive.items.by_drive_item_id(item_id).invite.post(request_body)
990
+
991
+ print(f"Invited {len(email_addresses)} users")
992
+ return permissions
993
+ ```
994
+
995
+ #### List Permissions
996
+
997
+ ```python
998
+ async def list_permissions(client: GraphServiceClient, item_id: str):
999
+ """Get all permissions for an item"""
1000
+ permissions = await client.me.drive.items.by_drive_item_id(item_id).permissions.get()
1001
+
1002
+ if permissions and permissions.value:
1003
+ for perm in permissions.value:
1004
+ print(f"Permission ID: {perm.id}")
1005
+ print(f"Roles: {perm.roles}")
1006
+
1007
+ if perm.granted_to_v2:
1008
+ print(f"Granted to: {perm.granted_to_v2.user.display_name}")
1009
+
1010
+ if perm.link:
1011
+ print(f"Link: {perm.link.web_url}")
1012
+
1013
+ print("---")
1014
+
1015
+ return permissions.value
1016
+
1017
+ # Remove permission
1018
+ async def remove_permission(client: GraphServiceClient, item_id: str, permission_id: str):
1019
+ """Delete a specific permission"""
1020
+ await client.me.drive.items.by_drive_item_id(item_id).permissions.by_permission_id(permission_id).delete()
1021
+ print(f"Removed permission: {permission_id}")
1022
+ ```
1023
+
1024
+ #### List Files Shared With Me
1025
+
1026
+ ```python
1027
+ async def list_shared_with_me(client: GraphServiceClient):
1028
+ """Get files shared with the current user"""
1029
+ shared_items = await client.me.drive.shared_with_me.get()
1030
+
1031
+ if shared_items and shared_items.value:
1032
+ for item in shared_items.value:
1033
+ print(f"Shared file: {item.name}")
1034
+ if item.remote_item:
1035
+ print(f" Size: {item.remote_item.size}")
1036
+ if item.remote_item.created_by:
1037
+ print(f" Owner: {item.remote_item.created_by.user.display_name}")
1038
+
1039
+ return shared_items.value
1040
+ ```
1041
+
1042
+
1043
+ ### 11. Thumbnails
1044
+
1045
+ ```python
1046
+ async def get_thumbnails(client: GraphServiceClient, item_id: str):
1047
+ """Get thumbnails for an item"""
1048
+ thumbnails = await client.me.drive.items.by_drive_item_id(item_id).thumbnails.get()
1049
+
1050
+ if thumbnails and thumbnails.value:
1051
+ thumb_set = thumbnails.value[0]
1052
+
1053
+ if thumb_set.small:
1054
+ print(f"Small: {thumb_set.small.url}")
1055
+ if thumb_set.medium:
1056
+ print(f"Medium: {thumb_set.medium.url}")
1057
+ if thumb_set.large:
1058
+ print(f"Large: {thumb_set.large.url}")
1059
+
1060
+ return thumbnails.value
1061
+
1062
+ # Get specific thumbnail size
1063
+ async def get_medium_thumbnail(client: GraphServiceClient, item_id: str):
1064
+ thumbnail = await client.me.drive.items.by_drive_item_id(item_id).thumbnails.by_thumbnail_set_id("0").medium.get()
1065
+
1066
+ if thumbnail:
1067
+ print(f"Thumbnail URL: {thumbnail.url}")
1068
+ print(f"Size: {thumbnail.width}x{thumbnail.height}")
1069
+
1070
+ return thumbnail
1071
+ ```
1072
+
1073
+
1074
+ ### 12. Delta (Change Tracking)
1075
+
1076
+ ```python
1077
+ async def get_initial_delta(client: GraphServiceClient):
1078
+ """Get initial delta token and all items"""
1079
+ all_items = []
1080
+ delta_link = None
1081
+
1082
+ result = await client.me.drive.root.delta.get()
1083
+
1084
+ while result:
1085
+ if result.value:
1086
+ all_items.extend(result.value)
1087
+
1088
+ # Check for next page or delta link
1089
+ if hasattr(result, 'odata_next_link') and result.odata_next_link:
1090
+ # More pages to fetch
1091
+ result = await client.me.drive.root.delta.get()
1092
+ elif hasattr(result, 'odata_delta_link') and result.odata_delta_link:
1093
+ # Save delta link for future syncs
1094
+ delta_link = result.odata_delta_link
1095
+ break
1096
+ else:
1097
+ break
1098
+
1099
+ print(f"Initial sync: {len(all_items)} items")
1100
+ print(f"Delta link: {delta_link}")
1101
+
1102
+ return all_items, delta_link
1103
+
1104
+ # Get changes since last sync
1105
+ async def get_delta_changes(client: GraphServiceClient, delta_token: str):
1106
+ """
1107
+ Get changes since last delta sync
1108
+
1109
+ Args:
1110
+ delta_token: Token from previous delta sync
1111
+ """
1112
+ # Note: Use the full delta link URL saved from previous call
1113
+ # This is a simplified example
1114
+ result = await client.me.drive.root.delta.get()
1115
+
1116
+ changes = []
1117
+
1118
+ if result and result.value:
1119
+ for item in result.value:
1120
+ if hasattr(item, 'deleted') and item.deleted:
1121
+ print(f"Deleted: {item.id}")
1122
+ changes.append(('deleted', item))
1123
+ else:
1124
+ print(f"Added/Modified: {item.name}")
1125
+ changes.append(('modified', item))
1126
+
1127
+ return changes
1128
+ ```
1129
+
1130
+
1131
+ ### 13. Special Folders
1132
+
1133
+ ```python
1134
+ async def get_special_folders(client: GraphServiceClient):
1135
+ """Access special OneDrive folders"""
1136
+
1137
+ # Documents folder
1138
+ documents = await client.me.drive.special.by_drive_item_id("documents").get()
1139
+ print(f"Documents: {documents.name}")
1140
+
1141
+ # Photos folder
1142
+ photos = await client.me.drive.special.by_drive_item_id("photos").get()
1143
+ print(f"Photos: {photos.name}")
1144
+
1145
+ # Camera roll
1146
+ cameraroll = await client.me.drive.special.by_drive_item_id("cameraroll").get()
1147
+ print(f"Camera Roll: {cameraroll.name}")
1148
+
1149
+ # App root folder
1150
+ approot = await client.me.drive.special.by_drive_item_id("approot").get()
1151
+ print(f"App Root: {approot.name}")
1152
+
1153
+ # List children of special folder
1154
+ async def list_documents_folder(client: GraphServiceClient):
1155
+ files = await client.me.drive.special.by_drive_item_id("documents").children.get()
1156
+
1157
+ if files and files.value:
1158
+ for file in files.value:
1159
+ print(f"{file.name}")
1160
+
1161
+ return files.value
1162
+ ```
1163
+
1164
+
1165
+ ### 14. Working with SharePoint Document Libraries
1166
+
1167
+ ```python
1168
+ async def get_sharepoint_drive(client: GraphServiceClient, site_id: str):
1169
+ """Access SharePoint site drive"""
1170
+ drive = await client.sites.by_site_id(site_id).drive.get()
1171
+
1172
+ print(f"Drive: {drive.name}")
1173
+ print(f"Type: {drive.drive_type}")
1174
+
1175
+ return drive
1176
+
1177
+ # List SharePoint document library contents
1178
+ async def list_sharepoint_files(client: GraphServiceClient, site_id: str):
1179
+ items = await client.sites.by_site_id(site_id).drive.root.children.get()
1180
+
1181
+ if items and items.value:
1182
+ for item in items.value:
1183
+ print(f"{item.name}")
1184
+
1185
+ return items.value
1186
+
1187
+ # Upload to SharePoint
1188
+ async def upload_to_sharepoint(client: GraphServiceClient, site_id: str, file_path: str, upload_path: str):
1189
+ async with aiofiles.open(file_path, 'rb') as f:
1190
+ content = await f.read()
1191
+
1192
+ item = await client.sites.by_site_id(site_id).drive.root.item_with_path(upload_path).content.put(content)
1193
+
1194
+ return item
1195
+
1196
+ # Get site by URL
1197
+ async def get_site_by_path(client: GraphServiceClient, hostname: str, server_relative_path: str):
1198
+ """
1199
+ Get SharePoint site by path
1200
+
1201
+ Example:
1202
+ hostname: 'contoso.sharepoint.com'
1203
+ server_relative_path: '/sites/marketing'
1204
+ """
1205
+ site = await client.sites.by_site_id(f"{hostname}:{server_relative_path}").get()
1206
+ return site
1207
+ ```
1208
+
1209
+
1210
+ ### 15. Batch Requests
1211
+
1212
+ ```python
1213
+ from msgraph.generated.models.batch_request_content import BatchRequestContent
1214
+ from msgraph.generated.models.batch_response_content import BatchResponseContent
1215
+
1216
+ async def batch_requests(client: GraphServiceClient):
1217
+ """Execute multiple requests in a single batch"""
1218
+
1219
+ # Create batch request
1220
+ batch_request_content = BatchRequestContent()
1221
+
1222
+ # Add requests to batch
1223
+ request1_id = batch_request_content.add_batch_request_step(
1224
+ {
1225
+ "id": "1",
1226
+ "method": "GET",
1227
+ "url": "/me/drive/root/children"
1228
+ }
1229
+ )
1230
+
1231
+ request2_id = batch_request_content.add_batch_request_step(
1232
+ {
1233
+ "id": "2",
1234
+ "method": "GET",
1235
+ "url": "/me/drive/special/documents"
1236
+ }
1237
+ )
1238
+
1239
+ # Execute batch
1240
+ batch_response = await client.batch.post(batch_request_content)
1241
+
1242
+ # Process responses
1243
+ for response_id, response in batch_response.get_responses().items():
1244
+ print(f"Request {response_id}: Status {response.status}")
1245
+ print(f"Body: {response.body}")
1246
+ ```
1247
+
1248
+
1249
+ ## Error Handling
1250
+
1251
+ ```python
1252
+ from msgraph.generated.models.o_data_errors.o_data_error import ODataError
1253
+ from azure.core.exceptions import HttpResponseError
1254
+
1255
+ async def safe_get_item(client: GraphServiceClient, item_id: str):
1256
+ """Example of comprehensive error handling"""
1257
+ try:
1258
+ item = await client.me.drive.items.by_drive_item_id(item_id).get()
1259
+ return item
1260
+
1261
+ except ODataError as e:
1262
+ print(f"OData Error: {e.error.code if e.error else 'Unknown'}")
1263
+ print(f"Message: {e.error.message if e.error else 'Unknown'}")
1264
+
1265
+ if e.response_status_code == 404:
1266
+ print("Item not found")
1267
+ elif e.response_status_code == 401:
1268
+ print("Unauthorized - check authentication")
1269
+ elif e.response_status_code == 403:
1270
+ print("Forbidden - check permissions")
1271
+ elif e.response_status_code == 429:
1272
+ print("Too many requests - rate limited")
1273
+
1274
+ return None
1275
+
1276
+ except HttpResponseError as e:
1277
+ print(f"HTTP Error: {e.status_code}")
1278
+ print(f"Message: {e.message}")
1279
+ return None
1280
+
1281
+ except Exception as e:
1282
+ print(f"Unexpected error: {type(e).__name__}")
1283
+ print(f"Details: {str(e)}")
1284
+ return None
1285
+ ```
1286
+
1287
+
1288
+ ## Complete Working Examples
1289
+
1290
+
1291
+ ### Example 1: File Backup Script
1292
+
1293
+ ```python
1294
+ import asyncio
1295
+ import os
1296
+ from azure.identity.aio import ClientSecretCredential
1297
+ from msgraph import GraphServiceClient
1298
+ from dotenv import load_dotenv
1299
+ import aiofiles
1300
+
1301
+ load_dotenv()
1302
+
1303
+ async def backup_onedrive_folder(folder_id: str, local_backup_path: str):
1304
+ """Backup an entire OneDrive folder to local disk"""
1305
+
1306
+ # Initialize client
1307
+ credentials = ClientSecretCredential(
1308
+ tenant_id=os.getenv('AZURE_TENANT_ID'),
1309
+ client_id=os.getenv('AZURE_CLIENT_ID'),
1310
+ client_secret=os.getenv('AZURE_CLIENT_SECRET')
1311
+ )
1312
+
1313
+ client = GraphServiceClient(
1314
+ credentials=credentials,
1315
+ scopes=['https://graph.microsoft.com/.default']
1316
+ )
1317
+
1318
+ user_id = os.getenv('USER_ID')
1319
+
1320
+ async def download_folder_recursive(folder_id: str, local_path: str):
1321
+ os.makedirs(local_path, exist_ok=True)
1322
+
1323
+ items = await client.users.by_user_id(user_id).drive.items.by_drive_item_id(folder_id).children.get()
1324
+
1325
+ if not items or not items.value:
1326
+ return
1327
+
1328
+ for item in items.value:
1329
+ if item.folder:
1330
+ subfolder_path = os.path.join(local_path, item.name)
1331
+ print(f"Backing up folder: {item.name}")
1332
+ await download_folder_recursive(item.id, subfolder_path)
1333
+
1334
+ elif item.file:
1335
+ file_path = os.path.join(local_path, item.name)
1336
+ print(f"Backing up file: {item.name} ({item.size} bytes)")
1337
+
1338
+ stream = await client.users.by_user_id(user_id).drive.items.by_drive_item_id(item.id).content.get()
1339
+
1340
+ async with aiofiles.open(file_path, 'wb') as f:
1341
+ await f.write(stream)
1342
+
1343
+ await download_folder_recursive(folder_id, local_backup_path)
1344
+ print("Backup complete!")
1345
+
1346
+ if __name__ == '__main__':
1347
+ asyncio.run(backup_onedrive_folder('FOLDER_ID_HERE', './backup'))
1348
+ ```
1349
+
1350
+
1351
+ ### Example 2: Sync Local Directory to OneDrive
1352
+
1353
+ ```python
1354
+ import asyncio
1355
+ import os
1356
+ from pathlib import Path
1357
+ from azure.identity.aio import ClientSecretCredential
1358
+ from msgraph import GraphServiceClient
1359
+ from msgraph.generated.models.drive_item import DriveItem
1360
+ from msgraph.generated.models.folder import Folder
1361
+ import aiofiles
1362
+
1363
+ async def sync_to_onedrive(local_path: str, onedrive_folder_id: str):
1364
+ """Sync local directory to OneDrive"""
1365
+
1366
+ credentials = ClientSecretCredential(
1367
+ tenant_id=os.getenv('AZURE_TENANT_ID'),
1368
+ client_id=os.getenv('AZURE_CLIENT_ID'),
1369
+ client_secret=os.getenv('AZURE_CLIENT_SECRET')
1370
+ )
1371
+
1372
+ client = GraphServiceClient(
1373
+ credentials=credentials,
1374
+ scopes=['https://graph.microsoft.com/.default']
1375
+ )
1376
+
1377
+ user_id = os.getenv('USER_ID')
1378
+
1379
+ async def sync_folder(local_dir: str, parent_id: str):
1380
+ for entry in os.listdir(local_dir):
1381
+ entry_path = os.path.join(local_dir, entry)
1382
+
1383
+ if os.path.isdir(entry_path):
1384
+ # Create folder in OneDrive
1385
+ drive_item = DriveItem()
1386
+ drive_item.name = entry
1387
+ drive_item.folder = Folder()
1388
+ drive_item.microsoft_graph_conflict_behavior = "replace"
1389
+
1390
+ folder = await client.users.by_user_id(user_id).drive.items.by_drive_item_id(parent_id).children.post(drive_item)
1391
+ print(f"Created folder: {entry}")
1392
+
1393
+ # Recursively sync subfolder
1394
+ await sync_folder(entry_path, folder.id)
1395
+
1396
+ else:
1397
+ # Upload file
1398
+ file_size = os.path.getsize(entry_path)
1399
+ print(f"Uploading {entry} ({file_size} bytes)...")
1400
+
1401
+ if file_size < 4 * 1024 * 1024:
1402
+ # Small file
1403
+ async with aiofiles.open(entry_path, 'rb') as f:
1404
+ content = await f.read()
1405
+
1406
+ await client.users.by_user_id(user_id).drive.items.by_drive_item_id(parent_id).item_with_path(entry).content.put(content)
1407
+ else:
1408
+ # Large file - would need upload session (simplified here)
1409
+ print(f"Skipping large file: {entry}")
1410
+
1411
+ print(f"Uploaded: {entry}")
1412
+
1413
+ await sync_folder(local_path, onedrive_folder_id)
1414
+ print("Sync complete!")
1415
+
1416
+ if __name__ == '__main__':
1417
+ asyncio.run(sync_to_onedrive('./local-folder', 'ONEDRIVE_FOLDER_ID'))
1418
+ ```
1419
+
1420
+
1421
+ ### Example 3: Share Files with Team
1422
+
1423
+ ```python
1424
+ import asyncio
1425
+ from azure.identity.aio import ClientSecretCredential
1426
+ from msgraph import GraphServiceClient
1427
+ from msgraph.generated.drives.item.items.item.invite.invite_post_request_body import InvitePostRequestBody
1428
+ from msgraph.generated.models.drive_recipient import DriveRecipient
1429
+
1430
+ async def share_folder_with_team(folder_id: str, team_emails: list):
1431
+ """Share a folder with team members"""
1432
+
1433
+ credentials = ClientSecretCredential(
1434
+ tenant_id=os.getenv('AZURE_TENANT_ID'),
1435
+ client_id=os.getenv('AZURE_CLIENT_ID'),
1436
+ client_secret=os.getenv('AZURE_CLIENT_SECRET')
1437
+ )
1438
+
1439
+ client = GraphServiceClient(
1440
+ credentials=credentials,
1441
+ scopes=['https://graph.microsoft.com/.default']
1442
+ )
1443
+
1444
+ user_id = os.getenv('USER_ID')
1445
+
1446
+ # Get folder info
1447
+ folder = await client.users.by_user_id(user_id).drive.items.by_drive_item_id(folder_id).get()
1448
+ print(f"Sharing folder: {folder.name}")
1449
+
1450
+ # Invite users
1451
+ request_body = InvitePostRequestBody()
1452
+ request_body.require_sign_in = True
1453
+ request_body.send_invitation = True
1454
+ request_body.roles = ["write"]
1455
+ request_body.message = f"You've been invited to collaborate on {folder.name}"
1456
+
1457
+ recipients = []
1458
+ for email in team_emails:
1459
+ recipient = DriveRecipient()
1460
+ recipient.email = email
1461
+ recipients.append(recipient)
1462
+
1463
+ request_body.recipients = recipients
1464
+
1465
+ permissions = await client.users.by_user_id(user_id).drive.items.by_drive_item_id(folder_id).invite.post(request_body)
1466
+
1467
+ print(f"Successfully shared with {len(team_emails)} team members")
1468
+
1469
+ return permissions
1470
+
1471
+ if __name__ == '__main__':
1472
+ team = ['alice@example.com', 'bob@example.com', 'charlie@example.com']
1473
+ asyncio.run(share_folder_with_team('FOLDER_ID', team))
1474
+ ```
1475
+
1476
+
1477
+ ## Rate Limiting and Throttling
1478
+
1479
+ ```python
1480
+ import asyncio
1481
+ from msgraph.generated.models.o_data_errors.o_data_error import ODataError
1482
+
1483
+ async def request_with_retry(client, request_func, max_retries=3):
1484
+ """Make request with automatic retry on rate limit"""
1485
+ retries = 0
1486
+
1487
+ while retries < max_retries:
1488
+ try:
1489
+ return await request_func()
1490
+
1491
+ except ODataError as e:
1492
+ if e.response_status_code == 429:
1493
+ # Rate limited
1494
+ retry_after = 5 # Default wait time
1495
+
1496
+ if e.response_headers and 'Retry-After' in e.response_headers:
1497
+ retry_after = int(e.response_headers['Retry-After'])
1498
+
1499
+ print(f"Rate limited. Waiting {retry_after} seconds...")
1500
+ await asyncio.sleep(retry_after)
1501
+ retries += 1
1502
+ else:
1503
+ raise
1504
+
1505
+ raise Exception("Max retries exceeded")
1506
+
1507
+ # Usage
1508
+ async def example_with_retry(client: GraphServiceClient):
1509
+ result = await request_with_retry(
1510
+ client,
1511
+ lambda: client.me.drive.root.children.get()
1512
+ )
1513
+ return result
1514
+ ```
1515
+
1516
+
1517
+ ## Testing Helper Functions
1518
+
1519
+ ```python
1520
+ async def test_connection(client: GraphServiceClient):
1521
+ """Test if client is properly configured"""
1522
+ try:
1523
+ me = await client.me.get()
1524
+ print(f"Connected as: {me.display_name}")
1525
+ print(f"Email: {me.user_principal_name}")
1526
+ return True
1527
+ except Exception as e:
1528
+ print(f"Connection test failed: {e}")
1529
+ return False
1530
+
1531
+ async def get_drive_info(client: GraphServiceClient):
1532
+ """Get OneDrive quota and usage information"""
1533
+ drive = await client.me.drive.get()
1534
+
1535
+ quota = drive.quota
1536
+
1537
+ if quota:
1538
+ total = quota.total / (1024**3) # Convert to GB
1539
+ used = quota.used / (1024**3)
1540
+ remaining = quota.remaining / (1024**3)
1541
+
1542
+ print(f"OneDrive Storage:")
1543
+ print(f" Total: {total:.2f} GB")
1544
+ print(f" Used: {used:.2f} GB")
1545
+ print(f" Remaining: {remaining:.2f} GB")
1546
+ print(f" State: {quota.state}")
1547
+
1548
+ return quota
1549
+ ```