@protontech/drive-sdk 0.0.13 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/dist/cache/index.d.ts +1 -0
  2. package/dist/cache/index.js +3 -1
  3. package/dist/cache/index.js.map +1 -1
  4. package/dist/cache/memoryCache.d.ts +1 -1
  5. package/dist/cache/nullCache.d.ts +14 -0
  6. package/dist/cache/nullCache.js +37 -0
  7. package/dist/cache/nullCache.js.map +1 -0
  8. package/dist/config.d.ts +16 -1
  9. package/dist/config.js +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/crypto/openPGPCrypto.js +2 -0
  12. package/dist/crypto/openPGPCrypto.js.map +1 -1
  13. package/dist/diagnostic/eventsGenerator.d.ts +14 -0
  14. package/dist/diagnostic/eventsGenerator.js +49 -0
  15. package/dist/diagnostic/eventsGenerator.js.map +1 -0
  16. package/dist/diagnostic/httpClient.d.ts +16 -0
  17. package/dist/diagnostic/httpClient.js +81 -0
  18. package/dist/diagnostic/httpClient.js.map +1 -0
  19. package/dist/diagnostic/index.d.ts +10 -0
  20. package/dist/diagnostic/index.js +35 -0
  21. package/dist/diagnostic/index.js.map +1 -0
  22. package/dist/diagnostic/integrityVerificationStream.d.ts +21 -0
  23. package/dist/diagnostic/integrityVerificationStream.js +56 -0
  24. package/dist/diagnostic/integrityVerificationStream.js.map +1 -0
  25. package/dist/diagnostic/interface.d.ts +102 -0
  26. package/dist/diagnostic/interface.js +3 -0
  27. package/dist/diagnostic/interface.js.map +1 -0
  28. package/dist/diagnostic/sdkDiagnostic.d.ts +22 -0
  29. package/dist/diagnostic/sdkDiagnostic.js +216 -0
  30. package/dist/diagnostic/sdkDiagnostic.js.map +1 -0
  31. package/dist/diagnostic/sdkDiagnosticFull.d.ts +18 -0
  32. package/dist/diagnostic/sdkDiagnosticFull.js +35 -0
  33. package/dist/diagnostic/sdkDiagnosticFull.js.map +1 -0
  34. package/dist/diagnostic/telemetry.d.ts +25 -0
  35. package/dist/diagnostic/telemetry.js +70 -0
  36. package/dist/diagnostic/telemetry.js.map +1 -0
  37. package/dist/diagnostic/zipGenerators.d.ts +9 -0
  38. package/dist/diagnostic/zipGenerators.js +64 -0
  39. package/dist/diagnostic/zipGenerators.js.map +1 -0
  40. package/dist/diagnostic/zipGenerators.test.js +144 -0
  41. package/dist/diagnostic/zipGenerators.test.js.map +1 -0
  42. package/dist/errors.d.ts +2 -1
  43. package/dist/errors.js +3 -1
  44. package/dist/errors.js.map +1 -1
  45. package/dist/interface/config.d.ts +26 -0
  46. package/dist/interface/config.js +3 -0
  47. package/dist/interface/config.js.map +1 -0
  48. package/dist/interface/download.d.ts +2 -2
  49. package/dist/interface/events.d.ts +60 -20
  50. package/dist/interface/events.js +11 -1
  51. package/dist/interface/events.js.map +1 -1
  52. package/dist/interface/httpClient.d.ts +0 -14
  53. package/dist/interface/index.d.ts +8 -4
  54. package/dist/interface/index.js +2 -1
  55. package/dist/interface/index.js.map +1 -1
  56. package/dist/interface/nodes.d.ts +9 -0
  57. package/dist/interface/nodes.js.map +1 -1
  58. package/dist/interface/sharing.d.ts +1 -0
  59. package/dist/interface/upload.d.ts +6 -0
  60. package/dist/internal/download/apiService.js +32 -31
  61. package/dist/internal/download/apiService.js.map +1 -1
  62. package/dist/internal/download/fileDownloader.d.ts +2 -2
  63. package/dist/internal/download/fileDownloader.js.map +1 -1
  64. package/dist/internal/events/apiService.d.ts +4 -6
  65. package/dist/internal/events/apiService.js +15 -22
  66. package/dist/internal/events/apiService.js.map +1 -1
  67. package/dist/internal/events/coreEventManager.d.ts +7 -10
  68. package/dist/internal/events/coreEventManager.js +19 -36
  69. package/dist/internal/events/coreEventManager.js.map +1 -1
  70. package/dist/internal/events/coreEventManager.test.js +87 -0
  71. package/dist/internal/events/coreEventManager.test.js.map +1 -0
  72. package/dist/internal/events/eventManager.d.ts +11 -36
  73. package/dist/internal/events/eventManager.js +59 -105
  74. package/dist/internal/events/eventManager.js.map +1 -1
  75. package/dist/internal/events/eventManager.test.js +167 -82
  76. package/dist/internal/events/eventManager.test.js.map +1 -1
  77. package/dist/internal/events/index.d.ts +13 -33
  78. package/dist/internal/events/index.js +56 -72
  79. package/dist/internal/events/index.js.map +1 -1
  80. package/dist/internal/events/interface.d.ts +59 -14
  81. package/dist/internal/events/interface.js +13 -3
  82. package/dist/internal/events/interface.js.map +1 -1
  83. package/dist/internal/events/volumeEventManager.d.ts +7 -17
  84. package/dist/internal/events/volumeEventManager.js +58 -45
  85. package/dist/internal/events/volumeEventManager.js.map +1 -1
  86. package/dist/internal/events/volumeEventManager.test.d.ts +1 -0
  87. package/dist/internal/events/volumeEventManager.test.js +203 -0
  88. package/dist/internal/events/volumeEventManager.test.js.map +1 -0
  89. package/dist/internal/nodes/cache.d.ts +10 -1
  90. package/dist/internal/nodes/cache.js +17 -0
  91. package/dist/internal/nodes/cache.js.map +1 -1
  92. package/dist/internal/nodes/cryptoService.d.ts +1 -1
  93. package/dist/internal/nodes/cryptoService.js.map +1 -1
  94. package/dist/internal/nodes/events.d.ts +7 -83
  95. package/dist/internal/nodes/events.js +43 -217
  96. package/dist/internal/nodes/events.js.map +1 -1
  97. package/dist/internal/nodes/events.test.js +27 -277
  98. package/dist/internal/nodes/events.test.js.map +1 -1
  99. package/dist/internal/nodes/index.d.ts +3 -4
  100. package/dist/internal/nodes/index.js +5 -5
  101. package/dist/internal/nodes/index.js.map +1 -1
  102. package/dist/internal/nodes/nodesAccess.d.ts +15 -0
  103. package/dist/internal/nodes/nodesAccess.js +37 -0
  104. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  105. package/dist/internal/nodes/nodesAccess.test.js +131 -93
  106. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  107. package/dist/internal/nodes/nodesManagement.d.ts +1 -3
  108. package/dist/internal/nodes/nodesManagement.js +12 -26
  109. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  110. package/dist/internal/nodes/nodesManagement.test.js +35 -14
  111. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  112. package/dist/internal/shares/cache.d.ts +2 -0
  113. package/dist/internal/shares/cache.js +2 -0
  114. package/dist/internal/shares/cache.js.map +1 -1
  115. package/dist/internal/shares/manager.d.ts +1 -0
  116. package/dist/internal/shares/manager.js +3 -0
  117. package/dist/internal/shares/manager.js.map +1 -1
  118. package/dist/internal/sharing/apiService.js +1 -0
  119. package/dist/internal/sharing/apiService.js.map +1 -1
  120. package/dist/internal/sharing/cryptoService.js +1 -0
  121. package/dist/internal/sharing/cryptoService.js.map +1 -1
  122. package/dist/internal/sharing/events.d.ts +23 -55
  123. package/dist/internal/sharing/events.js +46 -138
  124. package/dist/internal/sharing/events.js.map +1 -1
  125. package/dist/internal/sharing/events.test.js +77 -180
  126. package/dist/internal/sharing/events.test.js.map +1 -1
  127. package/dist/internal/sharing/index.d.ts +4 -5
  128. package/dist/internal/sharing/index.js +5 -5
  129. package/dist/internal/sharing/index.js.map +1 -1
  130. package/dist/internal/sharing/interface.d.ts +3 -0
  131. package/dist/internal/sharing/sharingManagement.d.ts +2 -3
  132. package/dist/internal/sharing/sharingManagement.js +7 -9
  133. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  134. package/dist/internal/sharing/sharingManagement.test.js +9 -39
  135. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  136. package/dist/internal/upload/apiService.d.ts +2 -3
  137. package/dist/internal/upload/apiService.js +7 -4
  138. package/dist/internal/upload/apiService.js.map +1 -1
  139. package/dist/internal/upload/index.d.ts +2 -2
  140. package/dist/internal/upload/index.js +3 -3
  141. package/dist/internal/upload/index.js.map +1 -1
  142. package/dist/internal/upload/interface.d.ts +2 -0
  143. package/dist/internal/upload/manager.d.ts +5 -5
  144. package/dist/internal/upload/manager.js +19 -50
  145. package/dist/internal/upload/manager.js.map +1 -1
  146. package/dist/internal/upload/manager.test.js +68 -44
  147. package/dist/internal/upload/manager.test.js.map +1 -1
  148. package/dist/internal/upload/streamUploader.js +1 -2
  149. package/dist/internal/upload/streamUploader.js.map +1 -1
  150. package/dist/internal/upload/streamUploader.test.js +1 -1
  151. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  152. package/dist/protonDriveClient.d.ts +19 -162
  153. package/dist/protonDriveClient.js +26 -190
  154. package/dist/protonDriveClient.js.map +1 -1
  155. package/dist/protonDrivePhotosClient.js +3 -2
  156. package/dist/protonDrivePhotosClient.js.map +1 -1
  157. package/package.json +3 -3
  158. package/src/cache/index.ts +1 -0
  159. package/src/cache/memoryCache.ts +1 -1
  160. package/src/cache/nullCache.ts +38 -0
  161. package/src/config.ts +17 -2
  162. package/src/crypto/openPGPCrypto.ts +2 -0
  163. package/src/diagnostic/eventsGenerator.ts +48 -0
  164. package/src/diagnostic/httpClient.ts +80 -0
  165. package/src/diagnostic/index.ts +38 -0
  166. package/src/diagnostic/integrityVerificationStream.ts +56 -0
  167. package/src/diagnostic/interface.ts +158 -0
  168. package/src/diagnostic/sdkDiagnostic.ts +238 -0
  169. package/src/diagnostic/sdkDiagnosticFull.ts +40 -0
  170. package/src/diagnostic/telemetry.ts +71 -0
  171. package/src/diagnostic/zipGenerators.test.ts +177 -0
  172. package/src/diagnostic/zipGenerators.ts +70 -0
  173. package/src/errors.ts +4 -1
  174. package/src/interface/config.ts +28 -0
  175. package/src/interface/download.ts +2 -2
  176. package/src/interface/events.ts +66 -21
  177. package/src/interface/httpClient.ts +0 -16
  178. package/src/interface/index.ts +8 -4
  179. package/src/interface/nodes.ts +21 -12
  180. package/src/interface/sharing.ts +1 -0
  181. package/src/interface/upload.ts +6 -0
  182. package/src/internal/download/apiService.ts +11 -8
  183. package/src/internal/download/fileDownloader.ts +2 -2
  184. package/src/internal/events/apiService.ts +25 -28
  185. package/src/internal/events/coreEventManager.test.ts +101 -0
  186. package/src/internal/events/coreEventManager.ts +20 -45
  187. package/src/internal/events/eventManager.test.ts +201 -88
  188. package/src/internal/events/eventManager.ts +69 -115
  189. package/src/internal/events/index.ts +54 -84
  190. package/src/internal/events/interface.ts +70 -15
  191. package/src/internal/events/volumeEventManager.test.ts +243 -0
  192. package/src/internal/events/volumeEventManager.ts +55 -53
  193. package/src/internal/nodes/cache.ts +20 -2
  194. package/src/internal/nodes/cryptoService.ts +1 -1
  195. package/src/internal/nodes/events.test.ts +29 -335
  196. package/src/internal/nodes/events.ts +45 -253
  197. package/src/internal/nodes/index.ts +6 -8
  198. package/src/internal/nodes/interface.ts +2 -2
  199. package/src/internal/nodes/nodesAccess.test.ts +132 -91
  200. package/src/internal/nodes/nodesAccess.ts +40 -1
  201. package/src/internal/nodes/nodesManagement.test.ts +39 -15
  202. package/src/internal/nodes/nodesManagement.ts +12 -30
  203. package/src/internal/shares/cache.ts +4 -2
  204. package/src/internal/shares/manager.ts +9 -5
  205. package/src/internal/sharing/apiService.ts +1 -0
  206. package/src/internal/sharing/cache.ts +1 -1
  207. package/src/internal/sharing/cryptoService.ts +1 -0
  208. package/src/internal/sharing/events.test.ts +89 -195
  209. package/src/internal/sharing/events.ts +42 -156
  210. package/src/internal/sharing/index.ts +6 -9
  211. package/src/internal/sharing/interface.ts +6 -2
  212. package/src/internal/sharing/sharingManagement.test.ts +10 -40
  213. package/src/internal/sharing/sharingManagement.ts +7 -11
  214. package/src/internal/upload/apiService.ts +5 -6
  215. package/src/internal/upload/index.ts +5 -5
  216. package/src/internal/upload/interface.ts +2 -0
  217. package/src/internal/upload/manager.test.ts +75 -45
  218. package/src/internal/upload/manager.ts +24 -54
  219. package/src/internal/upload/streamUploader.test.ts +0 -1
  220. package/src/internal/upload/streamUploader.ts +0 -2
  221. package/src/protonDriveClient.ts +75 -244
  222. package/src/protonDrivePhotosClient.ts +4 -3
  223. package/dist/internal/events/cache.d.ts +0 -28
  224. package/dist/internal/events/cache.js +0 -67
  225. package/dist/internal/events/cache.js.map +0 -1
  226. package/dist/internal/events/cache.test.js +0 -43
  227. package/dist/internal/events/cache.test.js.map +0 -1
  228. package/dist/internal/nodes/index.test.js +0 -114
  229. package/dist/internal/nodes/index.test.js.map +0 -1
  230. package/src/internal/events/cache.test.ts +0 -47
  231. package/src/internal/events/cache.ts +0 -80
  232. package/src/internal/nodes/index.test.ts +0 -137
  233. /package/dist/{internal/events/cache.test.d.ts → diagnostic/zipGenerators.test.d.ts} +0 -0
  234. /package/dist/internal/{nodes/index.test.d.ts → events/coreEventManager.test.d.ts} +0 -0
