@sinoia/hubdoc-tools 1.3.2 → 1.3.4

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 (68) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/plugins/alfresco/plugin.json +12 -0
  3. package/dist/plugins/aws-s3/plugin.json +12 -0
  4. package/dist/plugins/azure-blob/plugin.json +12 -0
  5. package/dist/plugins/box/plugin.json +12 -0
  6. package/dist/plugins/core/index.d.ts +25 -0
  7. package/dist/plugins/core/index.d.ts.map +1 -0
  8. package/dist/plugins/core/index.js +400 -0
  9. package/dist/plugins/core/index.js.map +1 -0
  10. package/dist/plugins/core/plugin.json +26 -0
  11. package/dist/plugins/dropbox/plugin.json +12 -0
  12. package/dist/plugins/filesystem/index.d.ts +22 -0
  13. package/dist/plugins/filesystem/index.d.ts.map +1 -0
  14. package/dist/plugins/filesystem/index.js +306 -0
  15. package/dist/plugins/filesystem/index.js.map +1 -0
  16. package/dist/plugins/filesystem/plugin.json +12 -0
  17. package/dist/plugins/googledrive/plugin.json +12 -0
  18. package/dist/plugins/nuxeo/plugin.json +12 -0
  19. package/dist/plugins/onedrive/plugin.json +12 -0
  20. package/dist/plugins/opentext/plugin.json +12 -0
  21. package/dist/plugins/sharepoint/plugin.json +12 -0
  22. package/dist/services/hubdoc-api.d.ts +1 -1
  23. package/dist/services/hubdoc-api.js +1 -1
  24. package/dist/services/oauth-token-service.d.ts +1 -1
  25. package/dist/services/oauth-token-service.js +2 -2
  26. package/dist/services/permission-manager.d.ts +1 -1
  27. package/dist/services/permission-manager.js +1 -1
  28. package/dist/src/types/plugins.d.ts +111 -0
  29. package/dist/src/types/plugins.d.ts.map +1 -0
  30. package/dist/src/types/plugins.js +3 -0
  31. package/dist/src/types/plugins.js.map +1 -0
  32. package/dist/src/utils/concurrent-processor.d.ts +63 -0
  33. package/dist/src/utils/concurrent-processor.d.ts.map +1 -0
  34. package/dist/src/utils/concurrent-processor.js +240 -0
  35. package/dist/src/utils/concurrent-processor.js.map +1 -0
  36. package/dist/src/utils/xml-metadata.d.ts +47 -0
  37. package/dist/src/utils/xml-metadata.d.ts.map +1 -0
  38. package/dist/src/utils/xml-metadata.js +200 -0
  39. package/dist/src/utils/xml-metadata.js.map +1 -0
  40. package/dist/types/index.d.ts +1 -1
  41. package/package.json +6 -2
  42. package/plugins/alfresco/index.ts +518 -0
  43. package/plugins/alfresco/plugin.json +12 -0
  44. package/plugins/aws-s3/index.ts +471 -0
  45. package/plugins/aws-s3/plugin.json +12 -0
  46. package/plugins/azure-blob/index.ts +420 -0
  47. package/plugins/azure-blob/plugin.json +12 -0
  48. package/plugins/box/index.ts +495 -0
  49. package/plugins/box/plugin.json +12 -0
  50. package/plugins/core/README.md +122 -0
  51. package/plugins/core/TESTING.md +155 -0
  52. package/plugins/core/index.ts +510 -0
  53. package/plugins/core/plugin.json +26 -0
  54. package/plugins/dropbox/index.ts +451 -0
  55. package/plugins/dropbox/plugin.json +12 -0
  56. package/plugins/filesystem/index.ts +360 -0
  57. package/plugins/filesystem/plugin.json +12 -0
  58. package/plugins/googledrive/index.ts +463 -0
  59. package/plugins/googledrive/plugin.json +12 -0
  60. package/plugins/nuxeo/index.ts +512 -0
  61. package/plugins/nuxeo/plugin.json +12 -0
  62. package/plugins/onedrive/TESTING.md +197 -0
  63. package/plugins/onedrive/index.ts +447 -0
  64. package/plugins/onedrive/plugin.json +12 -0
  65. package/plugins/opentext/index.ts +542 -0
  66. package/plugins/opentext/plugin.json +12 -0
  67. package/plugins/sharepoint/index.ts +509 -0
  68. package/plugins/sharepoint/plugin.json +12 -0
