@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.
- package/dist/diagnostic/diagnostic.d.ts +7 -4
- package/dist/diagnostic/diagnostic.js +16 -8
- package/dist/diagnostic/diagnostic.js.map +1 -1
- package/dist/diagnostic/index.d.ts +1 -1
- package/dist/diagnostic/index.js +9 -1
- package/dist/diagnostic/index.js.map +1 -1
- package/dist/diagnostic/interface.d.ts +24 -9
- package/dist/diagnostic/nodeUtils.d.ts +13 -0
- package/dist/diagnostic/nodeUtils.js +90 -0
- package/dist/diagnostic/nodeUtils.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticBase.d.ts +36 -0
- package/dist/diagnostic/sdkDiagnosticBase.js +305 -0
- package/dist/diagnostic/sdkDiagnosticBase.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticMain.d.ts +16 -0
- package/dist/diagnostic/sdkDiagnosticMain.js +79 -0
- package/dist/diagnostic/sdkDiagnosticMain.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.d.ts +13 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.js +65 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.js.map +1 -0
- package/dist/interface/index.d.ts +1 -1
- package/dist/interface/upload.d.ts +1 -12
- package/dist/internal/devices/interface.d.ts +1 -1
- package/dist/internal/devices/manager.js +1 -1
- package/dist/internal/devices/manager.js.map +1 -1
- package/dist/internal/devices/manager.test.js +3 -3
- package/dist/internal/devices/manager.test.js.map +1 -1
- package/dist/internal/errors.d.ts +5 -0
- package/dist/internal/errors.js +23 -0
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/errors.test.js +53 -2
- package/dist/internal/errors.test.js.map +1 -1
- package/dist/internal/nodes/apiService.d.ts +11 -1
- package/dist/internal/nodes/apiService.js +20 -1
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +1 -1
- package/dist/internal/nodes/apiService.test.js.map +1 -1
- package/dist/internal/nodes/cryptoReporter.js +3 -0
- package/dist/internal/nodes/cryptoReporter.js.map +1 -1
- package/dist/internal/nodes/cryptoService.d.ts +4 -0
- package/dist/internal/nodes/cryptoService.js +6 -0
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/index.d.ts +1 -1
- package/dist/internal/nodes/index.js +2 -2
- package/dist/internal/nodes/index.js.map +1 -1
- package/dist/internal/nodes/index.test.js +2 -2
- package/dist/internal/nodes/index.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +1 -1
- package/dist/internal/nodes/nodeName.d.ts +8 -0
- package/dist/internal/nodes/nodeName.js +30 -0
- package/dist/internal/nodes/nodeName.js.map +1 -0
- package/dist/internal/nodes/nodeName.test.d.ts +1 -0
- package/dist/internal/nodes/nodeName.test.js +50 -0
- package/dist/internal/nodes/nodeName.test.js.map +1 -0
- package/dist/internal/nodes/nodesAccess.d.ts +1 -1
- package/dist/internal/nodes/nodesAccess.js +4 -4
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +2 -2
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.d.ts +1 -0
- package/dist/internal/nodes/nodesManagement.js +30 -1
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +61 -0
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/photos/albums.js +1 -1
- package/dist/internal/photos/albums.js.map +1 -1
- package/dist/internal/photos/apiService.d.ts +6 -0
- package/dist/internal/photos/apiService.js +16 -0
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/index.d.ts +3 -1
- package/dist/internal/photos/index.js +6 -2
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/interface.d.ts +4 -1
- package/dist/internal/photos/shares.d.ts +1 -1
- package/dist/internal/photos/shares.js +3 -3
- package/dist/internal/photos/shares.js.map +1 -1
- package/dist/internal/photos/timeline.d.ts +8 -1
- package/dist/internal/photos/timeline.js +36 -2
- package/dist/internal/photos/timeline.js.map +1 -1
- package/dist/internal/photos/timeline.test.d.ts +1 -0
- package/dist/internal/photos/timeline.test.js +99 -0
- package/dist/internal/photos/timeline.test.js.map +1 -0
- package/dist/internal/shares/cryptoService.js +3 -0
- package/dist/internal/shares/cryptoService.js.map +1 -1
- package/dist/internal/shares/index.d.ts +1 -0
- package/dist/internal/shares/index.js +3 -0
- package/dist/internal/shares/index.js.map +1 -1
- package/dist/internal/shares/interface.d.ts +8 -0
- package/dist/internal/shares/interface.js +10 -1
- package/dist/internal/shares/interface.js.map +1 -1
- package/dist/internal/shares/manager.d.ts +1 -1
- package/dist/internal/shares/manager.js +4 -4
- package/dist/internal/shares/manager.js.map +1 -1
- package/dist/internal/shares/manager.test.js +7 -7
- package/dist/internal/shares/manager.test.js.map +1 -1
- package/dist/internal/sharing/apiService.d.ts +3 -1
- package/dist/internal/sharing/apiService.js +16 -12
- package/dist/internal/sharing/apiService.js.map +1 -1
- package/dist/internal/sharing/index.d.ts +2 -1
- package/dist/internal/sharing/index.js +6 -2
- package/dist/internal/sharing/index.js.map +1 -1
- package/dist/internal/sharing/interface.d.ts +1 -1
- package/dist/internal/sharing/sharingAccess.js +1 -1
- package/dist/internal/sharing/sharingAccess.js.map +1 -1
- package/dist/internal/sharing/sharingAccess.test.js +1 -1
- package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.js +32 -14
- package/dist/internal/sharing/sharingManagement.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.test.js +46 -1
- package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
- package/dist/internal/sharingPublic/cryptoReporter.js +3 -0
- package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +3 -0
- package/dist/internal/sharingPublic/index.js +5 -1
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/shares.d.ts +1 -1
- package/dist/internal/sharingPublic/shares.js +1 -2
- package/dist/internal/sharingPublic/shares.js.map +1 -1
- package/dist/internal/upload/apiService.d.ts +0 -9
- package/dist/internal/upload/apiService.js +0 -16
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/cryptoService.d.ts +0 -4
- package/dist/internal/upload/cryptoService.js +0 -6
- package/dist/internal/upload/cryptoService.js.map +1 -1
- package/dist/internal/upload/fileUploader.d.ts +0 -1
- package/dist/internal/upload/fileUploader.js +0 -4
- package/dist/internal/upload/fileUploader.js.map +1 -1
- package/dist/internal/upload/manager.d.ts +0 -1
- package/dist/internal/upload/manager.js +0 -51
- package/dist/internal/upload/manager.js.map +1 -1
- package/dist/internal/upload/manager.test.js +0 -61
- package/dist/internal/upload/manager.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +17 -2
- package/dist/protonDriveClient.js +19 -1
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +119 -4
- package/dist/protonDrivePhotosClient.js +183 -10
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +33 -1
- package/dist/protonDrivePublicLinkClient.js +51 -2
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/package.json +1 -1
- package/src/diagnostic/diagnostic.ts +27 -8
- package/src/diagnostic/index.ts +17 -2
- package/src/diagnostic/interface.ts +35 -9
- package/src/diagnostic/nodeUtils.ts +100 -0
- package/src/diagnostic/{sdkDiagnostic.ts → sdkDiagnosticBase.ts} +204 -204
- package/src/diagnostic/sdkDiagnosticMain.ts +95 -0
- package/src/diagnostic/sdkDiagnosticPhotos.ts +70 -0
- package/src/interface/index.ts +1 -1
- package/src/interface/upload.ts +1 -13
- package/src/internal/devices/interface.ts +1 -1
- package/src/internal/devices/manager.test.ts +3 -3
- package/src/internal/devices/manager.ts +1 -1
- package/src/internal/errors.test.ts +62 -1
- package/src/internal/errors.ts +27 -0
- package/src/internal/nodes/apiService.test.ts +1 -1
- package/src/internal/nodes/apiService.ts +42 -0
- package/src/internal/nodes/cryptoReporter.ts +6 -5
- package/src/internal/nodes/cryptoService.ts +9 -0
- package/src/internal/nodes/index.test.ts +2 -1
- package/src/internal/nodes/index.ts +2 -1
- package/src/internal/nodes/interface.ts +1 -1
- package/src/internal/nodes/nodeName.test.ts +57 -0
- package/src/internal/nodes/nodeName.ts +26 -0
- package/src/internal/nodes/nodesAccess.test.ts +2 -2
- package/src/internal/nodes/nodesAccess.ts +5 -5
- package/src/internal/nodes/nodesManagement.test.ts +65 -0
- package/src/internal/nodes/nodesManagement.ts +43 -1
- package/src/internal/photos/albums.ts +1 -1
- package/src/internal/photos/apiService.ts +40 -0
- package/src/internal/photos/index.ts +13 -1
- package/src/internal/photos/interface.ts +4 -1
- package/src/internal/photos/shares.ts +3 -3
- package/src/internal/photos/timeline.test.ts +116 -0
- package/src/internal/photos/timeline.ts +47 -2
- package/src/internal/shares/cryptoService.ts +5 -1
- package/src/internal/shares/index.ts +1 -0
- package/src/internal/shares/interface.ts +9 -0
- package/src/internal/shares/manager.test.ts +7 -7
- package/src/internal/shares/manager.ts +4 -4
- package/src/internal/sharing/apiService.ts +15 -12
- package/src/internal/sharing/index.ts +7 -1
- package/src/internal/sharing/interface.ts +1 -1
- package/src/internal/sharing/sharingAccess.test.ts +1 -1
- package/src/internal/sharing/sharingAccess.ts +1 -1
- package/src/internal/sharing/sharingManagement.test.ts +59 -1
- package/src/internal/sharing/sharingManagement.ts +33 -14
- package/src/internal/sharingPublic/cryptoReporter.ts +5 -1
- package/src/internal/sharingPublic/index.ts +5 -1
- package/src/internal/sharingPublic/shares.ts +1 -2
- package/src/internal/upload/apiService.ts +0 -39
- package/src/internal/upload/cryptoService.ts +0 -9
- package/src/internal/upload/fileUploader.ts +0 -5
- package/src/internal/upload/manager.test.ts +0 -65
- package/src/internal/upload/manager.ts +0 -64
- package/src/protonDriveClient.ts +21 -2
- package/src/protonDrivePhotosClient.ts +217 -9
- package/src/protonDrivePublicLinkClient.ts +77 -2
- package/dist/diagnostic/sdkDiagnostic.d.ts +0 -23
- package/dist/diagnostic/sdkDiagnostic.js +0 -320
- 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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
package/src/protonDriveClient.ts
CHANGED
|
@@ -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<
|
|
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
|
*
|