@@ -2,7 +2,7 @@ import { getMockLogger } from "../../tests/logger";
2
2
  import { Member, MemberRole, NonProtonInvitation, NonProtonInvitationState, ProtonDriveAccount, ProtonInvitation, PublicLink, resultOk } from "../../interface";
3
3
  import { SharingAPIService } from "./apiService";
4
4
  import { SharingCryptoService } from "./cryptoService";
5
- import { SharesService, NodesService, NodesEvents } from "./interface";
5
+ import { SharesService, NodesService } from "./interface";
6
6
  import { SharingManagement } from "./sharingManagement";
7
7
 
8
8
  describe("SharingManagement", () => {
@@ -11,7 +11,6 @@ describe("SharingManagement", () => {
11
11
  let accountService: ProtonDriveAccount;
12
12
  let sharesService: SharesService;
13
13
  let nodesService: NodesService;
14
- let nodesEvents: NodesEvents;
15
14
 
16
15
  let sharingManagement: SharingManagement;
17
16
 
@@ -82,12 +81,10 @@ describe("SharingManagement", () => {
82
81
  getNodeKeys: jest.fn().mockImplementation((nodeUid) => ({ key: "node-key" })),
83
82
  getNodePrivateAndSessionKeys: jest.fn().mockImplementation((nodeUid) => ({})),
84
83
  getRootNodeEmailKey: jest.fn().mockResolvedValue({ email: "volume-email", addressKey: "volume-key" }),
85
- }
86
- nodesEvents = {
87
- nodeUpdated: jest.fn(),
84
+ notifyNodeChanged: jest.fn(),
88
85
  }
89
86
 
90
- sharingManagement = new SharingManagement(getMockLogger(), apiService, cryptoService, accountService, sharesService, nodesService, nodesEvents);
87
+ sharingManagement = new SharingManagement(getMockLogger(), apiService, cryptoService, accountService, sharesService, nodesService);
91
88
  });
92
89
 
93
90
  describe("getSharingInfo", () => {
@@ -175,6 +172,7 @@ describe("SharingManagement", () => {
175
172
 
176
173
  it("should create share if no exists", async () => {
177
174
  nodesService.getNode = jest.fn().mockImplementation((nodeUid) => ({ nodeUid, parentUid: 'parentUid', name: { ok: true, value: "name" } }));
175
+ nodesService.notifyNodeChanged = jest.fn();
178
176
 
179
177
  const sharingInfo = await sharingManagement.shareNode(nodeUid, { users: ["email"] });
180
178
 
@@ -191,11 +189,7 @@ describe("SharingManagement", () => {
191
189
  });
192
190
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
193
191
  expect(apiService.inviteProtonUser).toHaveBeenCalled();
194
- expect(nodesEvents.nodeUpdated).toHaveBeenCalledWith({
195
- uid: nodeUid,
196
- shareId: "newShareId",
197
- isShared: true,
198
- });
192
+ expect(nodesService.notifyNodeChanged).toHaveBeenCalledWith(nodeUid);
199
193
  });
200
194
  })
201
195
 
@@ -264,7 +258,6 @@ describe("SharingManagement", () => {
264
258
  });
265
259
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
266
260
  expect(apiService.inviteProtonUser).toHaveBeenCalled();
267
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
268
261
  });
269
262
 
270
263
  it("should share node with proton email with specific role", async () => {
@@ -283,7 +276,6 @@ describe("SharingManagement", () => {
283
276
  });
284
277
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
285
278
  expect(apiService.inviteProtonUser).toHaveBeenCalled();
286
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
287
279
  });
288
280
 
289
281
  it("should update existing role", async () => {
@@ -300,7 +292,6 @@ describe("SharingManagement", () => {
300
292
  });
301
293
  expect(apiService.updateInvitation).toHaveBeenCalled();
302
294
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
303
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
304
295
  });
305
296
 
306
297
  it("should be no-op if no change", async () => {
@@ -314,7 +305,6 @@ describe("SharingManagement", () => {
314
305
  });
315
306
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
316
307
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
317
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
318
308
  });
319
309
  });
320
310
 
@@ -340,7 +330,6 @@ describe("SharingManagement", () => {
340
330
  });
341
331
  expect(apiService.updateExternalInvitation).not.toHaveBeenCalled();
342
332
  expect(apiService.inviteExternalUser).toHaveBeenCalled();
343
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
344
333
  });
