microsoft-onedrive-mock 1.0.2 → 1.0.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.
- package/dist/index.js +2 -0
- package/dist/routes/v1.js +189 -8
- package/dist/store.d.ts +13 -0
- package/dist/store.js +35 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -85,6 +85,8 @@ const createApp = (config = {}) => {
|
|
|
85
85
|
// Auth Middleware
|
|
86
86
|
const validTokens = ['valid-token', 'another-valid-token'];
|
|
87
87
|
app.use((req, res, next) => {
|
|
88
|
+
if (req.path.startsWith('/v1.0/upload-sessions'))
|
|
89
|
+
return next();
|
|
88
90
|
const authHeaderVal = req.headers.authorization;
|
|
89
91
|
const authHeader = Array.isArray(authHeaderVal) ? authHeaderVal[0] : authHeaderVal;
|
|
90
92
|
if (!authHeader) {
|
package/dist/routes/v1.js
CHANGED
|
@@ -48,15 +48,39 @@ const createV1Router = () => {
|
|
|
48
48
|
app.get('/v1.0/me/drive/items/:itemId/children', (req, res) => {
|
|
49
49
|
const itemId = req.params.itemId;
|
|
50
50
|
let children = store_1.driveStore.listItems(itemId);
|
|
51
|
-
// Basic OData $filter
|
|
51
|
+
// Basic OData $filter support for lastModifiedDateTime (RxDB sync)
|
|
52
52
|
if (req.query.$filter && typeof req.query.$filter === 'string') {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
const filterStr = req.query.$filter;
|
|
54
|
+
// Support formats like: lastModifiedDateTime ge '2026-03-07T16:41:28.611Z'
|
|
55
|
+
const match = filterStr.match(/lastModifiedDateTime\s+(ge|gt|le|lt|eq)\s+'?([^'\s]+)'?/);
|
|
56
|
+
if (match) {
|
|
57
|
+
const operator = match[1];
|
|
58
|
+
const dateVal = match[2];
|
|
59
|
+
children = children.filter(c => {
|
|
60
|
+
const cTime = c.lastModifiedDateTime || "";
|
|
61
|
+
if (operator === 'ge')
|
|
62
|
+
return cTime >= dateVal;
|
|
63
|
+
if (operator === 'gt')
|
|
64
|
+
return cTime > dateVal;
|
|
65
|
+
if (operator === 'le')
|
|
66
|
+
return cTime <= dateVal;
|
|
67
|
+
if (operator === 'lt')
|
|
68
|
+
return cTime < dateVal;
|
|
69
|
+
if (operator === 'eq')
|
|
70
|
+
return cTime === dateVal;
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// For unsupported filters, return 400 to match real API strictness
|
|
76
|
+
res.status(400).json({
|
|
77
|
+
error: {
|
|
78
|
+
code: 'invalidRequest',
|
|
79
|
+
message: 'Invalid request'
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
60
84
|
}
|
|
61
85
|
// Basic OData $orderby support
|
|
62
86
|
if (req.query.$orderby && typeof req.query.$orderby === 'string') {
|
|
@@ -224,6 +248,36 @@ const createV1Router = () => {
|
|
|
224
248
|
res.send(file.content);
|
|
225
249
|
}
|
|
226
250
|
});
|
|
251
|
+
// Path-based Addressing
|
|
252
|
+
app.get('/v1.0/me/drive/items/:parentId\\:/:filename', (req, res) => {
|
|
253
|
+
const params = req.params;
|
|
254
|
+
const parentId = params.parentId;
|
|
255
|
+
let filename = params.filename;
|
|
256
|
+
if (filename.endsWith(':'))
|
|
257
|
+
filename = filename.slice(0, -1);
|
|
258
|
+
filename = decodeURIComponent(filename);
|
|
259
|
+
const parentObj = store_1.driveStore.getItem(parentId) || (parentId === 'root' ? store_1.driveStore.getItem('root') : null);
|
|
260
|
+
if (!parentObj) {
|
|
261
|
+
return res.status(404).json({ error: { code: "itemNotFound", message: "Parent not found" } });
|
|
262
|
+
}
|
|
263
|
+
const child = store_1.driveStore.getItemByName(parentId, filename);
|
|
264
|
+
if (!child) {
|
|
265
|
+
return res.status(404).json({ error: { code: "itemNotFound", message: "Item not found" } });
|
|
266
|
+
}
|
|
267
|
+
res.status(200).json(applySelect(child, req.query.$select));
|
|
268
|
+
});
|
|
269
|
+
app.get('/v1.0/me/drive/root\\:/:filename', (req, res) => {
|
|
270
|
+
const params = req.params;
|
|
271
|
+
let filename = params.filename;
|
|
272
|
+
if (filename.endsWith(':'))
|
|
273
|
+
filename = filename.slice(0, -1);
|
|
274
|
+
filename = decodeURIComponent(filename);
|
|
275
|
+
const child = store_1.driveStore.getItemByName('root', filename);
|
|
276
|
+
if (!child) {
|
|
277
|
+
return res.status(404).json({ error: { code: "itemNotFound", message: "Item not found" } });
|
|
278
|
+
}
|
|
279
|
+
res.status(200).json(applySelect(child, req.query.$select));
|
|
280
|
+
});
|
|
227
281
|
// PUT /me/drive/items/{parent-id}:/{filename}:/content
|
|
228
282
|
app.put('/v1.0/me/drive/items/:parentId\\:/:filename\\:/content', (req, res) => {
|
|
229
283
|
const parentId = req.params.parentId;
|
|
@@ -295,6 +349,133 @@ const createV1Router = () => {
|
|
|
295
349
|
value: result.items
|
|
296
350
|
});
|
|
297
351
|
});
|
|
352
|
+
// ==========================================
|
|
353
|
+
// MISSING ENDPOINTS IMPLEMENTATION
|
|
354
|
+
// ==========================================
|
|
355
|
+
// --- Drives & Shared Content ---
|
|
356
|
+
const defaultDrive = {
|
|
357
|
+
id: "b!default-mock-drive-id",
|
|
358
|
+
driveType: "personal",
|
|
359
|
+
name: "OneDrive",
|
|
360
|
+
owner: { user: { id: "user1", displayName: "Mock User" } }
|
|
361
|
+
};
|
|
362
|
+
app.get('/v1.0/me/drives', (req, res) => {
|
|
363
|
+
res.json({ value: [defaultDrive] });
|
|
364
|
+
});
|
|
365
|
+
app.get('/v1.0/drives/:driveId', (req, res) => {
|
|
366
|
+
res.json(defaultDrive);
|
|
367
|
+
});
|
|
368
|
+
app.get('/v1.0/me/drive/sharedWithMe', (req, res) => {
|
|
369
|
+
res.json({ value: [] });
|
|
370
|
+
});
|
|
371
|
+
app.get('/v1.0/me/drive/recent', (req, res) => {
|
|
372
|
+
res.json({ value: [] });
|
|
373
|
+
});
|
|
374
|
+
app.get('/v1.0/me/drive/following', (req, res) => {
|
|
375
|
+
res.json({ value: [] });
|
|
376
|
+
});
|
|
377
|
+
// --- Special Folders ---
|
|
378
|
+
app.get('/v1.0/me/drive/special/:folderName', (req, res) => {
|
|
379
|
+
const root = store_1.driveStore.getItem('root');
|
|
380
|
+
if (!root)
|
|
381
|
+
return res.status(404).json({ error: { message: "Not found" } });
|
|
382
|
+
res.json(root);
|
|
383
|
+
});
|
|
384
|
+
// --- Advanced Item Operations ---
|
|
385
|
+
app.post('/v1.0/me/drive/items/:itemId/copy', (req, res) => {
|
|
386
|
+
const itemId = req.params.itemId;
|
|
387
|
+
const item = store_1.driveStore.getItem(itemId);
|
|
388
|
+
if (!item)
|
|
389
|
+
return res.status(404).json({ error: { message: "Not found" } });
|
|
390
|
+
const host = req.headers.host || 'localhost';
|
|
391
|
+
const protocol = req.protocol || 'http';
|
|
392
|
+
const baseUrl = `${protocol}://${host}`;
|
|
393
|
+
res.setHeader('Location', `${baseUrl}/v1.0/monitor/mock-copy-job-12345`);
|
|
394
|
+
res.status(202).json({});
|
|
395
|
+
});
|
|
396
|
+
app.post('/v1.0/me/drive/items/:itemId/createLink', (req, res) => {
|
|
397
|
+
const item = store_1.driveStore.getItem(req.params.itemId);
|
|
398
|
+
if (!item)
|
|
399
|
+
return res.status(404).json({ error: { message: "Not found" } });
|
|
400
|
+
res.json({
|
|
401
|
+
id: "mock-link-id",
|
|
402
|
+
roles: ["write"],
|
|
403
|
+
link: { webUrl: "https://mock-onedrive-link/123" }
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
app.get('/v1.0/me/drive/items/:itemId/permissions', (req, res) => {
|
|
407
|
+
res.json({ value: [{ id: "perm1", roles: ["write"] }] });
|
|
408
|
+
});
|
|
409
|
+
app.post('/v1.0/me/drive/items/:itemId/invite', (req, res) => {
|
|
410
|
+
res.json({ value: [{ id: "perm1", roles: ["write"] }] });
|
|
411
|
+
});
|
|
412
|
+
app.delete('/v1.0/me/drive/items/:itemId/permissions/:permId', (req, res) => {
|
|
413
|
+
res.status(204).send();
|
|
414
|
+
});
|
|
415
|
+
app.get('/v1.0/me/drive/items/:itemId/versions', (req, res) => {
|
|
416
|
+
res.json({ value: [] });
|
|
417
|
+
});
|
|
418
|
+
app.post('/v1.0/me/drive/items/:itemId/versions/:versionId/restoreVersion', (req, res) => {
|
|
419
|
+
res.status(204).send();
|
|
420
|
+
});
|
|
421
|
+
app.post('/v1.0/me/drive/items/:itemId/checkout', (req, res) => {
|
|
422
|
+
res.status(204).send();
|
|
423
|
+
});
|
|
424
|
+
app.post('/v1.0/me/drive/items/:itemId/checkin', (req, res) => {
|
|
425
|
+
res.status(204).send();
|
|
426
|
+
});
|
|
427
|
+
app.get('/v1.0/me/drive/items/:itemId/thumbnails', (req, res) => {
|
|
428
|
+
res.json({
|
|
429
|
+
value: [
|
|
430
|
+
{ id: "0", large: { url: "https://mock-thumbnail-url/large" } }
|
|
431
|
+
]
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
app.get('/v1.0/me/drive/items/:itemId/activities', (req, res) => {
|
|
435
|
+
res.json({ value: [] });
|
|
436
|
+
});
|
|
437
|
+
// --- Upload Sessions ---
|
|
438
|
+
const handleCreateUploadSession = (parentId, filename, req, res) => {
|
|
439
|
+
const parentObj = store_1.driveStore.getItem(parentId) || (parentId === 'root' ? store_1.driveStore.getItem('root') : null);
|
|
440
|
+
if (!parentObj) {
|
|
441
|
+
return res.status(404).json({ error: { code: "itemNotFound", message: "Parent not found" } });
|
|
442
|
+
}
|
|
443
|
+
const session = store_1.driveStore.createUploadSession(parentId, filename);
|
|
444
|
+
const host = req.headers.host || 'localhost';
|
|
445
|
+
const protocol = req.protocol || 'http';
|
|
446
|
+
const baseUrl = `${protocol}://${host}`;
|
|
447
|
+
res.status(200).json({
|
|
448
|
+
uploadUrl: `${baseUrl}/v1.0${session.uploadUrl}`,
|
|
449
|
+
expirationDateTime: session.expirationDateTime
|
|
450
|
+
});
|
|
451
|
+
};
|
|
452
|
+
app.post('/v1.0/me/drive/items/:parentId\\:/:filename\\:/createUploadSession', (req, res) => {
|
|
453
|
+
const params = req.params;
|
|
454
|
+
handleCreateUploadSession(params.parentId, decodeURIComponent(params.filename), req, res);
|
|
455
|
+
});
|
|
456
|
+
app.post('/v1.0/me/drive/root\\:/:filename\\:/createUploadSession', (req, res) => {
|
|
457
|
+
const params = req.params;
|
|
458
|
+
handleCreateUploadSession('root', decodeURIComponent(params.filename), req, res);
|
|
459
|
+
});
|
|
460
|
+
// --- Subscriptions ---
|
|
461
|
+
app.post('/v1.0/subscriptions', (req, res) => {
|
|
462
|
+
res.status(201).json({
|
|
463
|
+
id: "mock-subscription-123",
|
|
464
|
+
expirationDateTime: new Date(Date.now() + 2 * 24 * 3600 * 1000).toISOString(),
|
|
465
|
+
clientState: req.body.clientState || "mock-secret"
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
app.get('/v1.0/subscriptions', (req, res) => {
|
|
469
|
+
res.json({ value: [] });
|
|
470
|
+
});
|
|
471
|
+
app.put('/v1.0/upload-sessions/:sessionId', express_1.default.raw({ type: '*/*', limit: '50mb' }), (req, res) => {
|
|
472
|
+
const sessionId = req.params.sessionId;
|
|
473
|
+
const session = store_1.driveStore.getUploadSession(sessionId);
|
|
474
|
+
if (!session)
|
|
475
|
+
return res.status(404).json({ error: { message: "Session not found" } });
|
|
476
|
+
const item = store_1.driveStore.completeUploadSession(sessionId);
|
|
477
|
+
res.status(200).json(item);
|
|
478
|
+
});
|
|
298
479
|
return app;
|
|
299
480
|
};
|
|
300
481
|
exports.createV1Router = createV1Router;
|
package/dist/store.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { DriveItem } from './types';
|
|
|
2
2
|
export declare class DriveStore {
|
|
3
3
|
private items;
|
|
4
4
|
private deltaHistory;
|
|
5
|
+
private uploadSessions;
|
|
5
6
|
constructor();
|
|
6
7
|
private calculateStats;
|
|
7
8
|
createItem(item: Partial<DriveItem> & {
|
|
@@ -9,10 +10,22 @@ export declare class DriveStore {
|
|
|
9
10
|
}, isFolder?: boolean): DriveItem;
|
|
10
11
|
updateItem(id: string, updates: Partial<DriveItem>): DriveItem | null;
|
|
11
12
|
getItem(id: string): DriveItem | null;
|
|
13
|
+
getItemByName(parentId: string, name: string): DriveItem | null;
|
|
12
14
|
deleteItem(id: string): boolean;
|
|
13
15
|
listItems(parentId?: string): DriveItem[];
|
|
14
16
|
getAllItems(): DriveItem[];
|
|
15
17
|
clear(): void;
|
|
18
|
+
createUploadSession(parentId: string, filename: string): {
|
|
19
|
+
uploadUrl: string;
|
|
20
|
+
expirationDateTime: string;
|
|
21
|
+
};
|
|
22
|
+
completeUploadSession(sessionId: string): DriveItem | null;
|
|
23
|
+
getUploadSession(sessionId: string): {
|
|
24
|
+
parentId: string;
|
|
25
|
+
filename: string;
|
|
26
|
+
expirationDateTime: string;
|
|
27
|
+
} | null;
|
|
28
|
+
deleteUploadSession(sessionId: string): void;
|
|
16
29
|
private addDeltaHistory;
|
|
17
30
|
getDeltaToken(): string;
|
|
18
31
|
getDelta(token?: string): {
|
package/dist/store.js
CHANGED
|
@@ -39,6 +39,7 @@ class DriveStore {
|
|
|
39
39
|
constructor() {
|
|
40
40
|
this.items = new Map();
|
|
41
41
|
this.deltaHistory = [];
|
|
42
|
+
this.uploadSessions = new Map();
|
|
42
43
|
}
|
|
43
44
|
calculateStats(content) {
|
|
44
45
|
let buffer;
|
|
@@ -107,6 +108,10 @@ class DriveStore {
|
|
|
107
108
|
getItem(id) {
|
|
108
109
|
return this.items.get(id) || null;
|
|
109
110
|
}
|
|
111
|
+
getItemByName(parentId, name) {
|
|
112
|
+
const children = this.listItems(parentId);
|
|
113
|
+
return children.find(c => c.name === name && !c.deleted) || null;
|
|
114
|
+
}
|
|
110
115
|
deleteItem(id) {
|
|
111
116
|
const item = this.items.get(id);
|
|
112
117
|
if (!item)
|
|
@@ -134,9 +139,39 @@ class DriveStore {
|
|
|
134
139
|
clear() {
|
|
135
140
|
this.items.clear();
|
|
136
141
|
this.deltaHistory = [];
|
|
142
|
+
this.uploadSessions.clear();
|
|
137
143
|
// Always recreate a standard root folder
|
|
138
144
|
this.createItem({ id: 'root', name: 'root' }, true);
|
|
139
145
|
}
|
|
146
|
+
// Upload Sessions
|
|
147
|
+
createUploadSession(parentId, filename) {
|
|
148
|
+
const sessionId = Math.random().toString(36).substring(7);
|
|
149
|
+
const expirationDateTime = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); // 1 day
|
|
150
|
+
this.uploadSessions.set(sessionId, { parentId, filename, expirationDateTime });
|
|
151
|
+
// Return a relative URL path to be prefixed by the router
|
|
152
|
+
return {
|
|
153
|
+
uploadUrl: `/upload-sessions/${sessionId}`,
|
|
154
|
+
expirationDateTime
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
completeUploadSession(sessionId) {
|
|
158
|
+
const session = this.uploadSessions.get(sessionId);
|
|
159
|
+
if (!session)
|
|
160
|
+
return null;
|
|
161
|
+
this.uploadSessions.delete(sessionId);
|
|
162
|
+
const item = this.createItem({
|
|
163
|
+
name: session.filename,
|
|
164
|
+
file: { mimeType: 'application/octet-stream' },
|
|
165
|
+
parentReference: { id: session.parentId }
|
|
166
|
+
});
|
|
167
|
+
return item;
|
|
168
|
+
}
|
|
169
|
+
getUploadSession(sessionId) {
|
|
170
|
+
return this.uploadSessions.get(sessionId) || null;
|
|
171
|
+
}
|
|
172
|
+
deleteUploadSession(sessionId) {
|
|
173
|
+
this.uploadSessions.delete(sessionId);
|
|
174
|
+
}
|
|
140
175
|
// Delta History (simulated changes API)
|
|
141
176
|
addDeltaHistory(item) {
|
|
142
177
|
this.deltaHistory.push(JSON.parse(JSON.stringify(item)));
|