@protontech/drive-sdk 0.5.1 → 0.6.1

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 (201) hide show
  1. package/dist/diagnostic/diagnostic.d.ts +7 -4
  2. package/dist/diagnostic/diagnostic.js +16 -8
  3. package/dist/diagnostic/diagnostic.js.map +1 -1
  4. package/dist/diagnostic/index.d.ts +1 -1
  5. package/dist/diagnostic/index.js +9 -1
  6. package/dist/diagnostic/index.js.map +1 -1
  7. package/dist/diagnostic/interface.d.ts +24 -9
  8. package/dist/diagnostic/nodeUtils.d.ts +13 -0
  9. package/dist/diagnostic/nodeUtils.js +90 -0
  10. package/dist/diagnostic/nodeUtils.js.map +1 -0
  11. package/dist/diagnostic/sdkDiagnosticBase.d.ts +36 -0
  12. package/dist/diagnostic/sdkDiagnosticBase.js +305 -0
  13. package/dist/diagnostic/sdkDiagnosticBase.js.map +1 -0
  14. package/dist/diagnostic/sdkDiagnosticMain.d.ts +16 -0
  15. package/dist/diagnostic/sdkDiagnosticMain.js +79 -0
  16. package/dist/diagnostic/sdkDiagnosticMain.js.map +1 -0
  17. package/dist/diagnostic/sdkDiagnosticPhotos.d.ts +13 -0
  18. package/dist/diagnostic/sdkDiagnosticPhotos.js +65 -0
  19. package/dist/diagnostic/sdkDiagnosticPhotos.js.map +1 -0
  20. package/dist/interface/index.d.ts +1 -1
  21. package/dist/interface/upload.d.ts +1 -12
  22. package/dist/internal/devices/interface.d.ts +1 -1
  23. package/dist/internal/devices/manager.js +1 -1
  24. package/dist/internal/devices/manager.js.map +1 -1
  25. package/dist/internal/devices/manager.test.js +3 -3
  26. package/dist/internal/devices/manager.test.js.map +1 -1
  27. package/dist/internal/errors.d.ts +5 -0
  28. package/dist/internal/errors.js +23 -0
  29. package/dist/internal/errors.js.map +1 -1
  30. package/dist/internal/errors.test.js +53 -2
  31. package/dist/internal/errors.test.js.map +1 -1
  32. package/dist/internal/nodes/apiService.d.ts +11 -1
  33. package/dist/internal/nodes/apiService.js +20 -1
  34. package/dist/internal/nodes/apiService.js.map +1 -1
  35. package/dist/internal/nodes/apiService.test.js +1 -1
  36. package/dist/internal/nodes/apiService.test.js.map +1 -1
  37. package/dist/internal/nodes/cryptoReporter.js +3 -0
  38. package/dist/internal/nodes/cryptoReporter.js.map +1 -1
  39. package/dist/internal/nodes/cryptoService.d.ts +4 -0
  40. package/dist/internal/nodes/cryptoService.js +6 -0
  41. package/dist/internal/nodes/cryptoService.js.map +1 -1
  42. package/dist/internal/nodes/index.d.ts +1 -1
  43. package/dist/internal/nodes/index.js +2 -2
  44. package/dist/internal/nodes/index.js.map +1 -1
  45. package/dist/internal/nodes/index.test.js +2 -2
  46. package/dist/internal/nodes/index.test.js.map +1 -1
  47. package/dist/internal/nodes/interface.d.ts +1 -1
  48. package/dist/internal/nodes/nodeName.d.ts +8 -0
  49. package/dist/internal/nodes/nodeName.js +30 -0
  50. package/dist/internal/nodes/nodeName.js.map +1 -0
  51. package/dist/internal/nodes/nodeName.test.d.ts +1 -0
  52. package/dist/internal/nodes/nodeName.test.js +50 -0
  53. package/dist/internal/nodes/nodeName.test.js.map +1 -0
  54. package/dist/internal/nodes/nodesAccess.d.ts +1 -1
  55. package/dist/internal/nodes/nodesAccess.js +4 -4
  56. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  57. package/dist/internal/nodes/nodesAccess.test.js +2 -2
  58. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  59. package/dist/internal/nodes/nodesManagement.d.ts +1 -0
  60. package/dist/internal/nodes/nodesManagement.js +30 -1
  61. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  62. package/dist/internal/nodes/nodesManagement.test.js +61 -0
  63. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  64. package/dist/internal/photos/albums.js +1 -1
  65. package/dist/internal/photos/albums.js.map +1 -1
  66. package/dist/internal/photos/apiService.d.ts +6 -0
  67. package/dist/internal/photos/apiService.js +16 -0
  68. package/dist/internal/photos/apiService.js.map +1 -1
  69. package/dist/internal/photos/index.d.ts +3 -1
  70. package/dist/internal/photos/index.js +6 -2
  71. package/dist/internal/photos/index.js.map +1 -1
  72. package/dist/internal/photos/interface.d.ts +4 -1
  73. package/dist/internal/photos/shares.d.ts +1 -1
  74. package/dist/internal/photos/shares.js +3 -3
  75. package/dist/internal/photos/shares.js.map +1 -1
  76. package/dist/internal/photos/timeline.d.ts +8 -1
  77. package/dist/internal/photos/timeline.js +36 -2
  78. package/dist/internal/photos/timeline.js.map +1 -1
  79. package/dist/internal/photos/timeline.test.d.ts +1 -0
  80. package/dist/internal/photos/timeline.test.js +99 -0
  81. package/dist/internal/photos/timeline.test.js.map +1 -0
  82. package/dist/internal/shares/cryptoService.js +3 -0
  83. package/dist/internal/shares/cryptoService.js.map +1 -1
  84. package/dist/internal/shares/index.d.ts +1 -0
  85. package/dist/internal/shares/index.js +3 -0
  86. package/dist/internal/shares/index.js.map +1 -1
  87. package/dist/internal/shares/interface.d.ts +8 -0
  88. package/dist/internal/shares/interface.js +10 -1
  89. package/dist/internal/shares/interface.js.map +1 -1
  90. package/dist/internal/shares/manager.d.ts +1 -1
  91. package/dist/internal/shares/manager.js +4 -4
  92. package/dist/internal/shares/manager.js.map +1 -1
  93. package/dist/internal/shares/manager.test.js +7 -7
  94. package/dist/internal/shares/manager.test.js.map +1 -1
  95. package/dist/internal/sharing/apiService.d.ts +3 -1
  96. package/dist/internal/sharing/apiService.js +16 -12
  97. package/dist/internal/sharing/apiService.js.map +1 -1
  98. package/dist/internal/sharing/index.d.ts +2 -1
  99. package/dist/internal/sharing/index.js +6 -2
  100. package/dist/internal/sharing/index.js.map +1 -1
  101. package/dist/internal/sharing/interface.d.ts +1 -1
  102. package/dist/internal/sharing/sharingAccess.js +1 -1
  103. package/dist/internal/sharing/sharingAccess.js.map +1 -1
  104. package/dist/internal/sharing/sharingAccess.test.js +1 -1
  105. package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
  106. package/dist/internal/sharing/sharingManagement.js +32 -14
  107. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  108. package/dist/internal/sharing/sharingManagement.test.js +46 -1
  109. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  110. package/dist/internal/sharingPublic/cryptoReporter.js +3 -0
  111. package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -1
  112. package/dist/internal/sharingPublic/index.d.ts +3 -0
  113. package/dist/internal/sharingPublic/index.js +5 -1
  114. package/dist/internal/sharingPublic/index.js.map +1 -1
  115. package/dist/internal/sharingPublic/shares.d.ts +1 -1
  116. package/dist/internal/sharingPublic/shares.js +1 -2
  117. package/dist/internal/sharingPublic/shares.js.map +1 -1
  118. package/dist/internal/upload/apiService.d.ts +0 -9
  119. package/dist/internal/upload/apiService.js +0 -16
  120. package/dist/internal/upload/apiService.js.map +1 -1
  121. package/dist/internal/upload/cryptoService.d.ts +0 -4
  122. package/dist/internal/upload/cryptoService.js +0 -6
  123. package/dist/internal/upload/cryptoService.js.map +1 -1
  124. package/dist/internal/upload/fileUploader.d.ts +0 -1
  125. package/dist/internal/upload/fileUploader.js +0 -4
  126. package/dist/internal/upload/fileUploader.js.map +1 -1
  127. package/dist/internal/upload/manager.d.ts +0 -1
  128. package/dist/internal/upload/manager.js +0 -51
  129. package/dist/internal/upload/manager.js.map +1 -1
  130. package/dist/internal/upload/manager.test.js +0 -61
  131. package/dist/internal/upload/manager.test.js.map +1 -1
  132. package/dist/protonDriveClient.d.ts +17 -2
  133. package/dist/protonDriveClient.js +19 -1
  134. package/dist/protonDriveClient.js.map +1 -1
  135. package/dist/protonDrivePhotosClient.d.ts +119 -4
  136. package/dist/protonDrivePhotosClient.js +183 -10
  137. package/dist/protonDrivePhotosClient.js.map +1 -1
  138. package/dist/protonDrivePublicLinkClient.d.ts +33 -1
  139. package/dist/protonDrivePublicLinkClient.js +51 -2
  140. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  141. package/package.json +1 -1
  142. package/src/diagnostic/diagnostic.ts +27 -8
  143. package/src/diagnostic/index.ts +17 -2
  144. package/src/diagnostic/interface.ts +35 -9
  145. package/src/diagnostic/nodeUtils.ts +100 -0
  146. package/src/diagnostic/{sdkDiagnostic.ts → sdkDiagnosticBase.ts} +204 -204
  147. package/src/diagnostic/sdkDiagnosticMain.ts +95 -0
  148. package/src/diagnostic/sdkDiagnosticPhotos.ts +70 -0
  149. package/src/interface/index.ts +1 -1
  150. package/src/interface/upload.ts +1 -13
  151. package/src/internal/devices/interface.ts +1 -1
  152. package/src/internal/devices/manager.test.ts +3 -3
  153. package/src/internal/devices/manager.ts +1 -1
  154. package/src/internal/errors.test.ts +62 -1
  155. package/src/internal/errors.ts +27 -0
  156. package/src/internal/nodes/apiService.test.ts +1 -1
  157. package/src/internal/nodes/apiService.ts +42 -0
  158. package/src/internal/nodes/cryptoReporter.ts +6 -5
  159. package/src/internal/nodes/cryptoService.ts +9 -0
  160. package/src/internal/nodes/index.test.ts +2 -1
  161. package/src/internal/nodes/index.ts +2 -1
  162. package/src/internal/nodes/interface.ts +1 -1
  163. package/src/internal/nodes/nodeName.test.ts +57 -0
  164. package/src/internal/nodes/nodeName.ts +26 -0
  165. package/src/internal/nodes/nodesAccess.test.ts +2 -2
  166. package/src/internal/nodes/nodesAccess.ts +5 -5
  167. package/src/internal/nodes/nodesManagement.test.ts +65 -0
  168. package/src/internal/nodes/nodesManagement.ts +43 -1
  169. package/src/internal/photos/albums.ts +1 -1
  170. package/src/internal/photos/apiService.ts +40 -0
  171. package/src/internal/photos/index.ts +13 -1
  172. package/src/internal/photos/interface.ts +4 -1
  173. package/src/internal/photos/shares.ts +3 -3
  174. package/src/internal/photos/timeline.test.ts +116 -0
  175. package/src/internal/photos/timeline.ts +47 -2
  176. package/src/internal/shares/cryptoService.ts +5 -1
  177. package/src/internal/shares/index.ts +1 -0
  178. package/src/internal/shares/interface.ts +9 -0
  179. package/src/internal/shares/manager.test.ts +7 -7
  180. package/src/internal/shares/manager.ts +4 -4
  181. package/src/internal/sharing/apiService.ts +15 -12
  182. package/src/internal/sharing/index.ts +7 -1
  183. package/src/internal/sharing/interface.ts +1 -1
  184. package/src/internal/sharing/sharingAccess.test.ts +1 -1
  185. package/src/internal/sharing/sharingAccess.ts +1 -1
  186. package/src/internal/sharing/sharingManagement.test.ts +59 -1
  187. package/src/internal/sharing/sharingManagement.ts +33 -14
  188. package/src/internal/sharingPublic/cryptoReporter.ts +5 -1
  189. package/src/internal/sharingPublic/index.ts +5 -1
  190. package/src/internal/sharingPublic/shares.ts +1 -2
  191. package/src/internal/upload/apiService.ts +0 -39
  192. package/src/internal/upload/cryptoService.ts +0 -9
  193. package/src/internal/upload/fileUploader.ts +0 -5
  194. package/src/internal/upload/manager.test.ts +0 -65
  195. package/src/internal/upload/manager.ts +0 -64
  196. package/src/protonDriveClient.ts +21 -2
  197. package/src/protonDrivePhotosClient.ts +217 -9
  198. package/src/protonDrivePublicLinkClient.ts +77 -2
  199. package/dist/diagnostic/sdkDiagnostic.d.ts +0 -23
  200. package/dist/diagnostic/sdkDiagnostic.js +0 -320
  201. package/dist/diagnostic/sdkDiagnostic.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { getMockLogger } from '../../tests/logger';