345
334
 
346
335
  it("should share node with external email with specific role", async () => {
@@ -360,7 +349,6 @@ describe("SharingManagement", () => {
360
349
  });
361
350
  expect(apiService.updateExternalInvitation).not.toHaveBeenCalled();
362
351
  expect(apiService.inviteExternalUser).toHaveBeenCalled();
363
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
364
352
  });
365
353
 
366
354
  it("should update existing role", async () => {
@@ -377,7 +365,6 @@ describe("SharingManagement", () => {
377
365
  });
378
366
  expect(apiService.updateExternalInvitation).toHaveBeenCalled();
379
367
  expect(apiService.inviteExternalUser).not.toHaveBeenCalled();
380
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
381
368
  });
382
369
 
383
370
  it("should be no-op if no change", async () => {
@@ -391,7 +378,6 @@ describe("SharingManagement", () => {
391
378
  });
392
379
  expect(apiService.updateExternalInvitation).not.toHaveBeenCalled();
393
380
  expect(apiService.inviteExternalUser).not.toHaveBeenCalled();
394
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
395
381
  });
396
382
  });
397
383
 
@@ -429,7 +415,6 @@ describe("SharingManagement", () => {
429
415
  expect(apiService.inviteExternalUser).toHaveBeenCalledWith("shareId", expect.objectContaining({
430
416
  inviteeEmail: "email2",
431
417
  }), expect.anything());
432
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
433
418
  });
