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 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 REJECTION
51
+ // Basic OData $filter support for lastModifiedDateTime (RxDB sync)
52
52
  if (req.query.$filter && typeof req.query.$filter === 'string') {
53
- res.status(400).json({
54
- error: {
55
- code: 'invalidRequest',
56
- message: 'Invalid request'
57
- }
58
- });
59
- return;
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)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "microsoft-onedrive-mock",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Mock-Server that simulates being Microsoft OneDrive. Used for testing.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",