@@ -0,0 +1,197 @@
1
+ # Guide de test du plugin OneDrive
2
+
3
+ Ce guide vous permet de tester le plugin OneDrive avec Microsoft Graph API.
4
+
5
+ ## Prérequis
6
+
7
+ 1. **Azure CLI installé** : `brew install azure-cli` (macOS) ou voir [docs Azure](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
8
+ 2. **Compte Microsoft/Office 365** avec accès OneDrive
9
+ 3. **HubDoc Tools configuré**
10
+
11
+ > ⚠️ **Note** : Pour un test rapide, seul Azure CLI est nécessaire. L'enregistrement d'application Azure AD n'est requis que pour une intégration production.
12
+
13
+ ## Configuration de test
14
+
15
+ ### 1. Enregistrer une application Azure AD
16
+
17
+ 1. Aller sur [Azure Portal](https://portal.azure.com)
18
+ 2. Naviguer vers **Azure Active Directory** → **App registrations**
19
+ 3. Cliquer **New registration**
20
+ 4. Configurer les permissions Microsoft Graph :
21
+ - `Files.Read.All` (pour lire tous les fichiers)
22
+ - `Sites.Read.All` (pour SharePoint)
23
+ - `User.Read` (pour les informations utilisateur)
24
+
25
+ ### 2. Obtenir un token d'accès (Test rapide avec Azure CLI)
26
+
27
+ ```bash
28
+ # Installer Azure CLI si nécessaire
29
+ brew install azure-cli
30
+
31
+ # Se connecter à votre compte Microsoft/Office 365
32
+ az login
33
+
34
+ # Obtenir un token pour Microsoft Graph
35
+ az account get-access-token --resource https://graph.microsoft.com/
36
+ ```
37
+
38
+ La commande retournera un JSON avec le token dans le champ `accessToken` :
39
+ ```json
40
+ {
41
+ "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...",
42
+ "expiresOn": "2025-01-10 15:30:00.000000",
43
+ ...
44
+ }
45
+ ```
46
+
47
+ ### 3. Configuration pour test rapide
48
+
49
+ Pour un test avec Azure CLI (pas besoin d'enregistrer d'app) :
50
+
51
+ ```json
52
+ {
53
+ "clientId": "dummy",
54
+ "clientSecret": "dummy",
55
+ "tenantId": "dummy",
56
+ "accessToken": "YOUR_TOKEN_FROM_AZ_CLI_COMMAND",
57
+ "rootPath": "/",
58
+ "limit": 10
59
+ }
60
+ ```
61
+
62
+ > 💡 Les champs `clientId`, `clientSecret`, et `tenantId` peuvent être des valeurs factices quand vous utilisez un token direct d'Azure CLI.
63
+
64
+ ### 4. Tester la connexion
65
+
66
+ ```bash
67
+ # Lister les plugins disponibles
68
+ npm run dev -- plugins
69
+
70
+ # Vérifier que OneDrive est bien listé
71
+ ```
72
+
73
+ ### 5. Scanner les documents (test rapide)
74
+
75
+ Utiliser le script de test :
76
+
77
+ ```bash
78
+ # Modifier le fichier test-onedrive.js avec vos vraies valeurs
79
+ node test-onedrive.js
80
+ ```
81
+
82
+ ## Résultats attendus
83
+
84
+ ### Scan réussi
85
+ - ✅ Connection: SUCCESS
86
+ - ✅ Documents trouvés avec métadonnées OneDrive
87
+ - ✅ Limitation respectée (paramètre `limit`)
88
+ - ✅ Types de documents variés (Office, PDF, images, etc.)
89
+
90
+ ### Structure des documents
91
+ - **Chemin** : Structure de dossiers OneDrive/SharePoint
92
+ - **Métadonnées** : OneDrive ID, download URL, parent path
93
+ - **Types MIME** : Détection automatique basée sur l'extension
94
+
95
+ ## Test d'intégration complète
96
+
97
+ ### 1. Configuration d'une connexion
98
+ ```bash
99
+ echo '{
100
+ "clientId": "YOUR_CLIENT_ID",
101
+ "clientSecret": "YOUR_CLIENT_SECRET",
102
+ "tenantId": "YOUR_TENANT_ID",
103
+ "accessToken": "YOUR_TOKEN",
104
+ "rootPath": "/Documents",
105
+ "limit": 10
106
+ }' > onedrive-config.json
107
+
108
+ npm run dev -- connect onedrive --config onedrive-config.json --name test-onedrive
109
+ ```
110
+
111
+ ### 2. Scan des documents
112
+ ```bash
113
+ npm run dev -- plugin-scan test-onedrive --output onedrive-mapping.csv
114
+ ```
115
+
116
+ ### 3. Vérification du mapping
117
+ ```bash
118
+ head -10 onedrive-mapping.csv
119
+ ```
120
+
121
+ Le fichier doit contenir :
122
+ - Colonnes : File Path, Target Folder, Workspace, Metadata (JSON), Permissions
123
+ - Métadonnées JSON avec OneDrive ID, download URL
124
+ - Chemins organisés selon la structure OneDrive
125
+
126
+ ## Dépannage
127
+
128
+ ### Token expiré
129
+ ```
130
+ Error: Request failed with status code 401
131
+ ```
132
+ → Renouveler le token avec `az account get-access-token --resource https://graph.microsoft.com/`
133
+
134
+ ### Permissions insuffisantes
135
+ ```
136
+ Error: Insufficient privileges to complete the operation
137
+ ```
138
+ → Vérifier les permissions Microsoft Graph de l'application Azure
139
+
140
+ ### Dossier introuvable
141
+ ```
142
+ Error: The resource could not be found
143
+ ```
144
+ → Vérifier le `rootPath` ou utiliser `/` pour la racine
145
+
146
+ ### Limite de débit
147
+ ```
148
+ Error: Too many requests
149
+ ```
150
+ → Le plugin a déjà des délais intégrés, augmenter si nécessaire
151
+
152
+ ## Performance
153
+
154
+ Avec limit=10 :
155
+ - Durée : ~2-3 secondes
156
+ - Documents : 10 maximum
157
+ - API calls : 1-2 requêtes
158
+
159
+ Avec limit=100 :
160
+ - Durée : ~10-15 secondes
161
+ - Documents : 100 maximum
162
+ - API calls : Pagination automatique
163
+
164
+ ## Paramètres utiles
165
+
166
+ - **limit** : Nombre max de documents (utile pour tests)
167
+ - **rootPath** : Dossier spécifique à scanner (ex: `/Documents`)
168
+ - **accessToken** : Token direct (évite l'OAuth flow complexe)
169
+ - **clientId/clientSecret** : Pour l'authentification OAuth2
170
+
171
+ ## Types de documents OneDrive observés
172
+
173
+ - **Office** : .docx, .xlsx, .pptx (exportés automatiquement)
174
+ - **PDF** : Documents PDF
175
+ - **Images** : .jpg, .png, .gif
176
+ - **Archives** : .zip, .rar
177
+ - **Texte** : .txt, .csv, .json
178
+
179
+ ## Différences avec SharePoint
180
+
181
+ Le plugin fonctionne pour :
182
+ - **OneDrive personnel** : `/me/drive/`
183
+ - **SharePoint** : Modifier l'URL de base si nécessaire
184
+ - **OneDrive Entreprise** : Même API que personnel
185
+
186
+ ## Commandes utiles
187
+
188
+ ```bash
189
+ # Test rapide de connexion
190
+ node test-onedrive.js
191
+
192
+ # Scan avec limite
193
+ npm run dev -- plugin-scan test-onedrive --limit 5
194
+
195
+ # Import vers dossier local
196
+ npm run dev -- plugin-import test-onedrive ./downloads --limit 3
197
+ ```
@@ -0,0 +1,447 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import {
5
+ DocumentSourcePlugin,
6
+ DocumentSource,
7
+ PluginConfig,
8
+ ScanResult,
9
+ PluginImportOptions,
10
+ PluginExportOptions,
11
+ ImportResult,
12
+ ExportResult
13
+ } from '../../src/types/plugins';
14
+
15
+ interface OneDriveConfig extends PluginConfig {
16
+ clientId: string;
17
+ clientSecret: string;
18
+ tenantId: string;
19
+ accessToken?: string;
20
+ refreshToken?: string;
21
+ rootPath?: string; // e.g., '/Documents' or '/Shared Documents'
22
+ limit?: number;
23
+ }
24
+
25
+ interface OneDriveItem {
26
+ id: string;
27
+ name: string;
28
+ size?: number;
29
+ file?: {
30
+ mimeType: string;
31
+ };
32
+ folder?: {
33
+ childCount: number;
34
+ };
35
+ lastModifiedDateTime: string;
36
+ parentReference?: {
37
+ path: string;
38
+ };
39
+ '@microsoft.graph.downloadUrl'?: string;
40
+ }
41
+
42
+ export default class OneDrivePlugin implements DocumentSourcePlugin {
43
+ readonly name = 'onedrive';
44
+ readonly version = '1.0.0';
45
+ readonly description = 'Microsoft OneDrive/SharePoint document source';
46
+ readonly supportedOperations = ['import', 'export', 'both'] as const;
47
+
48
+ private config?: OneDriveConfig;
49
+ private apiClient?: AxiosInstance;
50
+ private baseUrl = 'https://graph.microsoft.com/v1.0';
51
+
52
+ async testConnection(config: PluginConfig): Promise<boolean> {
53
+ try {
54
+ const client = this.createApiClient(config as OneDriveConfig);
55
+ // Try simpler endpoint first
56
+ const response = await client.get('/me');
57
+ return response.status === 200;
58
+ } catch (error: any) {
59
+ console.error(`OneDrive connection test failed: ${error.message}`);
60
+ if (error.response?.data) {
61
+ console.error('API Response:', error.response.data);
62
+ }
63
+ return false;
64
+ }
65
+ }
66
+
67
+ async scan(config: PluginConfig, options?: PluginImportOptions): Promise<ScanResult> {
68
+ this.config = config as OneDriveConfig;
69
+ this.apiClient = this.createApiClient(this.config);
70
+
71
+ const sources: DocumentSource[] = [];
72
+ const errors: string[] = [];
73
+ let totalSize = 0;
74
+
75
+ try {
76
+ const limit = (this.config as any).limit || (options as any)?.limit;
77
+ console.log(`🔍 Scanning OneDrive documents${limit ? ` (limit: ${limit})` : ''}...`);
78
+
79
+ const rootPath = this.config.rootPath || '/';
80
+ const items = await this.scanFolder(rootPath, options);
81
+
82
+ let processedCount = 0;
83
+ for (const item of items) {
84
+ if (item.file) { // Only process files, not folders
85
+ const source: DocumentSource = {
86
+ id: item.id,
87
+ name: item.name,
88
+ path: this.getRelativePath(item),
89
+ size: item.size || 0,
90
+ mimeType: item.file.mimeType,
91
+ lastModified: new Date(item.lastModifiedDateTime),
92
+ metadata: {
93
+ oneDriveId: item.id,
94
+ downloadUrl: item['@microsoft.graph.downloadUrl'],
95
+ parentPath: item.parentReference?.path
96
+ }
97
+ };
98
+
99
+ // Apply filters
100
+ if (this.shouldIncludeSource(source, options)) {
101
+ sources.push(source);
102
+ totalSize += source.size;
103
+ processedCount++;
104
+
105
+ // Check limit
106
+ if (limit && processedCount >= limit) {
107
+ console.log(`📏 Reached limit of ${limit} files`);
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ return {
115
+ sources,
116
+ totalCount: sources.length,
117
+ totalSize,
118
+ errors
119
+ };
120
+ } catch (error: any) {
121
+ return {
122
+ sources: [],
123
+ totalCount: 0,
124
+ totalSize: 0,
125
+ errors: [`OneDrive scan failed: ${error.message}`]
126
+ };
127
+ }
128
+ }
129
+
130
+ private async scanFolder(folderPath: string, options?: PluginImportOptions): Promise<OneDriveItem[]> {
131
+ if (!this.apiClient) throw new Error('API client not initialized');
132
+
133
+ const items: OneDriveItem[] = [];
134
+ let nextLink: string | undefined;
135
+
136
+ do {
137
+ try {
138
+ // Handle root path correctly
139
+ let url: string;
140
+ if (nextLink) {
141
+ url = nextLink;
142
+ } else if (!folderPath || folderPath === '/') {
143
+ url = '/me/drive/root/children';
144
+ } else {
145
+ url = `/me/drive/root:${folderPath}:/children`;
146
+ }
147
+
148
+ const response = await this.apiClient.get(url);
149
+
150
+ const folderItems: OneDriveItem[] = response.data.value;
151
+ items.push(...folderItems);
152
+
153
+ // Recursively scan subfolders
154
+ for (const item of folderItems) {
155
+ if (item.folder && item.folder.childCount > 0) {
156
+ const subPath = `${folderPath}/${item.name}`;
157
+ const subItems = await this.scanFolder(subPath, options);
158
+ items.push(...subItems);
159
+ }
160
+ }
161
+
162
+ nextLink = response.data['@odata.nextLink'];
163
+ } catch (error: any) {
164
+ console.warn(`Warning: Failed to scan folder ${folderPath}: ${error.message}`);
165
+ break;
166
+ }
167
+ } while (nextLink);
168
+
169
+ return items;
170
+ }
171
+
172
+ async import(
173
+ config: PluginConfig,
174
+ sources: DocumentSource[],
175
+ targetDir: string,
176
+ options?: PluginImportOptions
177
+ ): Promise<ImportResult[]> {
178
+ this.config = config as OneDriveConfig;
179
+ this.apiClient = this.createApiClient(this.config);
180
+
181
+ const results: ImportResult[] = [];
182
+ const batchSize = options?.batchSize || 5; // OneDrive has rate limits
183
+
184
+ // Process in batches to respect API limits
185
+ for (let i = 0; i < sources.length; i += batchSize) {
186
+ const batch = sources.slice(i, i + batchSize);
187
+
188
+ for (const source of batch) {
189
+ const result = await this.importSingle(source, targetDir);
190
+ results.push(result);
191
+
192
+ // Small delay to respect rate limits
193
+ await this.sleep(100);
194
+ }
195
+ }
196
+
197
+ return results;
198
+ }
199
+
200
+ private async importSingle(source: DocumentSource, targetDir: string): Promise<ImportResult> {
201
+ try {
202
+ if (!this.apiClient) throw new Error('API client not initialized');
203
+
204
+ // Get download URL if not in metadata
205
+ let downloadUrl = source.metadata?.downloadUrl;
206
+
207
+ if (!downloadUrl) {
208
+ const response = await this.apiClient.get(`/me/drive/items/${source.id}`);
209
+ downloadUrl = response.data['@microsoft.graph.downloadUrl'];
210
+ }
211
+
212
+ if (!downloadUrl) {
213
+ throw new Error('No download URL available');
214
+ }
215
+
216
+ // Download file
217
+ const response = await axios.get(downloadUrl, {
218
+ responseType: 'stream'
219
+ });
220
+
221
+ const targetPath = path.join(targetDir, source.path);
222
+ const targetDirectory = path.dirname(targetPath);
223
+
224
+ await fs.ensureDir(targetDirectory);
225
+
226
+ const writer = fs.createWriteStream(targetPath);
227
+ response.data.pipe(writer);
228
+
229
+ return new Promise((resolve) => {
230
+ writer.on('finish', () => {
231
+ resolve({
232
+ success: true,
233
+ source,
234
+ localPath: targetPath,
235
+ bytesTransferred: source.size
236
+ });
237
+ });
238
+
239
+ writer.on('error', (error) => {
240
+ resolve({
241
+ success: false,
242
+ source,
243
+ error: error.message
244
+ });
245
+ });
246
+ });
247
+ } catch (error: any) {
248
+ return {
249
+ success: false,
250
+ source,
251
+ error: error.message
252
+ };
253
+ }
254
+ }
255
+
256
+ async export?(
257
+ config: PluginConfig,
258
+ localSources: DocumentSource[],
259
+ options?: PluginExportOptions
260
+ ): Promise<ExportResult[]> {
261
+ this.config = config as OneDriveConfig;
262
+ this.apiClient = this.createApiClient(this.config);
263
+
264
+ const results: ExportResult[] = [];
265
+
266
+ for (const source of localSources) {
267
+ try {
268
+ const targetPath = options?.preserveStructure
269
+ ? `${options.targetPath}/${source.path}`
270
+ : `${options?.targetPath || ''}/${source.name}`;
271
+
272
+ // Create folder structure if needed
273
+ const folderPath = path.dirname(targetPath);
274
+ if (folderPath && folderPath !== '.') {
275
+ await this.createFolderStructure(folderPath);
276
+ }
277
+
278
+ // Upload file
279
+ const fileContent = await fs.readFile(source.id);
280
+ const uploadUrl = `/me/drive/root:${targetPath}:/content`;
281
+
282
+ await this.apiClient!.put(uploadUrl, fileContent, {
283
+ headers: {
284
+ 'Content-Type': source.mimeType
285
+ }
286
+ });
287
+
288
+ results.push({
289
+ success: true,
290
+ targetPath,
291
+ source,
292
+ bytesTransferred: source.size
293
+ });
294
+ } catch (error: any) {
295
+ results.push({
296
+ success: false,
297
+ targetPath: options?.targetPath || '',
298
+ source,
299
+ error: error.message
300
+ });
301
+ }
302
+ }
303
+
304
+ return results;
305
+ }
306
+
307
+ private async createFolderStructure(folderPath: string): Promise<void> {
308
+ if (!this.apiClient) throw new Error('API client not initialized');
309
+
310
+ const parts = folderPath.split('/').filter(part => part.length > 0);
311
+ let currentPath = '';
312
+
313
+ for (const part of parts) {
314
+ currentPath += `/${part}`;
315
+
316
+ try {
317
+ // Try to get the folder
318
+ await this.apiClient.get(`/me/drive/root:${currentPath}`);
319
+ } catch (error: any) {
320
+ if (error.response?.status === 404) {
321
+ // Folder doesn't exist, create it
322
+ const parentPath = path.dirname(currentPath) || '/';
323
+ await this.apiClient.post(`/me/drive/root:${parentPath}:/children`, {
324
+ name: part,
325
+ folder: {}
326
+ });
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ getConfigSchema(): Record<string, any> {
333
+ return {
334
+ type: 'object',
335
+ properties: {
336
+ clientId: {
337
+ type: 'string',
338
+ description: 'Azure App Client ID (can be dummy when using accessToken)',
339
+ required: false
340
+ },
341
+ clientSecret: {
342
+ type: 'string',
343
+ description: 'Azure App Client Secret (can be dummy when using accessToken)',
344
+ required: false
345
+ },
346
+ tenantId: {
347
+ type: 'string',
348
+ description: 'Azure Tenant ID (can be dummy when using accessToken)',
349
+ required: false
350
+ },
351
+ accessToken: {
352
+ type: 'string',
353
+ description: 'OAuth2 Access Token (required - get with az account get-access-token)',
354
+ required: true
355
+ },
356
+ refreshToken: {
357
+ type: 'string',
358
+ description: 'OAuth2 Refresh Token (will be obtained automatically)',
359
+ required: false
360
+ },
361
+ rootPath: {
362
+ type: 'string',
363
+ description: 'Root path to scan (e.g., /Documents)',
364
+ default: '/'
365
+ },
366
+ limit: {
367
+ type: 'number',
368
+ description: 'Maximum number of documents to scan (useful for testing)',
369
+ required: false
370
+ }
371
+ },
372
+ required: ['accessToken']
373
+ };
374
+ }
375
+
376
+ async initialize(config: PluginConfig): Promise<void> {
377
+ this.config = config as OneDriveConfig;
378
+
379
+ // If no access token, we need to authenticate
380
+ if (!this.config.accessToken) {
381
+ console.log('ℹ️ OneDrive authentication required. Please obtain an access token first.');
382
+ console.log('💡 You can use Azure CLI: az account get-access-token --resource https://graph.microsoft.com/');
383
+ throw new Error('OneDrive authentication required. Please provide an access token.');
384
+ }
385
+
386
+ // Set default values for required fields if not provided
387
+ if (!this.config.clientId) this.config.clientId = 'dummy';
388
+ if (!this.config.clientSecret) this.config.clientSecret = 'dummy';
389
+ if (!this.config.tenantId) this.config.tenantId = 'dummy';
390
+
391
+ this.apiClient = this.createApiClient(this.config);
392
+ }
393
+
394
+ async destroy(): Promise<void> {
395
+ this.config = undefined;
396
+ this.apiClient = undefined;
397
+ }
398
+
399
+ private createApiClient(config: OneDriveConfig): AxiosInstance {
400
+ return axios.create({
401
+ baseURL: this.baseUrl,
402
+ headers: {
403
+ 'Authorization': `Bearer ${config.accessToken}`,
404
+ 'Content-Type': 'application/json'
405
+ },
406
+ timeout: 30000
407
+ });
408
+ }
409
+
410
+ private getRelativePath(item: OneDriveItem): string {
411
+ if (!item.parentReference?.path) {
412
+ return item.name;
413
+ }
414
+
415
+ // Remove the root drive path
416
+ const parentPath = item.parentReference.path
417
+ .replace(/^\/drive\/root:?/, '')
418
+ .replace(/^\//, '');
419
+
420
+ return parentPath ? `${parentPath}/${item.name}` : item.name;
421
+ }
422
+
423
+ private shouldIncludeSource(source: DocumentSource, options?: PluginImportOptions): boolean {
424
+ // Apply size filter
425
+ if (options?.filters?.maxSize && source.size > options.filters.maxSize) {
426
+ return false;
427
+ }
428
+
429
+ // Apply date range filter
430
+ if (options?.filters?.dateRange) {
431
+ const { from, to } = options.filters.dateRange;
432
+ if (from && source.lastModified < from) return false;
433
+ if (to && source.lastModified > to) return false;
434
+ }
435
+
436
+ // Apply MIME type filter
437
+ if (options?.filters?.mimeTypes && !options.filters.mimeTypes.includes(source.mimeType)) {
438
+ return false;
439
+ }
440
+
441
+ return true;
442
+ }
443
+
444
+ private sleep(ms: number): Promise<void> {
445
+ return new Promise(resolve => setTimeout(resolve, ms));
446
+ }
447
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "onedrive",
3
+ "version": "1.0.0",
4
+ "description": "Microsoft OneDrive document source plugin",
5
+ "author": "HubDoc Tools",
6
+ "main": "index.js",
7
+ "hubdocToolVersion": "^1.0.0",
8
+ "dependencies": {
9
+ "axios": "^1.5.0",
10
+ "fs-extra": "^11.1.0"
11
+ }
12
+ }