434
419
  });
435
420
 
@@ -449,7 +434,6 @@ describe("SharingManagement", () => {
449
434
  expect(apiService.updateMember).toHaveBeenCalled();
450
435
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
451
436
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
452
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
453
437
  });
454
438
 
455
439
  it("should be no-op if no change via proton user", async () => {
@@ -464,7 +448,6 @@ describe("SharingManagement", () => {
464
448
  expect(apiService.updateMember).not.toHaveBeenCalled();
465
449
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
466
450
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
467
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
468
451
  });
469
452
 
470
453
  it("should update member via non-proton user", async () => {
@@ -482,7 +465,6 @@ describe("SharingManagement", () => {
482
465
  expect(apiService.updateMember).toHaveBeenCalled();
483
466
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
484
467
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
485
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
486
468
  });
487
469
 
488
470
  it("should be no-op if no change via non-proton user", async () => {
@@ -497,7 +479,6 @@ describe("SharingManagement", () => {
497
479
  expect(apiService.updateMember).not.toHaveBeenCalled();
498
480
  expect(apiService.updateInvitation).not.toHaveBeenCalled();
499
481
  expect(apiService.inviteProtonUser).not.toHaveBeenCalled();
500
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
501
482
  });
502
483
  });
503
484
 
@@ -526,6 +507,7 @@ describe("SharingManagement", () => {
526
507
  expirationTime: undefined,
527
508
  customPassword: undefined,
528
509
  creatorEmail: "volume-email",
510
+ numberOfInitializedDownloads: 0,
529
511
  },
530
512
  });
531
513
  expect(cryptoService.generatePublicLinkPassword).toHaveBeenCalled();
@@ -563,6 +545,7 @@ describe("SharingManagement", () => {
563
545
  expirationTime: new Date('2025-01-02'),
564
546
  customPassword: "customPassword",
565
547
  creatorEmail: "volume-email",
548
+ numberOfInitializedDownloads: 0,
566
549
  },
567
550
  });
568
551
  expect(cryptoService.generatePublicLinkPassword).toHaveBeenCalled();
@@ -661,7 +644,7 @@ describe("SharingManagement", () => {
661
644
  });
662
645
  });
