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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1421 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: onedrive
|
|
3
|
+
description: "Microsoft OneDrive API coding guidelines for JavaScript/TypeScript using the official Microsoft Graph SDK"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "3.0.7"
|
|
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 (JavaScript/TypeScript)
|
|
13
|
+
|
|
14
|
+
You are a **Microsoft OneDrive API coding expert**. Help me write correct, idiomatic JavaScript/TypeScript 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 both **Node.js** and **browser** 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 `@microsoft/microsoft-graph-client` version 3.0.7 or later** for OneDrive operations. This is the official Microsoft Graph JavaScript 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 (Node.js):**
|
|
31
|
+
```bash
|
|
32
|
+
npm install @microsoft/microsoft-graph-client
|
|
33
|
+
npm install @azure/identity
|
|
34
|
+
npm install @microsoft/microsoft-graph-client/authProviders/azureTokenCredentials
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**For TypeScript projects, add type definitions:**
|
|
38
|
+
```bash
|
|
39
|
+
npm install --save-dev @microsoft/microsoft-graph-types
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
### Complete Setup for Node.js Applications
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Core Microsoft Graph SDK
|
|
49
|
+
npm install @microsoft/microsoft-graph-client
|
|
50
|
+
|
|
51
|
+
# Azure authentication library
|
|
52
|
+
npm install @azure/identity
|
|
53
|
+
|
|
54
|
+
# Install isomorphic-fetch for Node.js environments
|
|
55
|
+
npm install isomorphic-fetch
|
|
56
|
+
|
|
57
|
+
# TypeScript types (optional but recommended)
|
|
58
|
+
npm install --save-dev @microsoft/microsoft-graph-types
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Browser Applications
|
|
62
|
+
|
|
63
|
+
For browser-based applications, you can load the SDK via CDN or bundle it with your application:
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<!-- Microsoft Graph Client -->
|
|
67
|
+
<script src="https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js"></script>
|
|
68
|
+
|
|
69
|
+
<!-- For authentication in browser, use MSAL -->
|
|
70
|
+
<script src="https://alcdn.msauth.net/browser/2.32.0/js/msal-browser.min.js"></script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
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.
|
|
77
|
+
|
|
78
|
+
### Azure AD App Registration
|
|
79
|
+
|
|
80
|
+
1. Go to [Azure Portal](https://portal.azure.com) → Azure Active Directory → App registrations
|
|
81
|
+
2. Create a new registration
|
|
82
|
+
3. Note your **Application (client) ID** and **Directory (tenant) ID**
|
|
83
|
+
4. Create a client secret under "Certificates & secrets"
|
|
84
|
+
5. Add API permissions: Microsoft Graph → **Files.Read**, **Files.ReadWrite**, **Files.Read.All**, **Files.ReadWrite.All**
|
|
85
|
+
6. Grant admin consent for the permissions
|
|
86
|
+
|
|
87
|
+
### Required Scopes
|
|
88
|
+
|
|
89
|
+
Common OneDrive permission scopes:
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
// Read-only access to user's files
|
|
93
|
+
const SCOPES_READONLY = ['https://graph.microsoft.com/Files.Read'];
|
|
94
|
+
|
|
95
|
+
// Read/write access to user's files
|
|
96
|
+
const SCOPES_READWRITE = ['https://graph.microsoft.com/Files.ReadWrite'];
|
|
97
|
+
|
|
98
|
+
// Read all files user can access (including shared)
|
|
99
|
+
const SCOPES_READ_ALL = ['https://graph.microsoft.com/Files.Read.All'];
|
|
100
|
+
|
|
101
|
+
// Full access to all files user can access
|
|
102
|
+
const SCOPES_READWRITE_ALL = ['https://graph.microsoft.com/Files.ReadWrite.All'];
|
|
103
|
+
|
|
104
|
+
// Application permissions (no user context, requires admin consent)
|
|
105
|
+
const SCOPES_APP = ['https://graph.microsoft.com/.default'];
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
## Initialization
|
|
110
|
+
|
|
111
|
+
### Node.js with Client Credentials (Service/Daemon Apps)
|
|
112
|
+
|
|
113
|
+
For server-side applications using application permissions:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
117
|
+
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
|
118
|
+
import { ClientSecretCredential } from '@azure/identity';
|
|
119
|
+
import 'isomorphic-fetch';
|
|
120
|
+
|
|
121
|
+
// Environment variables
|
|
122
|
+
const tenantId = process.env.AZURE_TENANT_ID;
|
|
123
|
+
const clientId = process.env.AZURE_CLIENT_ID;
|
|
124
|
+
const clientSecret = process.env.AZURE_CLIENT_SECRET;
|
|
125
|
+
|
|
126
|
+
// Create credential
|
|
127
|
+
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
|
|
128
|
+
|
|
129
|
+
// Create authentication provider
|
|
130
|
+
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
|
131
|
+
scopes: ['https://graph.microsoft.com/.default']
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Initialize Microsoft Graph client
|
|
135
|
+
const client = Client.initWithMiddleware({ authProvider });
|
|
136
|
+
|
|
137
|
+
// Example: List files in user's OneDrive root
|
|
138
|
+
async function listFiles(userId) {
|
|
139
|
+
const response = await client
|
|
140
|
+
.api(`/users/${userId}/drive/root/children`)
|
|
141
|
+
.get();
|
|
142
|
+
|
|
143
|
+
return response.value;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Node.js with Device Code Flow (Interactive)
|
|
148
|
+
|
|
149
|
+
For CLI applications that need user interaction:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
153
|
+
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
|
154
|
+
import { DeviceCodeCredential } from '@azure/identity';
|
|
155
|
+
import 'isomorphic-fetch';
|
|
156
|
+
|
|
157
|
+
const credential = new DeviceCodeCredential({
|
|
158
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
159
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
160
|
+
userPromptCallback: (info) => {
|
|
161
|
+
console.log(info.message);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
|
166
|
+
scopes: ['Files.ReadWrite', 'Files.Read.All']
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const client = Client.initWithMiddleware({ authProvider });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Node.js with On-Behalf-Of Flow (Web APIs)
|
|
173
|
+
|
|
174
|
+
For web APIs that act on behalf of a signed-in user:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
178
|
+
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
|
179
|
+
import { OnBehalfOfCredential } from '@azure/identity';
|
|
180
|
+
|
|
181
|
+
const credential = new OnBehalfOfCredential({
|
|
182
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
183
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
184
|
+
clientSecret: process.env.AZURE_CLIENT_SECRET,
|
|
185
|
+
userAssertionToken: userToken // Token from incoming request
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
|
189
|
+
scopes: ['https://graph.microsoft.com/Files.ReadWrite']
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const client = Client.initWithMiddleware({ authProvider });
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Browser with MSAL (Interactive User Sign-In)
|
|
196
|
+
|
|
197
|
+
For single-page applications:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import * as msal from '@azure/msal-browser';
|
|
201
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
202
|
+
|
|
203
|
+
// MSAL configuration
|
|
204
|
+
const msalConfig = {
|
|
205
|
+
auth: {
|
|
206
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
207
|
+
authority: 'https://login.microsoftonline.com/YOUR_TENANT_ID',
|
|
208
|
+
redirectUri: 'http://localhost:3000'
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const msalInstance = new msal.PublicClientApplication(msalConfig);
|
|
213
|
+
|
|
214
|
+
// Custom authentication provider for MSAL
|
|
215
|
+
class MsalAuthenticationProvider {
|
|
216
|
+
constructor(msalInstance, scopes) {
|
|
217
|
+
this.msalInstance = msalInstance;
|
|
218
|
+
this.scopes = scopes;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async getAccessToken() {
|
|
222
|
+
const accounts = this.msalInstance.getAllAccounts();
|
|
223
|
+
if (accounts.length === 0) {
|
|
224
|
+
await this.msalInstance.loginPopup({ scopes: this.scopes });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const request = {
|
|
228
|
+
scopes: this.scopes,
|
|
229
|
+
account: this.msalInstance.getAllAccounts()[0]
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const response = await this.msalInstance.acquireTokenSilent(request);
|
|
234
|
+
return response.accessToken;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const response = await this.msalInstance.acquireTokenPopup(request);
|
|
237
|
+
return response.accessToken;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Initialize Graph client
|
|
243
|
+
const authProvider = new MsalAuthenticationProvider(
|
|
244
|
+
msalInstance,
|
|
245
|
+
['Files.ReadWrite']
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const client = Client.initWithMiddleware({
|
|
249
|
+
authProvider: (done) => {
|
|
250
|
+
authProvider.getAccessToken().then(token => {
|
|
251
|
+
done(null, token);
|
|
252
|
+
}).catch(err => done(err, null));
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Environment Variables Setup
|
|
258
|
+
|
|
259
|
+
Create a `.env` file:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
AZURE_TENANT_ID=your-tenant-id
|
|
263
|
+
AZURE_CLIENT_ID=your-client-id
|
|
264
|
+
AZURE_CLIENT_SECRET=your-client-secret
|
|
265
|
+
USER_ID=user@domain.com
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Load environment variables:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import dotenv from 'dotenv';
|
|
272
|
+
dotenv.config();
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
## Core API Surfaces
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
### 1. Listing Files and Folders
|
|
280
|
+
|
|
281
|
+
#### List Root Folder Contents
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Current user's OneDrive root
|
|
285
|
+
const result = await client.api('/me/drive/root/children').get();
|
|
286
|
+
|
|
287
|
+
console.log(result.value); // Array of DriveItem objects
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### List Specific Folder Contents
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// By item ID
|
|
294
|
+
const result = await client
|
|
295
|
+
.api(`/me/drive/items/{item-id}/children`)
|
|
296
|
+
.get();
|
|
297
|
+
|
|
298
|
+
// By path
|
|
299
|
+
const result = await client
|
|
300
|
+
.api('/me/drive/root:/Documents/Projects:/children')
|
|
301
|
+
.get();
|
|
302
|
+
|
|
303
|
+
// With pagination
|
|
304
|
+
let items = [];
|
|
305
|
+
let nextLink = '/me/drive/root/children';
|
|
306
|
+
|
|
307
|
+
while (nextLink) {
|
|
308
|
+
const response = await client.api(nextLink).get();
|
|
309
|
+
items = items.concat(response.value);
|
|
310
|
+
nextLink = response['@odata.nextLink'];
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### Advanced Listing with Query Parameters
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Select specific fields
|
|
318
|
+
const result = await client
|
|
319
|
+
.api('/me/drive/root/children')
|
|
320
|
+
.select('id,name,size,createdDateTime,lastModifiedDateTime')
|
|
321
|
+
.get();
|
|
322
|
+
|
|
323
|
+
// Filter files by type
|
|
324
|
+
const result = await client
|
|
325
|
+
.api('/me/drive/root/children')
|
|
326
|
+
.filter('file ne null')
|
|
327
|
+
.get();
|
|
328
|
+
|
|
329
|
+
// Order by name
|
|
330
|
+
const result = await client
|
|
331
|
+
.api('/me/drive/root/children')
|
|
332
|
+
.orderby('name asc')
|
|
333
|
+
.get();
|
|
334
|
+
|
|
335
|
+
// Limit results
|
|
336
|
+
const result = await client
|
|
337
|
+
.api('/me/drive/root/children')
|
|
338
|
+
.top(10)
|
|
339
|
+
.get();
|
|
340
|
+
|
|
341
|
+
// Expand related properties
|
|
342
|
+
const result = await client
|
|
343
|
+
.api('/me/drive/root/children')
|
|
344
|
+
.expand('thumbnails')
|
|
345
|
+
.get();
|
|
346
|
+
|
|
347
|
+
// Combine multiple query options
|
|
348
|
+
const result = await client
|
|
349
|
+
.api('/me/drive/root/children')
|
|
350
|
+
.select('id,name,size,file')
|
|
351
|
+
.filter('file ne null')
|
|
352
|
+
.orderby('lastModifiedDateTime desc')
|
|
353
|
+
.top(20)
|
|
354
|
+
.get();
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### List All Drives
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// List all drives accessible to user
|
|
361
|
+
const drives = await client.api('/me/drives').get();
|
|
362
|
+
|
|
363
|
+
// Get specific drive
|
|
364
|
+
const drive = await client.api('/drives/{drive-id}').get();
|
|
365
|
+
|
|
366
|
+
// Get default drive
|
|
367
|
+
const defaultDrive = await client.api('/me/drive').get();
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
### 2. Getting File/Folder Metadata
|
|
372
|
+
|
|
373
|
+
#### Get Item Metadata
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// By item ID
|
|
377
|
+
const item = await client
|
|
378
|
+
.api(`/me/drive/items/{item-id}`)
|
|
379
|
+
.get();
|
|
380
|
+
|
|
381
|
+
console.log(item.id);
|
|
382
|
+
console.log(item.name);
|
|
383
|
+
console.log(item.size);
|
|
384
|
+
console.log(item.createdDateTime);
|
|
385
|
+
console.log(item.lastModifiedDateTime);
|
|
386
|
+
console.log(item.webUrl);
|
|
387
|
+
|
|
388
|
+
// By path
|
|
389
|
+
const item = await client
|
|
390
|
+
.api('/me/drive/root:/Documents/report.pdf')
|
|
391
|
+
.get();
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
#### Get File with Specific Properties
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
const item = await client
|
|
398
|
+
.api('/me/drive/items/{item-id}')
|
|
399
|
+
.select('id,name,size,file,@microsoft.graph.downloadUrl')
|
|
400
|
+
.get();
|
|
401
|
+
|
|
402
|
+
// Access file-specific properties
|
|
403
|
+
if (item.file) {
|
|
404
|
+
console.log('MIME type:', item.file.mimeType);
|
|
405
|
+
console.log('Hashes:', item.file.hashes);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Get download URL
|
|
409
|
+
console.log('Download URL:', item['@microsoft.graph.downloadUrl']);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
### 3. Downloading Files
|
|
414
|
+
|
|
415
|
+
#### Simple Download (Small Files)
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import fs from 'fs';
|
|
419
|
+
import { Readable } from 'stream';
|
|
420
|
+
|
|
421
|
+
// Download file content
|
|
422
|
+
const fileStream = await client
|
|
423
|
+
.api(`/me/drive/items/{item-id}/content`)
|
|
424
|
+
.getStream();
|
|
425
|
+
|
|
426
|
+
// Save to disk
|
|
427
|
+
const writeStream = fs.createWriteStream('./downloaded-file.pdf');
|
|
428
|
+
fileStream.pipe(writeStream);
|
|
429
|
+
|
|
430
|
+
await new Promise((resolve, reject) => {
|
|
431
|
+
writeStream.on('finish', resolve);
|
|
432
|
+
writeStream.on('error', reject);
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
#### Download with Pre-Authenticated URL
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import fetch from 'node-fetch';
|
|
440
|
+
import fs from 'fs';
|
|
441
|
+
|
|
442
|
+
// Get item with download URL
|
|
443
|
+
const item = await client
|
|
444
|
+
.api('/me/drive/items/{item-id}')
|
|
445
|
+
.select('@microsoft.graph.downloadUrl')
|
|
446
|
+
.get();
|
|
447
|
+
|
|
448
|
+
const downloadUrl = item['@microsoft.graph.downloadUrl'];
|
|
449
|
+
|
|
450
|
+
// Download using the URL (valid for a few minutes)
|
|
451
|
+
const response = await fetch(downloadUrl);
|
|
452
|
+
const buffer = await response.buffer();
|
|
453
|
+
fs.writeFileSync('./file.dat', buffer);
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
#### Download Specific Byte Range
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Download partial file content
|
|
460
|
+
const stream = await client
|
|
461
|
+
.api(`/me/drive/items/{item-id}/content`)
|
|
462
|
+
.header('Range', 'bytes=0-1023')
|
|
463
|
+
.getStream();
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
### 4. Uploading Files
|
|
468
|
+
|
|
469
|
+
#### Simple Upload (Files < 4MB)
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import fs from 'fs';
|
|
473
|
+
|
|
474
|
+
// Upload file from buffer or stream
|
|
475
|
+
const fileContent = fs.readFileSync('./local-file.pdf');
|
|
476
|
+
|
|
477
|
+
const uploadedFile = await client
|
|
478
|
+
.api('/me/drive/root:/Documents/uploaded-file.pdf:/content')
|
|
479
|
+
.putStream(fs.createReadStream('./local-file.pdf'));
|
|
480
|
+
|
|
481
|
+
console.log('Uploaded file ID:', uploadedFile.id);
|
|
482
|
+
|
|
483
|
+
// Or upload with buffer
|
|
484
|
+
const uploadedFile = await client
|
|
485
|
+
.api('/me/drive/items/{parent-folder-id}:/filename.txt:/content')
|
|
486
|
+
.put(fileContent);
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### Upload to Specific Folder
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// Upload to folder by ID
|
|
493
|
+
const file = await client
|
|
494
|
+
.api(`/me/drive/items/{folder-id}:/newfile.txt:/content`)
|
|
495
|
+
.put('File content here');
|
|
496
|
+
|
|
497
|
+
// Upload to folder by path
|
|
498
|
+
const file = await client
|
|
499
|
+
.api('/me/drive/root:/Documents/Projects:/report.pdf:/content')
|
|
500
|
+
.putStream(fs.createReadStream('./report.pdf'));
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
#### Large File Upload (Files > 4MB) - Resumable Upload Session
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { LargeFileUploadTask, StreamUpload } from '@microsoft/microsoft-graph-client';
|
|
507
|
+
import fs from 'fs';
|
|
508
|
+
|
|
509
|
+
// Step 1: Create upload session
|
|
510
|
+
const uploadSession = await client
|
|
511
|
+
.api('/me/drive/root:/large-file.zip:/createUploadSession')
|
|
512
|
+
.post({
|
|
513
|
+
item: {
|
|
514
|
+
'@microsoft.graph.conflictBehavior': 'rename',
|
|
515
|
+
name: 'large-file.zip'
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Step 2: Upload file in chunks
|
|
520
|
+
const fileObject = new StreamUpload(
|
|
521
|
+
fs.createReadStream('./large-file.zip'),
|
|
522
|
+
'large-file.zip',
|
|
523
|
+
fs.statSync('./large-file.zip').size
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const task = new LargeFileUploadTask(client, fileObject, uploadSession);
|
|
527
|
+
|
|
528
|
+
const uploadResult = await task.upload();
|
|
529
|
+
|
|
530
|
+
console.log('Upload complete:', uploadResult);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
#### Advanced Large File Upload with Progress Tracking
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
import { LargeFileUploadTask, StreamUpload, UploadEventHandlers } from '@microsoft/microsoft-graph-client';
|
|
537
|
+
import fs from 'fs';
|
|
538
|
+
|
|
539
|
+
const fileName = 'video.mp4';
|
|
540
|
+
const filePath = './video.mp4';
|
|
541
|
+
const stats = fs.statSync(filePath);
|
|
542
|
+
const fileSize = stats.size;
|
|
543
|
+
|
|
544
|
+
// Create upload session with conflict resolution
|
|
545
|
+
const uploadSession = await client
|
|
546
|
+
.api('/me/drive/root:/Videos:/video.mp4:/createUploadSession')
|
|
547
|
+
.post({
|
|
548
|
+
item: {
|
|
549
|
+
'@microsoft.graph.conflictBehavior': 'replace', // or 'fail', 'rename'
|
|
550
|
+
name: fileName
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Configure upload
|
|
555
|
+
const fileStream = fs.createReadStream(filePath);
|
|
556
|
+
const fileObject = new StreamUpload(fileStream, fileName, fileSize);
|
|
557
|
+
|
|
558
|
+
// Upload with custom chunk size (must be multiple of 320 KB)
|
|
559
|
+
const options = {
|
|
560
|
+
rangeSize: 1024 * 1024 * 10, // 10 MB chunks (recommended: 5-10 MB)
|
|
561
|
+
uploadEventHandlers: {
|
|
562
|
+
progress: (range, extraCallbackParam) => {
|
|
563
|
+
const percentage = ((range?.minValue || 0) / fileSize) * 100;
|
|
564
|
+
console.log(`Uploaded ${percentage.toFixed(2)}%`);
|
|
565
|
+
},
|
|
566
|
+
extraCallbackParam: null
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const task = new LargeFileUploadTask(client, fileObject, uploadSession);
|
|
571
|
+
const uploadResult = await task.upload();
|
|
572
|
+
|
|
573
|
+
console.log('File uploaded successfully:', uploadResult.id);
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
#### Resume Interrupted Upload
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
// If upload fails, you can resume from where it stopped
|
|
580
|
+
const uploadSession = await client
|
|
581
|
+
.api('/me/drive/root:/large-file.zip:/createUploadSession')
|
|
582
|
+
.post({
|
|
583
|
+
item: {
|
|
584
|
+
'@microsoft.graph.conflictBehavior': 'replace',
|
|
585
|
+
name: 'large-file.zip'
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const fileObject = new StreamUpload(
|
|
590
|
+
fs.createReadStream('./large-file.zip'),
|
|
591
|
+
'large-file.zip',
|
|
592
|
+
fs.statSync('./large-file.zip').size
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
const task = new LargeFileUploadTask(client, fileObject, uploadSession);
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
const uploadResult = await task.upload();
|
|
599
|
+
console.log('Upload complete');
|
|
600
|
+
} catch (error) {
|
|
601
|
+
console.error('Upload failed, attempting to resume...');
|
|
602
|
+
|
|
603
|
+
// Resume upload
|
|
604
|
+
const resumeResult = await task.resume();
|
|
605
|
+
console.log('Resumed and completed:', resumeResult);
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
### 5. Creating Folders
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// Create folder in root
|
|
614
|
+
const folder = await client
|
|
615
|
+
.api('/me/drive/root/children')
|
|
616
|
+
.post({
|
|
617
|
+
name: 'New Folder',
|
|
618
|
+
folder: {},
|
|
619
|
+
'@microsoft.graph.conflictBehavior': 'rename'
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// Create folder at specific path
|
|
623
|
+
const folder = await client
|
|
624
|
+
.api('/me/drive/root:/Documents/Projects:/children')
|
|
625
|
+
.post({
|
|
626
|
+
name: 'Project Alpha',
|
|
627
|
+
folder: {},
|
|
628
|
+
'@microsoft.graph.conflictBehavior': 'fail' // or 'replace', 'rename'
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Create nested folder structure
|
|
632
|
+
const parentFolder = await client
|
|
633
|
+
.api('/me/drive/root/children')
|
|
634
|
+
.post({
|
|
635
|
+
name: 'Parent',
|
|
636
|
+
folder: {}
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const childFolder = await client
|
|
640
|
+
.api(`/me/drive/items/${parentFolder.id}/children`)
|
|
641
|
+
.post({
|
|
642
|
+
name: 'Child',
|
|
643
|
+
folder: {}
|
|
644
|
+
});
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
### 6. Searching Files
|
|
649
|
+
|
|
650
|
+
```typescript
|
|
651
|
+
// Search in entire drive
|
|
652
|
+
const results = await client
|
|
653
|
+
.api('/me/drive/root/search(q=\'{search-query}\')')
|
|
654
|
+
.get();
|
|
655
|
+
|
|
656
|
+
// Example: Search for PDFs
|
|
657
|
+
const pdfFiles = await client
|
|
658
|
+
.api('/me/drive/root/search(q=\'.pdf\')')
|
|
659
|
+
.get();
|
|
660
|
+
|
|
661
|
+
// Search in specific folder
|
|
662
|
+
const results = await client
|
|
663
|
+
.api('/me/drive/items/{folder-id}/search(q=\'quarterly report\')')
|
|
664
|
+
.get();
|
|
665
|
+
|
|
666
|
+
// Search with select and filter
|
|
667
|
+
const results = await client
|
|
668
|
+
.api('/me/drive/root/search(q=\'presentation\')')
|
|
669
|
+
.select('id,name,size,webUrl')
|
|
670
|
+
.filter('file ne null')
|
|
671
|
+
.top(10)
|
|
672
|
+
.get();
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
### 7. Updating/Renaming Files and Folders
|
|
677
|
+
|
|
678
|
+
```typescript
|
|
679
|
+
// Rename file
|
|
680
|
+
const updated = await client
|
|
681
|
+
.api('/me/drive/items/{item-id}')
|
|
682
|
+
.patch({
|
|
683
|
+
name: 'new-name.pdf'
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// Update file metadata
|
|
687
|
+
const updated = await client
|
|
688
|
+
.api('/me/drive/items/{item-id}')
|
|
689
|
+
.patch({
|
|
690
|
+
description: 'Updated description',
|
|
691
|
+
name: 'renamed-file.docx'
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// Move file to different folder
|
|
695
|
+
const moved = await client
|
|
696
|
+
.api('/me/drive/items/{item-id}')
|
|
697
|
+
.patch({
|
|
698
|
+
parentReference: {
|
|
699
|
+
id: '{new-parent-folder-id}'
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Move and rename simultaneously
|
|
704
|
+
const updated = await client
|
|
705
|
+
.api('/me/drive/items/{item-id}')
|
|
706
|
+
.patch({
|
|
707
|
+
name: 'new-name.xlsx',
|
|
708
|
+
parentReference: {
|
|
709
|
+
id: '{new-parent-folder-id}'
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
### 8. Copying Files
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
// Copy file to another location
|
|
719
|
+
const copyOperation = await client
|
|
720
|
+
.api('/me/drive/items/{item-id}/copy')
|
|
721
|
+
.post({
|
|
722
|
+
parentReference: {
|
|
723
|
+
id: '{destination-folder-id}'
|
|
724
|
+
},
|
|
725
|
+
name: 'copied-file.pdf'
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Monitor copy operation status
|
|
729
|
+
const monitorUrl = copyOperation.headers.get('Location');
|
|
730
|
+
|
|
731
|
+
// Poll for completion
|
|
732
|
+
async function waitForCopy(monitorUrl) {
|
|
733
|
+
while (true) {
|
|
734
|
+
const response = await fetch(monitorUrl);
|
|
735
|
+
const status = await response.json();
|
|
736
|
+
|
|
737
|
+
if (status.status === 'completed') {
|
|
738
|
+
return status.resourceId;
|
|
739
|
+
} else if (status.status === 'failed') {
|
|
740
|
+
throw new Error('Copy failed: ' + status.error);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
### 9. Deleting Files and Folders
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
// Delete file or folder
|
|
753
|
+
await client
|
|
754
|
+
.api('/me/drive/items/{item-id}')
|
|
755
|
+
.delete();
|
|
756
|
+
|
|
757
|
+
// Delete by path
|
|
758
|
+
await client
|
|
759
|
+
.api('/me/drive/root:/Documents/old-file.pdf')
|
|
760
|
+
.delete();
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
### 10. Sharing and Permissions
|
|
765
|
+
|
|
766
|
+
#### Create Sharing Link
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
// Create anonymous view link
|
|
770
|
+
const link = await client
|
|
771
|
+
.api('/me/drive/items/{item-id}/createLink')
|
|
772
|
+
.post({
|
|
773
|
+
type: 'view', // 'view', 'edit', 'embed'
|
|
774
|
+
scope: 'anonymous' // 'anonymous', 'organization'
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
console.log('Share link:', link.link.webUrl);
|
|
778
|
+
|
|
779
|
+
// Create organization-wide edit link
|
|
780
|
+
const link = await client
|
|
781
|
+
.api('/me/drive/items/{item-id}/createLink')
|
|
782
|
+
.post({
|
|
783
|
+
type: 'edit',
|
|
784
|
+
scope: 'organization'
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Create link with expiration
|
|
788
|
+
const link = await client
|
|
789
|
+
.api('/me/drive/items/{item-id}/createLink')
|
|
790
|
+
.post({
|
|
791
|
+
type: 'view',
|
|
792
|
+
scope: 'anonymous',
|
|
793
|
+
expirationDateTime: '2025-12-31T23:59:59Z',
|
|
794
|
+
password: 'SecurePass123' // Optional password protection
|
|
795
|
+
});
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
#### Grant Permissions to Specific Users
|
|
799
|
+
|
|
800
|
+
```typescript
|
|
801
|
+
// Invite user with edit permissions
|
|
802
|
+
const permission = await client
|
|
803
|
+
.api('/me/drive/items/{item-id}/invite')
|
|
804
|
+
.post({
|
|
805
|
+
requireSignIn: true,
|
|
806
|
+
sendInvitation: true,
|
|
807
|
+
roles: ['write'], // 'read' or 'write'
|
|
808
|
+
recipients: [
|
|
809
|
+
{ email: 'user@example.com' }
|
|
810
|
+
],
|
|
811
|
+
message: 'Here is the file I mentioned'
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// Add multiple recipients
|
|
815
|
+
const permission = await client
|
|
816
|
+
.api('/me/drive/items/{item-id}/invite')
|
|
817
|
+
.post({
|
|
818
|
+
requireSignIn: false,
|
|
819
|
+
sendInvitation: true,
|
|
820
|
+
roles: ['read'],
|
|
821
|
+
recipients: [
|
|
822
|
+
{ email: 'user1@example.com' },
|
|
823
|
+
{ email: 'user2@example.com' }
|
|
824
|
+
]
|
|
825
|
+
});
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
#### List Permissions
|
|
829
|
+
|
|
830
|
+
```typescript
|
|
831
|
+
// Get all permissions for an item
|
|
832
|
+
const permissions = await client
|
|
833
|
+
.api('/me/drive/items/{item-id}/permissions')
|
|
834
|
+
.get();
|
|
835
|
+
|
|
836
|
+
permissions.value.forEach(permission => {
|
|
837
|
+
console.log('Permission ID:', permission.id);
|
|
838
|
+
console.log('Roles:', permission.roles);
|
|
839
|
+
if (permission.grantedTo) {
|
|
840
|
+
console.log('Granted to:', permission.grantedTo.user.displayName);
|
|
841
|
+
}
|
|
842
|
+
if (permission.link) {
|
|
843
|
+
console.log('Share link:', permission.link.webUrl);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
#### Remove Permissions
|
|
849
|
+
|
|
850
|
+
```typescript
|
|
851
|
+
// Delete specific permission
|
|
852
|
+
await client
|
|
853
|
+
.api('/me/drive/items/{item-id}/permissions/{permission-id}')
|
|
854
|
+
.delete();
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
#### List Files Shared With Me
|
|
858
|
+
|
|
859
|
+
```typescript
|
|
860
|
+
// Get files shared with the current user
|
|
861
|
+
const sharedItems = await client
|
|
862
|
+
.api('/me/drive/sharedWithMe')
|
|
863
|
+
.get();
|
|
864
|
+
|
|
865
|
+
sharedItems.value.forEach(item => {
|
|
866
|
+
console.log('Shared file:', item.name);
|
|
867
|
+
console.log('Owner:', item.remoteItem.createdBy.user.displayName);
|
|
868
|
+
console.log('Parent path:', item.remoteItem.parentReference.path);
|
|
869
|
+
});
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
### 11. Thumbnails
|
|
874
|
+
|
|
875
|
+
```typescript
|
|
876
|
+
// Get thumbnails for an item
|
|
877
|
+
const thumbnails = await client
|
|
878
|
+
.api('/me/drive/items/{item-id}/thumbnails')
|
|
879
|
+
.get();
|
|
880
|
+
|
|
881
|
+
// Access different sizes
|
|
882
|
+
const thumbs = thumbnails.value[0];
|
|
883
|
+
console.log('Small:', thumbs.small.url);
|
|
884
|
+
console.log('Medium:', thumbs.medium.url);
|
|
885
|
+
console.log('Large:', thumbs.large.url);
|
|
886
|
+
|
|
887
|
+
// Get specific thumbnail size
|
|
888
|
+
const thumbnail = await client
|
|
889
|
+
.api('/me/drive/items/{item-id}/thumbnails/0/medium')
|
|
890
|
+
.get();
|
|
891
|
+
|
|
892
|
+
console.log('Thumbnail URL:', thumbnail.url);
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
### 12. Delta (Change Tracking)
|
|
897
|
+
|
|
898
|
+
```typescript
|
|
899
|
+
// Get initial delta token and items
|
|
900
|
+
let deltaUrl = '/me/drive/root/delta';
|
|
901
|
+
let allItems = [];
|
|
902
|
+
|
|
903
|
+
while (deltaUrl) {
|
|
904
|
+
const response = await client.api(deltaUrl).get();
|
|
905
|
+
|
|
906
|
+
allItems = allItems.concat(response.value);
|
|
907
|
+
|
|
908
|
+
if (response['@odata.nextLink']) {
|
|
909
|
+
deltaUrl = response['@odata.nextLink'];
|
|
910
|
+
} else {
|
|
911
|
+
// Store delta token for next sync
|
|
912
|
+
const deltaToken = response['@odata.deltaLink'];
|
|
913
|
+
console.log('Delta token:', deltaToken);
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Later, use the delta token to get only changes
|
|
919
|
+
const changes = await client
|
|
920
|
+
.api('/me/drive/root/delta?token={previous-delta-token}')
|
|
921
|
+
.get();
|
|
922
|
+
|
|
923
|
+
changes.value.forEach(item => {
|
|
924
|
+
if (item.deleted) {
|
|
925
|
+
console.log('Deleted:', item.id);
|
|
926
|
+
} else {
|
|
927
|
+
console.log('Added/Modified:', item.name);
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
### 13. Special Folders
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
// Access special folders
|
|
937
|
+
const documents = await client
|
|
938
|
+
.api('/me/drive/special/documents')
|
|
939
|
+
.get();
|
|
940
|
+
|
|
941
|
+
const photos = await client
|
|
942
|
+
.api('/me/drive/special/photos')
|
|
943
|
+
.get();
|
|
944
|
+
|
|
945
|
+
const cameraRoll = await client
|
|
946
|
+
.api('/me/drive/special/cameraroll')
|
|
947
|
+
.get();
|
|
948
|
+
|
|
949
|
+
const appRoot = await client
|
|
950
|
+
.api('/me/drive/special/approot')
|
|
951
|
+
.get();
|
|
952
|
+
|
|
953
|
+
// List children of special folder
|
|
954
|
+
const files = await client
|
|
955
|
+
.api('/me/drive/special/documents/children')
|
|
956
|
+
.get();
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
### 14. Working with SharePoint Document Libraries
|
|
961
|
+
|
|
962
|
+
```typescript
|
|
963
|
+
// Access SharePoint site drive
|
|
964
|
+
const drive = await client
|
|
965
|
+
.api('/sites/{site-id}/drive')
|
|
966
|
+
.get();
|
|
967
|
+
|
|
968
|
+
// List document library contents
|
|
969
|
+
const items = await client
|
|
970
|
+
.api('/sites/{site-id}/drive/root/children')
|
|
971
|
+
.get();
|
|
972
|
+
|
|
973
|
+
// Upload to SharePoint
|
|
974
|
+
const file = await client
|
|
975
|
+
.api('/sites/{site-id}/drive/root:/folder/file.pdf:/content')
|
|
976
|
+
.put(fileContent);
|
|
977
|
+
|
|
978
|
+
// Get site by path
|
|
979
|
+
const site = await client
|
|
980
|
+
.api('/sites/{hostname}:/{server-relative-path}')
|
|
981
|
+
.get();
|
|
982
|
+
|
|
983
|
+
// Example: Get site by URL
|
|
984
|
+
const site = await client
|
|
985
|
+
.api('/sites/contoso.sharepoint.com:/sites/marketing')
|
|
986
|
+
.get();
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
### 15. Batch Requests
|
|
991
|
+
|
|
992
|
+
```typescript
|
|
993
|
+
// Batch multiple requests
|
|
994
|
+
const batch = {
|
|
995
|
+
requests: [
|
|
996
|
+
{
|
|
997
|
+
id: '1',
|
|
998
|
+
method: 'GET',
|
|
999
|
+
url: '/me/drive/root/children'
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
id: '2',
|
|
1003
|
+
method: 'GET',
|
|
1004
|
+
url: '/me/drive/special/documents'
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
id: '3',
|
|
1008
|
+
method: 'GET',
|
|
1009
|
+
url: '/me/drive/root/search(q=\'report\')'
|
|
1010
|
+
}
|
|
1011
|
+
]
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
const batchResponse = await client
|
|
1015
|
+
.api('/$batch')
|
|
1016
|
+
.post(batch);
|
|
1017
|
+
|
|
1018
|
+
batchResponse.responses.forEach(response => {
|
|
1019
|
+
console.log(`Request ${response.id}:`, response.status);
|
|
1020
|
+
console.log('Body:', response.body);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
// Batch with dependencies
|
|
1024
|
+
const batchWithDeps = {
|
|
1025
|
+
requests: [
|
|
1026
|
+
{
|
|
1027
|
+
id: '1',
|
|
1028
|
+
method: 'POST',
|
|
1029
|
+
url: '/me/drive/root/children',
|
|
1030
|
+
body: {
|
|
1031
|
+
name: 'NewFolder',
|
|
1032
|
+
folder: {}
|
|
1033
|
+
},
|
|
1034
|
+
headers: {
|
|
1035
|
+
'Content-Type': 'application/json'
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
id: '2',
|
|
1040
|
+
dependsOn: ['1'],
|
|
1041
|
+
method: 'PUT',
|
|
1042
|
+
url: '/me/drive/items/{$1.id}:/file.txt:/content',
|
|
1043
|
+
body: 'File content',
|
|
1044
|
+
headers: {
|
|
1045
|
+
'Content-Type': 'text/plain'
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
]
|
|
1049
|
+
};
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
## Error Handling
|
|
1054
|
+
|
|
1055
|
+
```typescript
|
|
1056
|
+
import { GraphError } from '@microsoft/microsoft-graph-client';
|
|
1057
|
+
|
|
1058
|
+
try {
|
|
1059
|
+
const item = await client
|
|
1060
|
+
.api('/me/drive/items/{item-id}')
|
|
1061
|
+
.get();
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
if (error instanceof GraphError) {
|
|
1064
|
+
console.error('Graph error code:', error.code);
|
|
1065
|
+
console.error('Status code:', error.statusCode);
|
|
1066
|
+
console.error('Message:', error.message);
|
|
1067
|
+
|
|
1068
|
+
// Handle specific errors
|
|
1069
|
+
if (error.statusCode === 404) {
|
|
1070
|
+
console.error('Item not found');
|
|
1071
|
+
} else if (error.statusCode === 401) {
|
|
1072
|
+
console.error('Unauthorized - check authentication');
|
|
1073
|
+
} else if (error.statusCode === 403) {
|
|
1074
|
+
console.error('Forbidden - check permissions');
|
|
1075
|
+
} else if (error.statusCode === 429) {
|
|
1076
|
+
console.error('Too many requests - rate limited');
|
|
1077
|
+
const retryAfter = error.headers?.get('Retry-After');
|
|
1078
|
+
console.log(`Retry after ${retryAfter} seconds`);
|
|
1079
|
+
}
|
|
1080
|
+
} else {
|
|
1081
|
+
console.error('Unexpected error:', error);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
## Complete Working Examples
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
### Example 1: File Upload and Share Workflow
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
1094
|
+
import { ClientSecretCredential } from '@azure/identity';
|
|
1095
|
+
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
|
1096
|
+
import fs from 'fs';
|
|
1097
|
+
import 'isomorphic-fetch';
|
|
1098
|
+
|
|
1099
|
+
async function uploadAndShare() {
|
|
1100
|
+
// Initialize client
|
|
1101
|
+
const credential = new ClientSecretCredential(
|
|
1102
|
+
process.env.AZURE_TENANT_ID!,
|
|
1103
|
+
process.env.AZURE_CLIENT_ID!,
|
|
1104
|
+
process.env.AZURE_CLIENT_SECRET!
|
|
1105
|
+
);
|
|
1106
|
+
|
|
1107
|
+
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
|
1108
|
+
scopes: ['https://graph.microsoft.com/.default']
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
const client = Client.initWithMiddleware({ authProvider });
|
|
1112
|
+
|
|
1113
|
+
const userId = process.env.USER_ID!;
|
|
1114
|
+
|
|
1115
|
+
// Create folder
|
|
1116
|
+
const folder = await client
|
|
1117
|
+
.api(`/users/${userId}/drive/root/children`)
|
|
1118
|
+
.post({
|
|
1119
|
+
name: 'Shared Documents',
|
|
1120
|
+
folder: {},
|
|
1121
|
+
'@microsoft.graph.conflictBehavior': 'rename'
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
console.log('Created folder:', folder.id);
|
|
1125
|
+
|
|
1126
|
+
// Upload file
|
|
1127
|
+
const fileContent = fs.readFileSync('./document.pdf');
|
|
1128
|
+
const uploadedFile = await client
|
|
1129
|
+
.api(`/users/${userId}/drive/items/${folder.id}:/document.pdf:/content`)
|
|
1130
|
+
.put(fileContent);
|
|
1131
|
+
|
|
1132
|
+
console.log('Uploaded file:', uploadedFile.id);
|
|
1133
|
+
|
|
1134
|
+
// Create share link
|
|
1135
|
+
const shareLink = await client
|
|
1136
|
+
.api(`/users/${userId}/drive/items/${uploadedFile.id}/createLink`)
|
|
1137
|
+
.post({
|
|
1138
|
+
type: 'view',
|
|
1139
|
+
scope: 'organization',
|
|
1140
|
+
expirationDateTime: '2025-12-31T23:59:59Z'
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
console.log('Share URL:', shareLink.link.webUrl);
|
|
1144
|
+
|
|
1145
|
+
return shareLink.link.webUrl;
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
|
|
1150
|
+
### Example 2: Download All Files from Folder
|
|
1151
|
+
|
|
1152
|
+
```typescript
|
|
1153
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
1154
|
+
import fs from 'fs';
|
|
1155
|
+
import path from 'path';
|
|
1156
|
+
|
|
1157
|
+
async function downloadFolder(client, folderId, localPath) {
|
|
1158
|
+
// Create local directory
|
|
1159
|
+
if (!fs.existsSync(localPath)) {
|
|
1160
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Get folder contents
|
|
1164
|
+
const items = await client
|
|
1165
|
+
.api(`/me/drive/items/${folderId}/children`)
|
|
1166
|
+
.get();
|
|
1167
|
+
|
|
1168
|
+
for (const item of items.value) {
|
|
1169
|
+
if (item.folder) {
|
|
1170
|
+
// Recursively download subfolder
|
|
1171
|
+
const subPath = path.join(localPath, item.name);
|
|
1172
|
+
await downloadFolder(client, item.id, subPath);
|
|
1173
|
+
} else if (item.file) {
|
|
1174
|
+
// Download file
|
|
1175
|
+
console.log(`Downloading ${item.name}...`);
|
|
1176
|
+
|
|
1177
|
+
const stream = await client
|
|
1178
|
+
.api(`/me/drive/items/${item.id}/content`)
|
|
1179
|
+
.getStream();
|
|
1180
|
+
|
|
1181
|
+
const filePath = path.join(localPath, item.name);
|
|
1182
|
+
const writeStream = fs.createWriteStream(filePath);
|
|
1183
|
+
stream.pipe(writeStream);
|
|
1184
|
+
|
|
1185
|
+
await new Promise((resolve, reject) => {
|
|
1186
|
+
writeStream.on('finish', resolve);
|
|
1187
|
+
writeStream.on('error', reject);
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
console.log(`Downloaded to ${filePath}`);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
### Example 3: Sync Local Folder to OneDrive
|
|
1198
|
+
|
|
1199
|
+
```typescript
|
|
1200
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
1201
|
+
import fs from 'fs';
|
|
1202
|
+
import path from 'path';
|
|
1203
|
+
import { LargeFileUploadTask, StreamUpload } from '@microsoft/microsoft-graph-client';
|
|
1204
|
+
|
|
1205
|
+
async function syncFolderToOneDrive(client, localPath, oneDrivePath) {
|
|
1206
|
+
const files = fs.readdirSync(localPath);
|
|
1207
|
+
|
|
1208
|
+
for (const file of files) {
|
|
1209
|
+
const filePath = path.join(localPath, file);
|
|
1210
|
+
const stats = fs.statSync(filePath);
|
|
1211
|
+
|
|
1212
|
+
if (stats.isDirectory()) {
|
|
1213
|
+
// Create folder in OneDrive
|
|
1214
|
+
const folder = await client
|
|
1215
|
+
.api(`/me/drive/root:/${oneDrivePath}:/children`)
|
|
1216
|
+
.post({
|
|
1217
|
+
name: file,
|
|
1218
|
+
folder: {},
|
|
1219
|
+
'@microsoft.graph.conflictBehavior': 'rename'
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// Recursively sync subfolder
|
|
1223
|
+
await syncFolderToOneDrive(
|
|
1224
|
+
client,
|
|
1225
|
+
filePath,
|
|
1226
|
+
`${oneDrivePath}/${file}`
|
|
1227
|
+
);
|
|
1228
|
+
} else {
|
|
1229
|
+
// Upload file
|
|
1230
|
+
console.log(`Uploading ${file}...`);
|
|
1231
|
+
|
|
1232
|
+
if (stats.size < 4 * 1024 * 1024) {
|
|
1233
|
+
// Small file - simple upload
|
|
1234
|
+
const content = fs.readFileSync(filePath);
|
|
1235
|
+
await client
|
|
1236
|
+
.api(`/me/drive/root:/${oneDrivePath}/${file}:/content`)
|
|
1237
|
+
.put(content);
|
|
1238
|
+
} else {
|
|
1239
|
+
// Large file - resumable upload
|
|
1240
|
+
const uploadSession = await client
|
|
1241
|
+
.api(`/me/drive/root:/${oneDrivePath}/${file}:/createUploadSession`)
|
|
1242
|
+
.post({
|
|
1243
|
+
item: {
|
|
1244
|
+
'@microsoft.graph.conflictBehavior': 'replace'
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
const fileObject = new StreamUpload(
|
|
1249
|
+
fs.createReadStream(filePath),
|
|
1250
|
+
file,
|
|
1251
|
+
stats.size
|
|
1252
|
+
);
|
|
1253
|
+
|
|
1254
|
+
const task = new LargeFileUploadTask(client, fileObject, uploadSession);
|
|
1255
|
+
await task.upload();
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
console.log(`Uploaded ${file}`);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
### Example 4: Search and Download Files by Type
|
|
1266
|
+
|
|
1267
|
+
```typescript
|
|
1268
|
+
async function downloadFilesByType(client, fileExtension, downloadPath) {
|
|
1269
|
+
if (!fs.existsSync(downloadPath)) {
|
|
1270
|
+
fs.mkdirSync(downloadPath, { recursive: true });
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// Search for files
|
|
1274
|
+
const results = await client
|
|
1275
|
+
.api(`/me/drive/root/search(q='.${fileExtension}')`)
|
|
1276
|
+
.select('id,name,size,@microsoft.graph.downloadUrl')
|
|
1277
|
+
.get();
|
|
1278
|
+
|
|
1279
|
+
console.log(`Found ${results.value.length} ${fileExtension} files`);
|
|
1280
|
+
|
|
1281
|
+
for (const file of results.value) {
|
|
1282
|
+
if (file.file) {
|
|
1283
|
+
console.log(`Downloading ${file.name}...`);
|
|
1284
|
+
|
|
1285
|
+
const stream = await client
|
|
1286
|
+
.api(`/me/drive/items/${file.id}/content`)
|
|
1287
|
+
.getStream();
|
|
1288
|
+
|
|
1289
|
+
const filePath = path.join(downloadPath, file.name);
|
|
1290
|
+
const writeStream = fs.createWriteStream(filePath);
|
|
1291
|
+
stream.pipe(writeStream);
|
|
1292
|
+
|
|
1293
|
+
await new Promise((resolve, reject) => {
|
|
1294
|
+
writeStream.on('finish', resolve);
|
|
1295
|
+
writeStream.on('error', reject);
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
console.log('Download complete');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Usage
|
|
1304
|
+
await downloadFilesByType(client, 'pdf', './downloads/pdfs');
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
## Rate Limiting and Throttling
|
|
1309
|
+
|
|
1310
|
+
Microsoft Graph implements throttling to maintain service health:
|
|
1311
|
+
|
|
1312
|
+
```typescript
|
|
1313
|
+
async function makeRequestWithRetry(client, apiPath, maxRetries = 3) {
|
|
1314
|
+
let retries = 0;
|
|
1315
|
+
|
|
1316
|
+
while (retries < maxRetries) {
|
|
1317
|
+
try {
|
|
1318
|
+
return await client.api(apiPath).get();
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
if (error.statusCode === 429) {
|
|
1321
|
+
const retryAfter = parseInt(error.headers?.get('Retry-After') || '5');
|
|
1322
|
+
console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
|
|
1323
|
+
|
|
1324
|
+
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
|
1325
|
+
retries++;
|
|
1326
|
+
} else {
|
|
1327
|
+
throw error;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
throw new Error('Max retries exceeded');
|
|
1333
|
+
}
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
## Advanced Configuration
|
|
1338
|
+
|
|
1339
|
+
### Custom Middleware
|
|
1340
|
+
|
|
1341
|
+
```typescript
|
|
1342
|
+
import { Middleware } from '@microsoft/microsoft-graph-client';
|
|
1343
|
+
|
|
1344
|
+
// Custom logging middleware
|
|
1345
|
+
class LoggingMiddleware implements Middleware {
|
|
1346
|
+
async execute(context) {
|
|
1347
|
+
console.log(`Request: ${context.request}`);
|
|
1348
|
+
|
|
1349
|
+
await this.nextMiddleware.execute(context);
|
|
1350
|
+
|
|
1351
|
+
console.log(`Response: ${context.response.status}`);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// Register middleware
|
|
1356
|
+
const client = Client.initWithMiddleware({
|
|
1357
|
+
authProvider,
|
|
1358
|
+
middleware: new LoggingMiddleware()
|
|
1359
|
+
});
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
### Custom Headers
|
|
1363
|
+
|
|
1364
|
+
```typescript
|
|
1365
|
+
// Add custom headers to requests
|
|
1366
|
+
const result = await client
|
|
1367
|
+
.api('/me/drive/root/children')
|
|
1368
|
+
.header('Prefer', 'HonorNonIndexedQueriesWarningMayFailRandomly')
|
|
1369
|
+
.header('ConsistencyLevel', 'eventual')
|
|
1370
|
+
.get();
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
## Working with Different Drive Types
|
|
1375
|
+
|
|
1376
|
+
```typescript
|
|
1377
|
+
// Personal OneDrive
|
|
1378
|
+
const personalDrive = await client.api('/me/drive').get();
|
|
1379
|
+
|
|
1380
|
+
// User's OneDrive (requires admin permissions)
|
|
1381
|
+
const userDrive = await client.api('/users/{user-id}/drive').get();
|
|
1382
|
+
|
|
1383
|
+
// Group drive
|
|
1384
|
+
const groupDrive = await client.api('/groups/{group-id}/drive').get();
|
|
1385
|
+
|
|
1386
|
+
// SharePoint site drive
|
|
1387
|
+
const siteDrive = await client.api('/sites/{site-id}/drive').get();
|
|
1388
|
+
|
|
1389
|
+
// Specific drive by ID
|
|
1390
|
+
const drive = await client.api('/drives/{drive-id}').get();
|
|
1391
|
+
```
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
## Webhooks and Change Notifications
|
|
1395
|
+
|
|
1396
|
+
```typescript
|
|
1397
|
+
// Subscribe to changes in a drive
|
|
1398
|
+
const subscription = await client
|
|
1399
|
+
.api('/subscriptions')
|
|
1400
|
+
.post({
|
|
1401
|
+
changeType: 'updated',
|
|
1402
|
+
notificationUrl: 'https://your-webhook-endpoint.com/notifications',
|
|
1403
|
+
resource: '/me/drive/root',
|
|
1404
|
+
expirationDateTime: '2025-12-31T18:23:45.9356913Z',
|
|
1405
|
+
clientState: 'secretClientState'
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
console.log('Subscription ID:', subscription.id);
|
|
1409
|
+
|
|
1410
|
+
// Renew subscription
|
|
1411
|
+
const renewed = await client
|
|
1412
|
+
.api(`/subscriptions/${subscription.id}`)
|
|
1413
|
+
.patch({
|
|
1414
|
+
expirationDateTime: '2026-01-31T18:23:45.9356913Z'
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
// Delete subscription
|
|
1418
|
+
await client
|
|
1419
|
+
.api(`/subscriptions/${subscription.id}`)
|
|
1420
|
+
.delete();
|
|
1421
|
+
```
|