2
2
  import {
3
+ Logger,
3
4
  Member,
4
5
  MemberRole,
5
6
  NonProtonInvitation,
@@ -14,8 +15,11 @@ import { SharingCache } from './cache';
14
15
  import { SharingCryptoService } from './cryptoService';
15
16
  import { SharesService, NodesService } from './interface';
16
17
  import { SharingManagement } from './sharingManagement';
18
+ import { ValidationError } from '../../errors';
19
+ import { ErrorCode } from '../apiService';
17
20
 
18
21
  describe('SharingManagement', () => {
22
+ let logger: Logger;
19
23
  let apiService: SharingAPIService;
20
24
  let cache: SharingCache;
21
25
  let cryptoService: SharingCryptoService;
@@ -26,6 +30,8 @@ describe('SharingManagement', () => {
26
30
  let sharingManagement: SharingManagement;
27
31
 
28
32
  beforeEach(() => {
33
+ logger = getMockLogger();
34
+
29
35
  // @ts-expect-error No need to implement all methods for mocking
30
36
  apiService = {
31
37
  createStandardShare: jest.fn().mockReturnValue('newShareId'),
@@ -110,7 +116,7 @@ describe('SharingManagement', () => {
110
116
  };
111
117
 
112
118
  sharingManagement = new SharingManagement(
113
- getMockLogger(),
119
+ logger,
114
120
  apiService,
115
121
  cache,
116
122
  cryptoService,
@@ -225,6 +231,58 @@ describe('SharingManagement', () => {
225
231
  expect(nodesService.notifyNodeChanged).toHaveBeenCalledWith(nodeUid);
226
232
  expect(cache.addSharedByMeNodeUid).toHaveBeenCalledWith(nodeUid);
227
233
  });
234
+
235
+ it('should refresh node info if share already exists', async () => {
236
+ nodesService.getNode = jest
237
+ .fn()
238
+ .mockImplementationOnce((nodeUid) => ({
239
+ nodeUid,
240
+ parentUid: 'parentUid',
241
+ name: { ok: true, value: 'name' },
242
+ }))
243
+ .mockImplementation((nodeUid) => ({
244
+ nodeUid,
245
+ shareId: 'shareId',
246
+ parentUid: 'parentUid',
247
+ name: { ok: true, value: 'name' },
248
+ }));
249
+ apiService.createStandardShare = jest
250
+ .fn()
251
+ .mockRejectedValue(new ValidationError('Share already exists', ErrorCode.ALREADY_EXISTS));
252
+
253
+ const sharingInfo = await sharingManagement.shareNode(nodeUid, { users: ['email'] });
254
+
255
+ expect(sharingInfo).toEqual({
256
+ protonInvitations: [
257
+ {
258
+ uid: 'created-invitation',
259
+ addedByEmail: { ok: true, value: 'volume-email' },
260
+ inviteeEmail: 'email',
261
+ role: 'viewer',
262
+ },
263
+ ],
264
+ nonProtonInvitations: [],
265
+ members: [],
266
+ publicLink: undefined,
267
+ });
268
+
269
+ expect(nodesService.notifyNodeChanged).toHaveBeenCalledWith(nodeUid);
270
+ expect(logger.debug).toHaveBeenCalledWith(
271
+ 'Share already exists for node volumeId~nodeUid, refreshing node',
272
+ );
273
+ expect(apiService.inviteProtonUser).toHaveBeenCalledWith(
274
+ 'shareId',
275
+ {
276
+ addedByEmail: 'volume-email',
277
+ inviteeEmail: 'email',
278
+ role: 'viewer',
279
+ },
280
+ {
281
+ message: undefined,
282
+ nodeName: undefined,
283
+ },
284
+ );
285
+ });
228
286
  });
229
287
 
230
288
  describe('shareNode with share re-use', () => {
@@ -15,6 +15,7 @@ import {
15
15
  ProtonDriveAccount,
16
16
  SharePublicLinkSettingsObject,
17
17
  } from '../../interface';
18
+ import { ErrorCode } from '../apiService';
18
19
  import { splitNodeUid } from '../uids';
19
20
  import { getErrorMessage } from '../errors';
20
21
  import { SharingAPIService } from './apiService';
@@ -147,22 +148,40 @@ export class SharingManagement {
147
148
  throw new ValidationError(c('Error').t`Expiration date cannot be in the past`);
148
149
  }
149
150
 
150
- let contextShareAddress: ContextShareAddress;
151
+ let contextShareAddress: ContextShareAddress | undefined;
151
152
  let currentSharing = await this.getInternalSharingInfo(nodeUid);
152
- if (currentSharing) {
153
- contextShareAddress = await this.nodesService.getRootNodeEmailKey(nodeUid);
154
- } else {
153
+ if (!currentSharing) {
155
154
  const node = await this.nodesService.getNode(nodeUid);
156
- const result = await this.createShare(nodeUid);
157
- currentSharing = {
158
- share: result.share,
159
- nodeName: node.name.ok ? node.name.value : node.name.error.name,
160
- protonInvitations: [],
161
- nonProtonInvitations: [],
162
- members: [],
163
- publicLink: undefined,
164
- };
165
- contextShareAddress = result.contextShareAddress;
155
+ try {
156
+ const result = await this.createShare(nodeUid);
157
+ currentSharing = {
158
+ share: result.share,
159
+ nodeName: node.name.ok ? node.name.value : node.name.error.name,
160
+ protonInvitations: [],
161
+ nonProtonInvitations: [],
162
+ members: [],
163
+ publicLink: undefined,
164
+ };
165
+ contextShareAddress = result.contextShareAddress;
166
+ } catch (error: unknown) {
167
+ // If the share already exists, notify that the node has
168
+ // changed to force refresh and get the latest sharing info
169
+ // again.
170
+ if (error instanceof ValidationError && error.code === ErrorCode.ALREADY_EXISTS) {
171
+ this.logger.debug(`Share already exists for node ${nodeUid}, refreshing node`);
172
+ await this.nodesService.notifyNodeChanged(nodeUid);
173
+ currentSharing = await this.getInternalSharingInfo(nodeUid);
174
+ } else {
175
+ throw error;
176
+ }
177
+ }
178
+ }
179
+
180
+ if (!currentSharing) {
181
+ throw new ValidationError(c('Error').t`Failed to get sharing info for node ${nodeUid}`);
182
+ }
183
+ if (!contextShareAddress) {
184
+ contextShareAddress = await this.nodesService.getRootNodeEmailKey(nodeUid);
166
185
  }
167
186
 
168
187
  const emailOptions: EmailOptions = {
@@ -1,7 +1,7 @@
1
1
  import { c } from 'ttag';
2
2
 
3
3
  import { VERIFICATION_STATUS } from '../../crypto';
4
- import { getVerificationMessage } from '../errors';
4
+ import { getVerificationMessage, isNotApplicationError } from '../errors';
5
5
  import {
6
6
  resultOk,
7
7
  resultError,
@@ -49,6 +49,10 @@ export class SharingPublicCryptoReporter {
49
49
  field: MetricsDecryptionErrorField,
50
50
  error: unknown,
51
51
  ) {
52
+ if (isNotApplicationError(error)) {
53
+ return;
54
+ }
55
+
52
56
  const fromBefore2024 = node.creationTime < new Date('2024-01-01');
53
57
 
54
58
  this.logger.error(
@@ -10,6 +10,7 @@ import { NodeAPIService } from '../nodes/apiService';
10
10
  import { NodesCache } from '../nodes/cache';
11
11
  import { NodesCryptoCache } from '../nodes/cryptoCache';
12
12
  import { NodesCryptoService } from '../nodes/cryptoService';
13
+ import { NodesManagement } from '../nodes/nodesManagement';
13
14
  import { NodesRevisons } from '../nodes/nodesRevisions';
14
15
  import { SharingPublicCryptoReporter } from './cryptoReporter';
15
16
  import { SharingPublicNodesAccess } from './nodes';
@@ -78,7 +79,8 @@ export function initSharingPublicNodesModule(
78
79
  publicShareKey: PrivateKey,
79
80
  publicRootNodeUid: string,
80
81
  ) {
81
- const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService);
82
+ const clientUid = undefined; // No client UID for public context yet.
83
+ const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService, clientUid);
82
84
  const cache = new NodesCache(telemetry.getLogger('nodes-cache'), driveEntitiesCache);
83
85
  const cryptoCache = new NodesCryptoCache(telemetry.getLogger('nodes-cache'), driveCryptoCache);
84
86
  const cryptoReporter = new SharingPublicCryptoReporter(telemetry);
@@ -95,10 +97,12 @@ export function initSharingPublicNodesModule(
95
97
  publicShareKey,
96
98
  publicRootNodeUid,
97
99
  );
100
+ const nodesManagement = new NodesManagement(api, cryptoCache, cryptoService, nodesAccess);
98
101
  const nodesRevisions = new NodesRevisons(telemetry.getLogger('nodes'), api, cryptoService, nodesAccess);
99
102
 
100
103
  return {
101
104
  access: nodesAccess,
105
+ management: nodesManagement,
102
106
  revisions: nodesRevisions,
103
107
  };
104
108
  }
@@ -19,8 +19,7 @@ export class SharingPublicSharesManager {
19
19
  this.publicRootNodeUid = publicRootNodeUid;
20
20
  }
21
21
 
22
- // TODO: Rename to getRootIDs everywhere.
23
- async getOwnVolumeIDs(): Promise<{ volumeId: string; rootNodeId: string; rootNodeUid: string }> {
22
+ async getRootIDs(): Promise<{ volumeId: string; rootNodeId: string; rootNodeUid: string }> {
24
23
  const { volumeId, nodeId: rootNodeId } = splitNodeUid(this.publicRootNodeUid);
25
24
  return { volumeId, rootNodeId, rootNodeUid: this.publicRootNodeUid };
26
25
  }
@@ -6,13 +6,6 @@ import { splitNodeUid, makeNodeUid, splitNodeRevisionUid, makeNodeRevisionUid }
6
6
  import { UploadTokens } from './interface';
7
7
  import { ThumbnailType } from '../../interface';
8
8
 
9
- type PostCheckAvailableHashesRequest = Extract<
10
- drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['requestBody'],
11
- { content: object }
12
- >['content']['application/json'];
13
- type PostCheckAvailableHashesResponse =
14
- drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['responses']['200']['content']['application/json'];
15
-
16
9
  type PostCreateDraftRequest = Extract<
17
10
  drivePaths['/drive/v2/volumes/{volumeID}/files']['post']['requestBody'],
18
11
  { content: object }
@@ -60,38 +53,6 @@ export class UploadAPIService {
60
53
  this.clientUid = clientUid;
61
54
  }
62
55
 
63
- async checkAvailableHashes(
64
- parentNodeUid: string,
65
- hashes: string[],
66
- ): Promise<{
67
- availalbleHashes: string[];
68
- pendingHashes: {
69
- hash: string;
70
- nodeUid: string;
71
- revisionUid: string;
72
- clientUid?: string;
73
- }[];
74
- }> {
75
- const { volumeId, nodeId: parentNodeId } = splitNodeUid(parentNodeUid);
76
- const result = await this.apiService.post<PostCheckAvailableHashesRequest, PostCheckAvailableHashesResponse>(
77
- `drive/v2/volumes/${volumeId}/links/${parentNodeId}/checkAvailableHashes`,
78
- {
79
- Hashes: hashes,
80
- ClientUID: this.clientUid ? [this.clientUid] : null,
81
- },
82
- );
83
-
84
- return {
85
- availalbleHashes: result.AvailableHashes,
86
- pendingHashes: result.PendingHashes.map((hash) => ({
87
- hash: hash.Hash,
88
- nodeUid: makeNodeUid(volumeId, hash.LinkID),
89
- revisionUid: makeNodeRevisionUid(volumeId, hash.LinkID, hash.RevisionID),
90
- clientUid: hash.ClientUID || undefined,
91
- })),
92
- };
93
- }
94
-
95
56
  async createDraft(
96
57
  parentNodeUid: string,
97
58
  node: {
@@ -40,15 +40,6 @@ export class UploadCryptoService {
40
40
  };
41
41
  }
42
42
 
43
- async generateNameHashes(parentHashKey: Uint8Array, names: string[]): Promise<{ name: string; hash: string }[]> {
44
- return Promise.all(
45
- names.map(async (name) => ({
46
- name,
47
- hash: await this.driveCrypto.generateLookupHash(name, parentHashKey),
48
- })),
49
- );
50
- }
51
-
52
43
  async encryptThumbnail(
53
44
  nodeRevisionDraftKeys: NodeRevisionDraftKeys,
54
45
  thumbnail: Thumbnail,
@@ -176,11 +176,6 @@ export class FileUploader extends Uploader {
176
176
  blockVerifier,
177
177
  };
178
178
  }
179
-
180
- async getAvailableName(): Promise<string> {
181
- const availableName = await this.manager.findAvailableName(this.parentFolderUid, this.name);
182
- return availableName;
183
- }
184
179
  }
185
180
 
186
181
  /**
@@ -26,10 +26,6 @@ describe('UploadManager', () => {
26
26
  nodeRevisionUid: 'newNode:nodeRevisionUid',
27
27
  }),
28
28
  deleteDraft: jest.fn(),
29
- checkAvailableHashes: jest.fn().mockResolvedValue({
30
- availalbleHashes: ['name1Hash'],
31
- pendingHashes: [],
32
- }),
33
29
  commitDraftRevision: jest.fn(),
34
30
  };
35
31
  // @ts-expect-error No need to implement all methods for mocking
@@ -58,20 +54,6 @@ describe('UploadManager', () => {
58
54
  email: 'signatureEmail',
59
55
  },
60
56
  }),
61
- generateNameHashes: jest.fn().mockResolvedValue([
62
- {
63
- name: 'name1',
64
- hash: 'name1Hash',
65
- },
66
- {
67
- name: 'name2',
68
- hash: 'name2Hash',
69
- },
70
- {
71
- name: 'name3',
72
- hash: 'name3Hash',
73
- },
74
- ]),
75
57
  commitFile: jest.fn().mockResolvedValue({
76
58
  armoredManifestSignature: 'newNode:armoredManifestSignature',
77
59
  signatureEmail: 'signatureEmail',
@@ -280,53 +262,6 @@ describe('UploadManager', () => {
280
262
  });
281
263
  });
282
264
 
283
- describe('findAvailableName', () => {
284
- it('should find available name', async () => {
285
- apiService.checkAvailableHashes = jest.fn().mockImplementation(() => {
286
- return {
287
- availalbleHashes: ['name3Hash'],
288
- pendingHashes: [],
289
- };
290
- });
291
-
292
- const result = await manager.findAvailableName('parentUid', 'name');
293
- expect(result).toBe('name3');
294
- expect(apiService.checkAvailableHashes).toHaveBeenCalledTimes(1);
295
- expect(apiService.checkAvailableHashes).toHaveBeenCalledWith('parentUid', [
296
- 'name1Hash',
297
- 'name2Hash',
298
- 'name3Hash',
299
- ]);
300
- });
301
-
302
- it('should find available name with multiple pages', async () => {
303
- let firstCall = false;
304
- apiService.checkAvailableHashes = jest.fn().mockImplementation(() => {
305
- if (!firstCall) {
306
- firstCall = true;
307
- return {
308
- // First page has no available hashes
309
- availalbleHashes: [],
310
- pendingHashes: [],
311
- };
312
- }
313
- return {
314
- availalbleHashes: ['name3Hash'],
315
- pendingHashes: [],
316
- };
317
- });
318
-
319
- const result = await manager.findAvailableName('parentUid', 'name');
320
- expect(result).toBe('name3');
321
- expect(apiService.checkAvailableHashes).toHaveBeenCalledTimes(2);
322
- expect(apiService.checkAvailableHashes).toHaveBeenCalledWith('parentUid', [
323
- 'name1Hash',
324
- 'name2Hash',
325
- 'name3Hash',
326
- ]);
327
- });
328
- });
329
-
330
265
  describe('commit draft', () => {
331
266
  const nodeRevisionDraft = {
332
267
  nodeUid: 'newNode:nodeUid',
@@ -173,43 +173,6 @@ export class UploadManager {
173
173
  }
174
174
  }
175
175
 
176
- async findAvailableName(parentFolderUid: string, name: string): Promise<string> {
177
- const { hashKey: parentHashKey } = await this.nodesService.getNodeKeys(parentFolderUid);
178
- if (!parentHashKey) {
179
- throw new ValidationError(c('Error').t`Creating files in non-folders is not allowed`);
180
- }
181
-
182
- const [namePart, extension] = splitExtension(name);
183
-
184
- const batchSize = 10;
185
- let startIndex = 1;
186
- while (true) {
187
- const namesToCheck = [];
188
- for (let i = startIndex; i < startIndex + batchSize; i++) {
189
- namesToCheck.push(joinNameAndExtension(namePart, i, extension));
190
- }
191
-
192
- const hashesToCheck = await this.cryptoService.generateNameHashes(parentHashKey, namesToCheck);
193
-
194
- const { availalbleHashes } = await this.apiService.checkAvailableHashes(
195
- parentFolderUid,
196
- hashesToCheck.map(({ hash }) => hash),
197
- );
198
-
199
- if (!availalbleHashes.length) {
200
- startIndex += batchSize;
201
- continue;
202
- }
203
-
204
- const availableHash = hashesToCheck.find(({ hash }) => hash === availalbleHashes[0]);
205
- if (!availableHash) {
206
- throw Error('Backend returned unexpected hash');
207
- }
208
-
209
- return availableHash.name;
210
- }
211
- }
212
-
213
176
  async deleteDraftNode(nodeUid: string): Promise<void> {
214
177
  try {
215
178
  await this.apiService.deleteDraft(nodeUid);
@@ -294,30 +257,3 @@ export class UploadManager {
294
257
  }
295
258
  }
296
259
  }
297
-
298
- /**
299
- * Split a filename into `[name, extension]`
300
- */
301
- function splitExtension(filename = ''): [string, string] {
302
- const endIdx = filename.lastIndexOf('.');
303
- if (endIdx === -1 || endIdx === filename.length - 1) {
304
- return [filename, ''];
305
- }
306
- return [filename.slice(0, endIdx), filename.slice(endIdx + 1)];
307
- }
308
-
309
- /**
310
- * Join a filename into `name (index).extension`
311
- */
312
- function joinNameAndExtension(name: string, index: number, extension: string): string {
313
- if (!name && !extension) {
314
- return `(${index})`;
315
- }
316
- if (!name) {
317
- return `(${index}).${extension}`;
318
- }
319
- if (!extension) {
320
- return `${name} (${index})`;
321
- }
322
- return `${name} (${index}).${extension}`;
323
- }
@@ -24,7 +24,6 @@ import {
24
24
  UploadMetadata,
25
25
  FileDownloader,
26
26
  FileUploader,
27
- FileRevisionUploader,
28
27
  ThumbnailType,
29
28
  ThumbnailResult,
30
29
  SDKEvent,
@@ -144,6 +143,7 @@ export class ProtonDriveClient {
144
143
  account,
145
144
  cryptoModule,
146
145
  this.shares,
146
+ fullConfig.clientUid,
147
147
  );
148
148
  this.sharing = initSharingModule(
149
149
  telemetry,
@@ -703,6 +703,12 @@ export class ProtonDriveClient {
703
703
  return this.sharing.management.unshareNode(getUid(nodeUid), settings);
704
704
  }
705
705
 
706
+ /**
707
+ * Resend the invitation email to shared node.
708
+ *
709
+ * @param nodeUid - Node entity or its UID string.
710
+ * @param invitationUid - Invitation entity or its UID string.
711
+ */
706
712
  async resendInvitation(
707
713
  nodeUid: NodeOrUid,
708
714
  invitationUid: ProtonInvitationOrUid | NonProtonInvitationOrUid,
@@ -829,11 +835,24 @@ export class ProtonDriveClient {
829
835
  nodeUid: NodeOrUid,
830
836
  metadata: UploadMetadata,
831
837
  signal?: AbortSignal,
832
- ): Promise<FileRevisionUploader> {
838
+ ): Promise<FileUploader> {
833
839
  this.logger.info(`Getting file revision uploader for ${getUid(nodeUid)}`);
834
840
  return this.upload.getFileRevisionUploader(getUid(nodeUid), metadata, signal);
835
841
  }
836
842
 
843
+ /**
844
+ * Returns the available name for the file in the given parent folder.
845
+ *
846
+ * The function will return a name that includes the original name with the
847
+ * available index. The name is guaranteed to be unique in the parent folder.
848
+ *
849
+ * Example new name: `file (2).txt`.
850
+ */
851
+ async getAvailableName(parentFolderUid: NodeOrUid, name: string): Promise<string> {
852
+ this.logger.info(`Getting available name in folder ${getUid(parentFolderUid)}`);
853
+ return this.nodes.management.findAvailableName(getUid(parentFolderUid), name);
854
+ }
855
+
837
856
  /**
838
857
  * Iterates the devices of the user.
839
858
  *