663
646
 
664
- describe("unsahreNode", () => {
647
+ describe("unshareNode", () => {
665
648
  const nodeUid = "volumeId~nodeUid";
666
649
 
667
650
  let invitation: ProtonInvitation;
@@ -697,6 +680,7 @@ describe("SharingManagement", () => {
697
680
  creationTime: new Date(),
698
681
  role: MemberRole.Viewer,
699
682
  url: "url",
683
+ numberOfInitializedDownloads: 0,
700
684
  }
701
685
 
702
686
  apiService.getShareInvitations = jest.fn().mockResolvedValue([
@@ -725,7 +709,6 @@ describe("SharingManagement", () => {
725
709
  expect(apiService.deleteExternalInvitation).not.toHaveBeenCalled();
726
710
  expect(apiService.removeMember).not.toHaveBeenCalled();
727
711
  expect(apiService.removePublicLink).not.toHaveBeenCalled();
728
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
729
712
  });
730
713
 
731
714
  it("should delete external invitation", async () => {
@@ -742,7 +725,6 @@ describe("SharingManagement", () => {
742
725
  expect(apiService.deleteExternalInvitation).toHaveBeenCalled();
743
726
  expect(apiService.removeMember).not.toHaveBeenCalled();
744
727
  expect(apiService.removePublicLink).not.toHaveBeenCalled();
745
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
746
728
  });
747
729
 
748
730
  it("should remove member", async () => {
@@ -759,7 +741,6 @@ describe("SharingManagement", () => {
759
741
  expect(apiService.deleteExternalInvitation).not.toHaveBeenCalled();
760
742
  expect(apiService.removeMember).toHaveBeenCalled();
761
743
  expect(apiService.removePublicLink).not.toHaveBeenCalled();
762
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
763
744
  });
764
745
 
765
746
  it("should be no-op if not shared with email", async () => {
@@ -776,7 +757,6 @@ describe("SharingManagement", () => {
776
757
  expect(apiService.deleteExternalInvitation).not.toHaveBeenCalled();
777
758
  expect(apiService.removeMember).not.toHaveBeenCalled();
778
759
  expect(apiService.removePublicLink).not.toHaveBeenCalled();
779
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
780
760
  });
781
761
 
782
762
  it("should remove public link", async () => {
@@ -793,7 +773,6 @@ describe("SharingManagement", () => {
793
773
  expect(apiService.deleteExternalInvitation).not.toHaveBeenCalled();
794
774
  expect(apiService.removeMember).not.toHaveBeenCalled();
795
775
  expect(apiService.removePublicLink).toHaveBeenCalled();
796
- expect(nodesEvents.nodeUpdated).not.toHaveBeenCalled();
797
776
  });
798
777
 
799
778
  it("should remove share if all is removed", async () => {
@@ -805,11 +784,7 @@ describe("SharingManagement", () => {
805
784
  expect(apiService.deleteExternalInvitation).not.toHaveBeenCalled();
806
785
  expect(apiService.removeMember).not.toHaveBeenCalled();
807
786
  expect(apiService.removePublicLink).not.toHaveBeenCalled();
808
- expect(nodesEvents.nodeUpdated).toHaveBeenCalledWith({
809
- uid: nodeUid,
810
- shareId: undefined,
811
- isShared: false,
812
- });
787
+ expect(nodesService.notifyNodeChanged).toHaveBeenCalled();
813
788
  });
814
789
 
815
790
  it("should remove share if everything is manually removed", async () => {
@@ -824,11 +799,6 @@ describe("SharingManagement", () => {
824
799
  expect(apiService.deleteExternalInvitation).toHaveBeenCalled();
825
800
  expect(apiService.removeMember).toHaveBeenCalled();
826
801
  expect(apiService.removePublicLink).toHaveBeenCalled();
827
- expect(nodesEvents.nodeUpdated).toHaveBeenCalledWith({
828
- uid: nodeUid,
829
- shareId: undefined,
830
- isShared: false,
831
- });
832
802
  });
833
803
  });
834
804
 
@@ -7,7 +7,7 @@ import { splitNodeUid } from "../uids";
7
7
  import { getErrorMessage } from '../errors';
8
8
  import { SharingAPIService } from "./apiService";
9
9
  import { PUBLIC_LINK_GENERATED_PASSWORD_LENGTH, SharingCryptoService } from "./cryptoService";
10
- import { SharesService, NodesService, NodesEvents, ShareResultWithCreatorEmail, PublicLinkWithCreatorEmail } from "./interface";
10
+ import { SharesService, NodesService, ShareResultWithCreatorEmail, PublicLinkWithCreatorEmail } from "./interface";
11
11
 
12
12
  interface InternalShareResult extends ShareResultWithCreatorEmail {
13
13
  share: Share;
@@ -40,7 +40,6 @@ export class SharingManagement {
40
40
  private account: ProtonDriveAccount,
41
41
  private sharesService: SharesService,
42
42
  private nodesService: NodesService,
43
- private nodesEvents: NodesEvents,
44
43
  ) {
45
44
  this.logger = logger;
46
45
  this.apiService = apiService;
@@ -48,7 +47,6 @@ export class SharingManagement {
48
47
  this.account = account;
49
48
  this.sharesService = sharesService;
50
49
  this.nodesService = nodesService;
51
- this.nodesEvents = nodesEvents;
52
50
  }
53
51
 
54
52
  async getSharingInfo(nodeUid: string): Promise<ShareResultWithCreatorEmail | undefined> {
@@ -239,7 +237,7 @@ export class SharingManagement {
239
237
 
240
238
  if (!settings) {
241
239
  this.logger.info(`Unsharing node ${nodeUid}`);
242
- await this.deleteShare(nodeUid, currentSharing.share.shareId);
240
+ await this.deleteShare(currentSharing.share.shareId, nodeUid);
243
241
  return;
244
242
  }
245
243
 
@@ -293,7 +291,7 @@ export class SharingManagement {
293
291
  // update local state immediately.
294
292
  this.logger.info(`Deleting share ${currentSharing.share.shareId} for node ${nodeUid}`);
295
293
  try {
296
- await this.deleteShare(nodeUid, currentSharing.share.shareId);
294
+ await this.deleteShare(currentSharing.share.shareId, nodeUid);
297
295
  } catch (error: unknown) {
298
296
  // If deleting the share fails, we don't want to throw an error
299
297
  // as it might be a race condition that other client updated
@@ -359,9 +357,7 @@ export class SharingManagement {
359
357
  base64NameKeyPacket: keys.base64NameKeyPacket,
360
358
  },
361
359
  );
362
-
363
- await this.nodesEvents.nodeUpdated({ uid: nodeUid, shareId, isShared: true });
364
-
360
+ await this.nodesService.notifyNodeChanged(nodeUid);
365
361
  return {
366
362
  volumeId,
367
363
  shareId,
@@ -370,10 +366,9 @@ export class SharingManagement {
370
366
  }
371
367
  }
372
368
 
373
- private async deleteShare(nodeUid: string, shareId: string): Promise<void> {
369
+ private async deleteShare(shareId: string, nodeUid: string): Promise<void> {
374
370
  await this.apiService.deleteShare(shareId);
375
-
376
- await this.nodesEvents.nodeUpdated({ uid: nodeUid, shareId: undefined, isShared: false });
371
+ await this.nodesService.notifyNodeChanged(nodeUid);
377
372
  }
378
373
 
379
374
  private async inviteProtonUser(share: Share, inviteeEmail: string, role: MemberRole, emailOptions: EmailOptions): Promise<ProtonInvitation> {
@@ -486,6 +481,7 @@ export class SharingManagement {
486
481
  url: `${publicLink.publicUrl}#${generatedPassword}`,
487
482
  customPassword: options.customPassword,
488
483
  expirationTime: options.expiration,
484
+ numberOfInitializedDownloads: 0,
489
485
  creatorEmail,
490
486
  }
491
487
  }
@@ -27,8 +27,9 @@ type PostDeleteNodesRequest = Extract<drivePaths['/drive/v2/volumes/{volumeID}/d
27
27
  type PostDeleteNodesResponse = drivePaths['/drive/v2/volumes/{volumeID}/delete_multiple']['post']['responses']['200']['content']['application/json'];
28
28
 
29
29
  export class UploadAPIService {
30
- constructor(private apiService: DriveAPIService) {
30
+ constructor(private apiService: DriveAPIService, private clientUid: string | undefined) {
31
31
  this.apiService = apiService;
32
+ this.clientUid = clientUid;
32
33
  }
33
34
 
34
35
  async checkAvailableHashes(parentNodeUid: string, hashes: string[]): Promise<{
@@ -46,7 +47,7 @@ export class UploadAPIService {
46
47
  PostCheckAvailableHashesResponse
47
48
  >(`drive/v2/volumes/${volumeId}/links/${parentNodeId}/checkAvailableHashes`, {
48
49
  Hashes: hashes,
49
- ClientUID: null,
50
+ ClientUID: this.clientUid ? [this.clientUid] : null,
50
51
  });
51
52
 
52
53
  return {
@@ -64,7 +65,6 @@ export class UploadAPIService {
64
65
  armoredEncryptedName: string,
65
66
  hash: string,
66
67
  mediaType: string,
67
- clientUID?: string,
68
68
  intendedUploadSize?: number,
69
69
  armoredNodeKey: string,
70
70
  armoredNodePassphrase: string,
@@ -85,7 +85,7 @@ export class UploadAPIService {
85
85
  Name: node.armoredEncryptedName,
86
86
  Hash: node.hash,
87
87
  MIMEType: node.mediaType,
88
- ClientUID: node.clientUID || null,
88
+ ClientUID: this.clientUid || null,
89
89
  IntendedUploadSize: node.intendedUploadSize || null,
90
90
  NodeKey: node.armoredNodeKey,
91
91
  NodePassphrase: node.armoredNodePassphrase,
@@ -103,7 +103,6 @@ export class UploadAPIService {
103
103
 
104
104
  async createDraftRevision(nodeUid: string, revision: {
105
105
  currentRevisionUid: string,
106
- clientUID?: string,
107
106
  intendedUploadSize?: number,
108
107
  }): Promise<{
109
108
  nodeRevisionUid: string,
@@ -116,7 +115,7 @@ export class UploadAPIService {
116
115
  PostCreateDraftRevisionResponse
117
116
  >(`drive/v2/volumes/${volumeId}/files/${nodeId}/revisions`, {
118
117
  CurrentRevisionID: currentRevisionId,
119
- ClientUID: revision.clientUID || null,
118
+ ClientUID: this.clientUid || null,
120
119
  IntendedUploadSize: revision.intendedUploadSize || null,
121
120
  });
122
121
 
@@ -4,14 +4,14 @@ import { DriveCrypto } from "../../crypto";
4
4
  import { UploadAPIService } from "./apiService";
5
5
  import { UploadCryptoService } from "./cryptoService";
6
6
  import { FileUploader, FileRevisionUploader } from "./fileUploader";
7
- import { NodesService, NodesEvents, SharesService } from "./interface";
7
+ import { NodesService, SharesService } from "./interface";
8
8
  import { UploadManager } from "./manager";
9
9
  import { UploadQueue } from "./queue";
10
10
  import { UploadTelemetry } from "./telemetry";
11
11
 
12
12
  /**
13
13
  * Provides facade for the upload module.
14
- *
14
+ *
15
15
  * The upload module is responsible for handling file uploads, including
16
16
  * metadata generation, content upload, API communication, encryption,
17
17
  * and verifications.
@@ -22,13 +22,13 @@ export function initUploadModule(
22
22
  driveCrypto: DriveCrypto,
23
23
  sharesService: SharesService,
24
24
  nodesService: NodesService,
25
- nodesEvents: NodesEvents,
25
+ clientUid?: string,
26
26
  ) {
27
- const api = new UploadAPIService(apiService);
27
+ const api = new UploadAPIService(apiService, clientUid);
28
28
  const cryptoService = new UploadCryptoService(driveCrypto, nodesService);
29
29
 
30
30
  const uploadTelemetry = new UploadTelemetry(telemetry, sharesService);
31
- const manager = new UploadManager(telemetry, api, cryptoService, nodesService, nodesEvents);
31
+ const manager = new UploadManager(telemetry, api, cryptoService, nodesService, clientUid);
32
32
 
33
33
  const queue = new UploadQueue();
34
34
 
@@ -105,6 +105,7 @@ export interface NodesService {
105
105
  addressKey: PrivateKey,
106
106
  addressKeyId: string,
107
107
  }>,
108
+ notifyChildCreated(nodeUid: string): Promise<void>;
108
109
  }
109
110
 
110
111
  /**
@@ -117,6 +118,7 @@ export interface NodesEvents {
117
118
 
118
119
  export interface NodesServiceNode {
119
120
  uid: string,
121
+ parentUid?: string,
120
122
  activeRevision?: Result<Revision, Error>,
121
123
  }
122
124
 
@@ -1,10 +1,10 @@
1
1
  import { ValidationError } from "../../errors";
2
- import { NodeType, ProtonDriveTelemetry, RevisionState, UploadMetadata } from "../../interface";
2
+ import { ProtonDriveTelemetry, UploadMetadata } from "../../interface";
3
3
  import { getMockTelemetry } from "../../tests/telemetry";
4
4
  import { ErrorCode } from "../apiService";
5
5
  import { UploadAPIService } from "./apiService";
6
6
  import { UploadCryptoService } from "./cryptoService";
7
- import { NodesService, NodesEvents } from "./interface";
7
+ import { NodesService } from "./interface";
8
8
  import { UploadManager } from './manager';
9
9
 
10
10
  describe("UploadManager", () => {
@@ -12,10 +12,11 @@ describe("UploadManager", () => {
12
12
  let apiService: UploadAPIService;
13
13
  let cryptoService: UploadCryptoService;
14
14
  let nodesService: NodesService;
15
- let nodesEvents: NodesEvents;
16
15
 
17
16
  let manager: UploadManager;
18
17
 
18
+ const clientUid = 'clientUid';
19
+
19
20
  beforeEach(() => {
20
21
  telemetry = getMockTelemetry();
21
22
  // @ts-expect-error No need to implement all methods for mocking
@@ -73,8 +74,12 @@ describe("UploadManager", () => {
73
74
  armoredExtendedAttributes: "newNode:armoredExtendedAttributes",
74
75
  }),
75
76
  }
76
- // @ts-expect-error No need to implement all methods for mocking
77
77
  nodesService = {
78
+ getNode: jest.fn(async (nodeUid: string) => ({
79
+ uid: nodeUid,
80
+ parentUid: 'parentUid',
81
+
82
+ })),
78
83
  getNodeKeys: jest.fn().mockResolvedValue({
79
84
  hashKey: 'parentNode:hashKey',
80
85
  key: 'parentNode:nodekey',
@@ -83,13 +88,10 @@ describe("UploadManager", () => {
83
88
  email: "signatureEmail",
84
89
  addressId: "addressId",
85
90
  }),
86
- }
87
- nodesEvents = {
88
- nodeCreated: jest.fn(),
89
- nodeUpdated: jest.fn(),
91
+ notifyChildCreated: jest.fn(async (nodeUid: string) => { return }),
90
92
  }
91
93
 
92
- manager = new UploadManager(telemetry, apiService, cryptoService, nodesService, nodesEvents);
94
+ manager = new UploadManager(telemetry, apiService, cryptoService, nodesService, clientUid);
93
95
  });
94
96
 
95
97
  describe("createDraftNode", () => {
@@ -145,7 +147,7 @@ describe("UploadManager", () => {
145
147
  throw new ValidationError("Draft already exists", ErrorCode.ALREADY_EXISTS, {
146
148
  ConflictLinkID: "existingLinkId",
147
149
  ConflictDraftRevisionID: "existingDraftRevisionId",
148
- ConflictDraftClientUID: "existingDraftClientUid",
150
+ ConflictDraftClientUID: clientUid,
149
151
  });
150
152
  }
151
153
  return {
@@ -156,7 +158,6 @@ describe("UploadManager", () => {
156
158
 
157
159
  const result = await manager.createDraftNode("volumeId~parentUid", "name", {} as UploadMetadata);
158
160
 
159
- expect(apiService.deleteDraft).toHaveBeenCalledTimes(1);
160
161
  expect(result).toEqual({
161
162
  nodeUid: "newNode:nodeUid",
162
163
  nodeRevisionUid: "newNode:nodeRevisionUid",
@@ -174,9 +175,69 @@ describe("UploadManager", () => {
174
175
  hash: "newNode:hash",
175
176
  },
176
177
  });
178
+ expect(apiService.deleteDraft).toHaveBeenCalledTimes(1);
177
179
  expect(apiService.deleteDraft).toHaveBeenCalledWith("volumeId~existingLinkId");
178
180
  });
179
181
 
182
+ it("should not delete existing draft if client UID does not match", async () => {
183
+ let firstCall = true;
184
+ apiService.createDraft = jest.fn().mockImplementation(() => {
185
+ if (firstCall) {
186
+ firstCall = false;
187
+ throw new ValidationError("Draft already exists", ErrorCode.ALREADY_EXISTS, {
188
+ ConflictLinkID: "existingLinkId",
189
+ ConflictDraftRevisionID: "existingDraftRevisionId",
190
+ ConflictDraftClientUID: "anotherClientUid",
191
+ });
192
+ }
193
+ return {
194
+ nodeUid: "newNode:nodeUid",
195
+ nodeRevisionUid: "newNode:nodeRevisionUid",
196
+ };
197
+ });
198
+
199
+ const promise = manager.createDraftNode("volumeId~parentUid", "name", {} as UploadMetadata);
200
+
201
+ try {
202
+ await promise;
203
+ } catch (error: any) {
204
+ expect(error.message).toBe("Draft already exists");
205
+ expect(error.ongoingUploadByOtherClient).toBe(true);
206
+ }
207
+ expect(apiService.deleteDraft).not.toHaveBeenCalled();
208
+ });
209
+
210
+ it("should not delete existing draft if client UID is not set", async () => {
211
+ const clientUid = undefined;
212
+ manager = new UploadManager(telemetry, apiService, cryptoService, nodesService, clientUid);
213
+
214
+ let firstCall = true;
215
+ apiService.createDraft = jest.fn().mockImplementation(() => {
216
+ if (firstCall) {
217
+ firstCall = false;
218
+ throw new ValidationError("Draft already exists", ErrorCode.ALREADY_EXISTS, {
219
+ ConflictLinkID: "existingLinkId",
220
+ ConflictDraftRevisionID: "existingDraftRevisionId",
221
+ ConflictDraftClientUID: clientUid,
222
+ });
223
+ }
224
+ return {
225
+ nodeUid: "newNode:nodeUid",
226
+ nodeRevisionUid: "newNode:nodeRevisionUid",
227
+ };
228
+ });
229
+
230
+ const promise = manager.createDraftNode("volumeId~parentUid", "name", {} as UploadMetadata);
231
+
232
+ try {
233
+ await promise;
234
+ } catch (error: any) {
235
+ expect(error.message).toBe("Draft already exists");
236
+ expect(error.ongoingUploadByOtherClient).toBe(true);
237
+ }
238
+ expect(apiService.deleteDraft).not.toHaveBeenCalled();
239
+ });
240
+
180
241
  it("should handle error when deleting existing draft", async () => {
181
242
  let firstCall = true;
182
243
  apiService.createDraft = jest.fn().mockImplementation(() => {
@@ -185,7 +246,7 @@ describe("UploadManager", () => {
185
246
  throw new ValidationError("Draft already exists", ErrorCode.ALREADY_EXISTS, {
186
247
  ConflictLinkID: "existingLinkId",
187
248
  ConflictDraftRevisionID: "existingDraftRevisionId",
188
- ConflictDraftClientUID: "existingDraftClientUid",
249
+ ConflictDraftClientUID: clientUid,
189
250
  });
190
251
  }
191
252
  return {
@@ -280,29 +341,11 @@ describe("UploadManager", () => {
280
341
  manifest,
281
342
  metadata,
282
343
  extendedAttributes,
283
- 1234567,
284
344
  );
285
345
 
286
346
  expect(cryptoService.commitFile).toHaveBeenCalledWith(nodeRevisionDraft.nodeKeys, manifest, expect.anything());
287
347
  expect(apiService.commitDraftRevision).toHaveBeenCalledWith(nodeRevisionDraft.nodeRevisionUid, expect.anything());
288
- expect(nodesEvents.nodeUpdated).toHaveBeenCalledWith({
289
- uid: "newNode:nodeUid",
290
- activeRevision: {
291
- ok: true,
292
- value: {
293
- uid: "newNode:nodeRevisionUid",
294
- state: RevisionState.Active,
295
- creationTime: expect.any(Date),
296
- contentAuthor: { ok: true, value: "signatureEmail" },
297
- storageSize: 1234567,
298
- claimedSize: 123456,
299
- claimedModificationTime: extendedAttributes.modificationTime,
300
- claimedDigests: {
301
- sha1: "sha1",
302
- },
303
- },
304
- },
305
- });
348
+ expect(nodesService.notifyChildCreated).toHaveBeenCalledWith("parentUid");
306
349
  })
307
350
 
308
351
  it("should commit node draft", async () => {
@@ -320,24 +363,11 @@ describe("UploadManager", () => {
320
363
  manifest,
321
364
  metadata,
322
365
  extendedAttributes,
323
- 1234567,
324
366
  );
325
367
 
326
368
  expect(cryptoService.commitFile).toHaveBeenCalledWith(nodeRevisionDraft.nodeKeys, manifest, expect.anything());
327
369
  expect(apiService.commitDraftRevision).toHaveBeenCalledWith(nodeRevisionDraft.nodeRevisionUid, expect.anything());
328
- expect(nodesEvents.nodeCreated).toHaveBeenCalledWith(expect.objectContaining({
329
- uid: "newNode:nodeUid",
330
- parentUid: "parentUid",
331
- type: NodeType.File,
332
- totalStorageSize: 1234567,
333
- activeRevision: {
334
- ok: true,
335
- value: expect.objectContaining({
336
- uid: "newNode:nodeRevisionUid",
337
- storageSize: 1234567,
338
- }),
339
- },
340
- }));
370
+ expect(nodesService.notifyChildCreated).toHaveBeenCalledWith("parentUid");
341
371
  });
342
372
  });
343
373
  });