@protontech/drive-sdk 0.5.0 → 0.6.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.
- package/dist/diagnostic/{sdkDiagnosticFull.d.ts → diagnostic.d.ts} +5 -4
- package/dist/diagnostic/{sdkDiagnosticFull.js → diagnostic.js} +13 -10
- package/dist/diagnostic/diagnostic.js.map +1 -0
- package/dist/diagnostic/index.js +2 -4
- package/dist/diagnostic/index.js.map +1 -1
- package/dist/diagnostic/interface.d.ts +22 -1
- package/dist/diagnostic/sdkDiagnostic.d.ts +3 -2
- package/dist/diagnostic/sdkDiagnostic.js +79 -7
- package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
- package/dist/interface/index.d.ts +2 -2
- package/dist/interface/index.js.map +1 -1
- package/dist/interface/nodes.d.ts +9 -0
- package/dist/interface/telemetry.d.ts +4 -1
- package/dist/interface/telemetry.js.map +1 -1
- package/dist/interface/upload.d.ts +1 -12
- package/dist/internal/apiService/driveTypes.d.ts +2571 -2356
- package/dist/internal/download/controller.d.ts +2 -0
- package/dist/internal/download/controller.js +15 -1
- package/dist/internal/download/controller.js.map +1 -1
- package/dist/internal/download/fileDownloader.js +6 -1
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/nodes/apiService.d.ts +11 -1
- package/dist/internal/nodes/apiService.js +47 -13
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +61 -3
- package/dist/internal/nodes/apiService.test.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/debouncer.d.ts +3 -2
- package/dist/internal/nodes/debouncer.js +16 -4
- package/dist/internal/nodes/debouncer.js.map +1 -1
- package/dist/internal/nodes/debouncer.test.js +20 -12
- package/dist/internal/nodes/debouncer.test.js.map +1 -1
- package/dist/internal/nodes/extendedAttributes.js +2 -2
- package/dist/internal/nodes/extendedAttributes.js.map +1 -1
- package/dist/internal/nodes/index.d.ts +1 -1
- package/dist/internal/nodes/index.js +3 -3
- package/dist/internal/nodes/index.js.map +1 -1
- package/dist/internal/nodes/index.test.js +1 -1
- package/dist/internal/nodes/index.test.js.map +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 +5 -4
- package/dist/internal/nodes/nodesAccess.js +6 -5
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +17 -5
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.d.ts +3 -2
- package/dist/internal/nodes/nodesManagement.js +35 -4
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +64 -1
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/photos/apiService.js +9 -20
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/index.d.ts +2 -0
- package/dist/internal/photos/index.js +4 -0
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/upload.js +7 -1
- package/dist/internal/photos/upload.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/sharing/apiService.d.ts +4 -2
- package/dist/internal/sharing/apiService.js +18 -14
- 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/sharingManagement.d.ts +4 -1
- package/dist/internal/sharing/sharingManagement.js +7 -4
- package/dist/internal/sharing/sharingManagement.js.map +1 -1
- package/dist/internal/sharingPublic/apiService.d.ts +8 -10
- package/dist/internal/sharingPublic/apiService.js +9 -63
- package/dist/internal/sharingPublic/apiService.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +3 -3
- package/dist/internal/sharingPublic/index.js +7 -12
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/nodes.d.ts +13 -8
- package/dist/internal/sharingPublic/nodes.js +20 -2
- package/dist/internal/sharingPublic/nodes.js.map +1 -1
- package/dist/internal/sharingPublic/session/apiService.d.ts +7 -5
- package/dist/internal/sharingPublic/session/apiService.js +25 -4
- package/dist/internal/sharingPublic/session/apiService.js.map +1 -1
- package/dist/internal/sharingPublic/session/interface.d.ts +17 -0
- package/dist/internal/sharingPublic/session/manager.d.ts +12 -4
- package/dist/internal/sharingPublic/session/manager.js +14 -4
- package/dist/internal/sharingPublic/session/manager.js.map +1 -1
- package/dist/internal/sharingPublic/session/session.d.ts +5 -2
- package/dist/internal/sharingPublic/session/session.js +7 -3
- package/dist/internal/sharingPublic/session/session.js.map +1 -1
- package/dist/internal/sharingPublic/shares.d.ts +3 -10
- package/dist/internal/sharingPublic/shares.js +10 -33
- 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/controller.d.ts +3 -1
- package/dist/internal/upload/controller.js +16 -2
- package/dist/internal/upload/controller.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 +2 -6
- 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/internal/upload/streamUploader.d.ts +4 -3
- package/dist/internal/upload/streamUploader.js +61 -18
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +38 -12
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +24 -4
- package/dist/protonDriveClient.js +26 -7
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +100 -4
- package/dist/protonDrivePhotosClient.js +160 -9
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +4 -3
- package/dist/protonDrivePublicLinkClient.js +2 -2
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/dist/tests/telemetry.d.ts +4 -2
- package/dist/tests/telemetry.js +3 -1
- package/dist/tests/telemetry.js.map +1 -1
- package/dist/transformers.d.ts +3 -2
- package/dist/transformers.js +6 -0
- package/dist/transformers.js.map +1 -1
- package/package.json +1 -1
- package/src/diagnostic/{sdkDiagnosticFull.ts → diagnostic.ts} +10 -6
- package/src/diagnostic/index.ts +3 -5
- package/src/diagnostic/interface.ts +39 -0
- package/src/diagnostic/sdkDiagnostic.ts +110 -9
- package/src/interface/index.ts +2 -1
- package/src/interface/nodes.ts +3 -0
- package/src/interface/telemetry.ts +5 -0
- package/src/interface/upload.ts +1 -13
- package/src/internal/apiService/driveTypes.ts +2698 -2529
- package/src/internal/download/controller.ts +13 -1
- package/src/internal/download/fileDownloader.ts +8 -1
- package/src/internal/nodes/apiService.test.ts +65 -1
- package/src/internal/nodes/apiService.ts +80 -17
- package/src/internal/nodes/cryptoService.ts +9 -0
- package/src/internal/nodes/debouncer.test.ts +25 -13
- package/src/internal/nodes/debouncer.ts +20 -4
- package/src/internal/nodes/extendedAttributes.ts +2 -2
- package/src/internal/nodes/index.test.ts +1 -0
- package/src/internal/nodes/index.ts +3 -9
- package/src/internal/nodes/nodeName.test.ts +57 -0
- package/src/internal/nodes/nodeName.ts +26 -0
- package/src/internal/nodes/nodesAccess.test.ts +17 -5
- package/src/internal/nodes/nodesAccess.ts +15 -5
- package/src/internal/nodes/nodesManagement.test.ts +68 -1
- package/src/internal/nodes/nodesManagement.ts +54 -6
- package/src/internal/photos/apiService.ts +12 -29
- package/src/internal/photos/index.ts +4 -0
- package/src/internal/photos/upload.ts +19 -1
- package/src/internal/shares/index.ts +1 -0
- package/src/internal/shares/interface.ts +9 -0
- package/src/internal/sharing/apiService.ts +17 -14
- package/src/internal/sharing/index.ts +7 -1
- package/src/internal/sharing/sharingManagement.ts +7 -4
- package/src/internal/sharingPublic/apiService.ts +23 -77
- package/src/internal/sharingPublic/index.ts +13 -11
- package/src/internal/sharingPublic/nodes.ts +33 -11
- package/src/internal/sharingPublic/session/apiService.ts +31 -9
- package/src/internal/sharingPublic/session/interface.ts +20 -0
- package/src/internal/sharingPublic/session/manager.ts +31 -8
- package/src/internal/sharingPublic/session/session.ts +10 -5
- package/src/internal/sharingPublic/shares.ts +7 -43
- package/src/internal/upload/apiService.ts +0 -39
- package/src/internal/upload/controller.ts +16 -4
- package/src/internal/upload/cryptoService.ts +0 -9
- package/src/internal/upload/fileUploader.ts +2 -7
- package/src/internal/upload/manager.test.ts +0 -65
- package/src/internal/upload/manager.ts +0 -64
- package/src/internal/upload/streamUploader.test.ts +46 -14
- package/src/internal/upload/streamUploader.ts +74 -21
- package/src/protonDriveClient.ts +46 -9
- package/src/protonDrivePhotosClient.ts +193 -8
- package/src/protonDrivePublicLinkClient.ts +7 -4
- package/src/tests/telemetry.ts +6 -3
- package/src/transformers.ts +8 -0
- package/dist/diagnostic/sdkDiagnosticFull.js.map +0 -1
- package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -15
- package/dist/internal/sharingPublic/cryptoCache.js +0 -44
- package/dist/internal/sharingPublic/cryptoCache.js.map +0 -1
- package/dist/internal/sharingPublic/cryptoService.d.ts +0 -8
- package/dist/internal/sharingPublic/cryptoService.js +0 -19
- package/dist/internal/sharingPublic/cryptoService.js.map +0 -1
- package/dist/internal/sharingPublic/interface.d.ts +0 -5
- package/dist/internal/sharingPublic/interface.js +0 -3
- package/dist/internal/sharingPublic/interface.js.map +0 -1
- package/src/internal/sharingPublic/cryptoCache.ts +0 -45
- package/src/internal/sharingPublic/cryptoService.ts +0 -22
- package/src/internal/sharingPublic/interface.ts +0 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DriveCrypto } from '../../crypto';
|
|
1
|
+
import { DriveCrypto, PrivateKey } from '../../crypto';
|
|
2
2
|
import {
|
|
3
3
|
ProtonDriveCryptoCache,
|
|
4
4
|
ProtonDriveTelemetry,
|
|
@@ -11,10 +11,7 @@ import { NodesCache } from '../nodes/cache';
|
|
|
11
11
|
import { NodesCryptoCache } from '../nodes/cryptoCache';
|
|
12
12
|
import { NodesCryptoService } from '../nodes/cryptoService';
|
|
13
13
|
import { NodesRevisons } from '../nodes/nodesRevisions';
|
|
14
|
-
import { SharingPublicAPIService } from './apiService';
|
|
15
|
-
import { SharingPublicCryptoCache } from './cryptoCache';
|
|
16
14
|
import { SharingPublicCryptoReporter } from './cryptoReporter';
|
|
17
|
-
import { SharingPublicCryptoService } from './cryptoService';
|
|
18
15
|
import { SharingPublicNodesAccess } from './nodes';
|
|
19
16
|
import { SharingPublicSharesManager } from './shares';
|
|
20
17
|
|
|
@@ -38,12 +35,10 @@ export function initSharingPublicModule(
|
|
|
38
35
|
account: ProtonDriveAccount,
|
|
39
36
|
url: string,
|
|
40
37
|
token: string,
|
|
41
|
-
|
|
38
|
+
publicShareKey: PrivateKey,
|
|
39
|
+
publicRootNodeUid: string,
|
|
42
40
|
) {
|
|
43
|
-
const
|
|
44
|
-
const cryptoCache = new SharingPublicCryptoCache(telemetry.getLogger('sharingPublic-crypto'), driveCryptoCache);
|
|
45
|
-
const cryptoService = new SharingPublicCryptoService(driveCrypto, password);
|
|
46
|
-
const shares = new SharingPublicSharesManager(api, cryptoCache, cryptoService, account, token);
|
|
41
|
+
const shares = new SharingPublicSharesManager(account, publicShareKey, publicRootNodeUid);
|
|
47
42
|
const nodes = initSharingPublicNodesModule(
|
|
48
43
|
telemetry,
|
|
49
44
|
apiService,
|
|
@@ -54,6 +49,8 @@ export function initSharingPublicModule(
|
|
|
54
49
|
shares,
|
|
55
50
|
url,
|
|
56
51
|
token,
|
|
52
|
+
publicShareKey,
|
|
53
|
+
publicRootNodeUid,
|
|
57
54
|
);
|
|
58
55
|
|
|
59
56
|
return {
|
|
@@ -78,14 +75,17 @@ export function initSharingPublicNodesModule(
|
|
|
78
75
|
sharesService: SharingPublicSharesManager,
|
|
79
76
|
url: string,
|
|
80
77
|
token: string,
|
|
78
|
+
publicShareKey: PrivateKey,
|
|
79
|
+
publicRootNodeUid: string,
|
|
81
80
|
) {
|
|
82
|
-
const
|
|
81
|
+
const clientUid = undefined; // No client UID for public context yet.
|
|
82
|
+
const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService, clientUid);
|
|
83
83
|
const cache = new NodesCache(telemetry.getLogger('nodes-cache'), driveEntitiesCache);
|
|
84
84
|
const cryptoCache = new NodesCryptoCache(telemetry.getLogger('nodes-cache'), driveCryptoCache);
|
|
85
85
|
const cryptoReporter = new SharingPublicCryptoReporter(telemetry);
|
|
86
86
|
const cryptoService = new NodesCryptoService(telemetry, driveCrypto, account, cryptoReporter);
|
|
87
87
|
const nodesAccess = new SharingPublicNodesAccess(
|
|
88
|
-
telemetry
|
|
88
|
+
telemetry,
|
|
89
89
|
api,
|
|
90
90
|
cache,
|
|
91
91
|
cryptoCache,
|
|
@@ -93,6 +93,8 @@ export function initSharingPublicNodesModule(
|
|
|
93
93
|
sharesService,
|
|
94
94
|
url,
|
|
95
95
|
token,
|
|
96
|
+
publicShareKey,
|
|
97
|
+
publicRootNodeUid,
|
|
96
98
|
);
|
|
97
99
|
const nodesRevisions = new NodesRevisons(telemetry.getLogger('nodes'), api, cryptoService, nodesAccess);
|
|
98
100
|
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NodeAPIService } from
|
|
3
|
-
import { NodesCache } from
|
|
4
|
-
import { NodesCryptoCache } from
|
|
5
|
-
import { NodesCryptoService } from
|
|
6
|
-
import { NodesAccess } from
|
|
7
|
-
import { isProtonDocument, isProtonSheet } from
|
|
8
|
-
import { splitNodeUid } from
|
|
9
|
-
import { SharingPublicSharesManager } from
|
|
1
|
+
import { ProtonDriveTelemetry } from '../../interface';
|
|
2
|
+
import { NodeAPIService } from '../nodes/apiService';
|
|
3
|
+
import { NodesCache } from '../nodes/cache';
|
|
4
|
+
import { NodesCryptoCache } from '../nodes/cryptoCache';
|
|
5
|
+
import { NodesCryptoService } from '../nodes/cryptoService';
|
|
6
|
+
import { NodesAccess } from '../nodes/nodesAccess';
|
|
7
|
+
import { isProtonDocument, isProtonSheet } from '../nodes/mediaTypes';
|
|
8
|
+
import { splitNodeUid } from '../uids';
|
|
9
|
+
import { SharingPublicSharesManager } from './shares';
|
|
10
|
+
import { DecryptedNode, DecryptedNodeKeys } from '../nodes/interface';
|
|
11
|
+
import { PrivateKey } from '../../crypto';
|
|
10
12
|
|
|
11
13
|
export class SharingPublicNodesAccess extends NodesAccess {
|
|
12
14
|
constructor(
|
|
13
|
-
|
|
15
|
+
telemetry: ProtonDriveTelemetry,
|
|
14
16
|
apiService: NodeAPIService,
|
|
15
17
|
cache: NodesCache,
|
|
16
18
|
cryptoCache: NodesCryptoCache,
|
|
@@ -18,9 +20,29 @@ export class SharingPublicNodesAccess extends NodesAccess {
|
|
|
18
20
|
sharesService: SharingPublicSharesManager,
|
|
19
21
|
private url: string,
|
|
20
22
|
private token: string,
|
|
23
|
+
private publicShareKey: PrivateKey,
|
|
24
|
+
private publicRootNodeUid: string,
|
|
21
25
|
) {
|
|
22
|
-
super(
|
|
26
|
+
super(telemetry, apiService, cache, cryptoCache, cryptoService, sharesService);
|
|
23
27
|
this.token = token;
|
|
28
|
+
this.publicShareKey = publicShareKey;
|
|
29
|
+
this.publicRootNodeUid = publicRootNodeUid;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getParentKeys(
|
|
33
|
+
node: Pick<DecryptedNode, 'uid' | 'parentUid' | 'shareId'>,
|
|
34
|
+
): Promise<Pick<DecryptedNodeKeys, 'key' | 'hashKey'>> {
|
|
35
|
+
// If we reached the root node of the public link, return the public
|
|
36
|
+
// share key even if user has access to the parent node. We do not
|
|
37
|
+
// support access to nodes outside of the public link context.
|
|
38
|
+
// For other nodes, the client must use the main SDK.
|
|
39
|
+
if (node.uid === this.publicRootNodeUid) {
|
|
40
|
+
return {
|
|
41
|
+
key: this.publicShareKey,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return super.getParentKeys(node);
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
async getNodeUrl(nodeUid: string): Promise<string> {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Logger } from '../../../interface';
|
|
2
|
+
import { DriveAPIService, drivePaths, permissionsToMemberRole } from '../../apiService';
|
|
3
|
+
import { makeNodeUid } from '../../uids';
|
|
4
|
+
import { PublicLinkInfo, PublicLinkSrpAuth, PublicLinkSession, EncryptedShareCrypto } from './interface';
|
|
3
5
|
|
|
4
6
|
type GetPublicLinkInfoResponse =
|
|
5
7
|
drivePaths['/drive/urls/{token}/info']['get']['responses']['200']['content']['application/json'];
|
|
@@ -18,7 +20,11 @@ type PostPublicLinkAuthResponse =
|
|
|
18
20
|
* and vice versa. It should not contain any business logic.
|
|
19
21
|
*/
|
|
20
22
|
export class SharingPublicSessionAPIService {
|
|
21
|
-
constructor(
|
|
23
|
+
constructor(
|
|
24
|
+
private logger: Logger,
|
|
25
|
+
private apiService: DriveAPIService,
|
|
26
|
+
) {
|
|
27
|
+
this.logger = logger;
|
|
22
28
|
this.apiService = apiService;
|
|
23
29
|
}
|
|
24
30
|
|
|
@@ -38,6 +44,13 @@ export class SharingPublicSessionAPIService {
|
|
|
38
44
|
isCustomPasswordProtected: (response.Flags & 1) === 1,
|
|
39
45
|
isLegacy: response.Flags === 0 || response.Flags === 1,
|
|
40
46
|
vendorType: response.VendorType,
|
|
47
|
+
directAccess: response.DirectAccess
|
|
48
|
+
? {
|
|
49
|
+
nodeUid: makeNodeUid(response.DirectAccess.VolumeID, response.DirectAccess.LinkID),
|
|
50
|
+
directRole: permissionsToMemberRole(this.logger, response.DirectAccess.DirectPermissions),
|
|
51
|
+
publicRole: permissionsToMemberRole(this.logger, response.DirectAccess.PublicPermissions),
|
|
52
|
+
}
|
|
53
|
+
: undefined,
|
|
41
54
|
};
|
|
42
55
|
}
|
|
43
56
|
|
|
@@ -52,9 +65,9 @@ export class SharingPublicSessionAPIService {
|
|
|
52
65
|
token: string,
|
|
53
66
|
srp: PublicLinkSrpAuth,
|
|
54
67
|
): Promise<{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
session: PublicLinkSession;
|
|
69
|
+
encryptedShare: EncryptedShareCrypto;
|
|
70
|
+
rootUid: string;
|
|
58
71
|
}> {
|
|
59
72
|
const response = await this.apiService.post<PostPublicLinkAuthRequest, PostPublicLinkAuthResponse>(
|
|
60
73
|
`drive/urls/${token}/auth`,
|
|
@@ -66,9 +79,18 @@ export class SharingPublicSessionAPIService {
|
|
|
66
79
|
);
|
|
67
80
|
|
|
68
81
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
session: {
|
|
83
|
+
serverProof: response.ServerProof,
|
|
84
|
+
sessionUid: response.UID,
|
|
85
|
+
sessionAccessToken: response.AccessToken,
|
|
86
|
+
},
|
|
87
|
+
encryptedShare: {
|
|
88
|
+
base64UrlPasswordSalt: response.Share.SharePasswordSalt,
|
|
89
|
+
armoredKey: response.Share.ShareKey,
|
|
90
|
+
armoredPassphrase: response.Share.SharePassphrase,
|
|
91
|
+
publicRole: permissionsToMemberRole(this.logger, response.Share.PublicPermissions),
|
|
92
|
+
},
|
|
93
|
+
rootUid: makeNodeUid(response.Share.VolumeID, response.Share.LinkID),
|
|
72
94
|
};
|
|
73
95
|
}
|
|
74
96
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { MemberRole } from '../../../interface';
|
|
2
|
+
|
|
1
3
|
export type PublicLinkInfo = {
|
|
2
4
|
srp: PublicLinkSrpInfo;
|
|
3
5
|
isCustomPasswordProtected: boolean;
|
|
4
6
|
isLegacy: boolean;
|
|
5
7
|
vendorType: number;
|
|
8
|
+
directAccess?: {
|
|
9
|
+
nodeUid: string;
|
|
10
|
+
directRole: MemberRole;
|
|
11
|
+
publicRole: MemberRole;
|
|
12
|
+
};
|
|
6
13
|
};
|
|
7
14
|
|
|
8
15
|
export type PublicLinkSrpInfo = {
|
|
@@ -18,3 +25,16 @@ export type PublicLinkSrpAuth = {
|
|
|
18
25
|
clientEphemeral: string;
|
|
19
26
|
srpSession: string;
|
|
20
27
|
};
|
|
28
|
+
|
|
29
|
+
export type PublicLinkSession = {
|
|
30
|
+
serverProof: string;
|
|
31
|
+
sessionUid: string;
|
|
32
|
+
sessionAccessToken?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type EncryptedShareCrypto = {
|
|
36
|
+
base64UrlPasswordSalt: string;
|
|
37
|
+
armoredKey: string;
|
|
38
|
+
armoredPassphrase: string;
|
|
39
|
+
publicRole: MemberRole;
|
|
40
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ProtonDriveHTTPClient } from '../../../interface';
|
|
2
|
-
import { SRPModule } from '../../../crypto';
|
|
1
|
+
import { MemberRole, ProtonDriveHTTPClient, ProtonDriveTelemetry } from '../../../interface';
|
|
2
|
+
import { DriveCrypto, PrivateKey, SRPModule } from '../../../crypto';
|
|
3
3
|
import { DriveAPIService } from '../../apiService';
|
|
4
4
|
import { SharingPublicSessionAPIService } from './apiService';
|
|
5
5
|
import { SharingPublicSessionHttpClient } from './httpClient';
|
|
6
|
-
import { PublicLinkInfo } from './interface';
|
|
6
|
+
import { EncryptedShareCrypto, PublicLinkInfo } from './interface';
|
|
7
7
|
import { SharingPublicLinkSession } from './session';
|
|
8
8
|
import { getTokenAndPasswordFromUrl } from './url';
|
|
9
9
|
|
|
@@ -18,14 +18,17 @@ export class SharingPublicSessionManager {
|
|
|
18
18
|
private infosPerToken: Map<string, PublicLinkInfo> = new Map();
|
|
19
19
|
|
|
20
20
|
constructor(
|
|
21
|
+
telemetry: ProtonDriveTelemetry,
|
|
21
22
|
private httpClient: ProtonDriveHTTPClient,
|
|
22
|
-
|
|
23
|
+
private driveCrypto: DriveCrypto,
|
|
23
24
|
private srpModule: SRPModule,
|
|
25
|
+
apiService: DriveAPIService,
|
|
24
26
|
) {
|
|
25
27
|
this.httpClient = httpClient;
|
|
28
|
+
this.driveCrypto = driveCrypto;
|
|
26
29
|
this.srpModule = srpModule;
|
|
27
30
|
|
|
28
|
-
this.api = new SharingPublicSessionAPIService(apiService);
|
|
31
|
+
this.api = new SharingPublicSessionAPIService(telemetry.getLogger('sharingPublicSession'), apiService);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -42,6 +45,11 @@ export class SharingPublicSessionManager {
|
|
|
42
45
|
isCustomPasswordProtected: boolean;
|
|
43
46
|
isLegacy: boolean;
|
|
44
47
|
vendorType: number;
|
|
48
|
+
directAccess?: {
|
|
49
|
+
nodeUid: string;
|
|
50
|
+
directRole: MemberRole;
|
|
51
|
+
publicRole: MemberRole;
|
|
52
|
+
};
|
|
45
53
|
}> {
|
|
46
54
|
const { token } = getTokenAndPasswordFromUrl(url);
|
|
47
55
|
|
|
@@ -52,6 +60,7 @@ export class SharingPublicSessionManager {
|
|
|
52
60
|
isCustomPasswordProtected: info.isCustomPasswordProtected,
|
|
53
61
|
isLegacy: info.isLegacy,
|
|
54
62
|
vendorType: info.vendorType,
|
|
63
|
+
directAccess: info.directAccess,
|
|
55
64
|
};
|
|
56
65
|
}
|
|
57
66
|
|
|
@@ -73,8 +82,9 @@ export class SharingPublicSessionManager {
|
|
|
73
82
|
customPassword?: string,
|
|
74
83
|
): Promise<{
|
|
75
84
|
token: string;
|
|
76
|
-
password: string;
|
|
77
85
|
httpClient: SharingPublicSessionHttpClient;
|
|
86
|
+
shareKey: PrivateKey;
|
|
87
|
+
rootUid: string;
|
|
78
88
|
}> {
|
|
79
89
|
const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url);
|
|
80
90
|
|
|
@@ -86,12 +96,25 @@ export class SharingPublicSessionManager {
|
|
|
86
96
|
const password = `${urlPassword}${customPassword || ''}`;
|
|
87
97
|
|
|
88
98
|
const session = new SharingPublicLinkSession(this.api, this.srpModule, token, password);
|
|
89
|
-
await session.auth(info.srp);
|
|
99
|
+
const { encryptedShare, rootUid } = await session.auth(info.srp);
|
|
100
|
+
|
|
101
|
+
const shareKey = await this.decryptShareKey(encryptedShare, password);
|
|
90
102
|
|
|
91
103
|
return {
|
|
92
104
|
token,
|
|
93
|
-
password,
|
|
94
105
|
httpClient: new SharingPublicSessionHttpClient(this.httpClient, session),
|
|
106
|
+
shareKey,
|
|
107
|
+
rootUid,
|
|
95
108
|
};
|
|
96
109
|
}
|
|
110
|
+
|
|
111
|
+
private async decryptShareKey(encryptedShare: EncryptedShareCrypto, password: string): Promise<PrivateKey> {
|
|
112
|
+
const { key: shareKey } = await this.driveCrypto.decryptKeyWithSrpPassword(
|
|
113
|
+
password,
|
|
114
|
+
encryptedShare.base64UrlPasswordSalt,
|
|
115
|
+
encryptedShare.armoredKey,
|
|
116
|
+
encryptedShare.armoredPassphrase,
|
|
117
|
+
);
|
|
118
|
+
return shareKey;
|
|
119
|
+
}
|
|
97
120
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SRPModule } from '../../../crypto';
|
|
2
2
|
import { SharingPublicSessionAPIService } from './apiService';
|
|
3
|
-
import { PublicLinkInfo, PublicLinkSrpInfo } from './interface';
|
|
3
|
+
import { EncryptedShareCrypto, PublicLinkInfo, PublicLinkSrpInfo } from './interface';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Session for a public link.
|
|
@@ -33,7 +33,7 @@ export class SharingPublicLinkSession {
|
|
|
33
33
|
return this.apiService.initPublicLinkSession(this.token);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
async auth(srp: PublicLinkSrpInfo): Promise<
|
|
36
|
+
async auth(srp: PublicLinkSrpInfo): Promise<{ encryptedShare: EncryptedShareCrypto; rootUid: string }> {
|
|
37
37
|
const { expectedServerProof, clientProof, clientEphemeral } = await this.srpModule.getSrp(
|
|
38
38
|
srp.version,
|
|
39
39
|
srp.modulus,
|
|
@@ -48,12 +48,17 @@ export class SharingPublicLinkSession {
|
|
|
48
48
|
srpSession: srp.srpSession,
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
if (auth.serverProof !== expectedServerProof) {
|
|
51
|
+
if (auth.session.serverProof !== expectedServerProof) {
|
|
52
52
|
throw new Error('Invalid server proof');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
this.sessionUid = auth.sessionUid;
|
|
56
|
-
this.sessionAccessToken = auth.sessionAccessToken;
|
|
55
|
+
this.sessionUid = auth.session.sessionUid;
|
|
56
|
+
this.sessionAccessToken = auth.session.sessionAccessToken;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
encryptedShare: auth.encryptedShare,
|
|
60
|
+
rootUid: auth.rootUid,
|
|
61
|
+
};
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
/**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { PrivateKey } from '../../crypto';
|
|
2
2
|
import { MetricVolumeType, ProtonDriveAccount } from '../../interface';
|
|
3
3
|
import { splitNodeUid } from '../uids';
|
|
4
|
-
import { SharingPublicAPIService } from './apiService';
|
|
5
|
-
import { SharingPublicCryptoCache } from './cryptoCache';
|
|
6
|
-
import { SharingPublicCryptoService } from './cryptoService';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* Provides high-level actions for managing public link share.
|
|
@@ -12,57 +9,24 @@ import { SharingPublicCryptoService } from './cryptoService';
|
|
|
12
9
|
* service so it can be used in the same way in various modules that use shares.
|
|
13
10
|
*/
|
|
14
11
|
export class SharingPublicSharesManager {
|
|
15
|
-
private promisePublicLinkRoot?: Promise<{
|
|
16
|
-
rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
|
|
17
|
-
shareKey: PrivateKey;
|
|
18
|
-
}>;
|
|
19
|
-
|
|
20
12
|
constructor(
|
|
21
|
-
private apiService: SharingPublicAPIService,
|
|
22
|
-
private cryptoCache: SharingPublicCryptoCache,
|
|
23
|
-
private cryptoService: SharingPublicCryptoService,
|
|
24
13
|
private account: ProtonDriveAccount,
|
|
25
|
-
private
|
|
14
|
+
private publicShareKey: PrivateKey,
|
|
15
|
+
private publicRootNodeUid: string,
|
|
26
16
|
) {
|
|
27
|
-
this.apiService = apiService;
|
|
28
|
-
this.cryptoCache = cryptoCache;
|
|
29
|
-
this.cryptoService = cryptoService;
|
|
30
17
|
this.account = account;
|
|
31
|
-
this.
|
|
18
|
+
this.publicShareKey = publicShareKey;
|
|
19
|
+
this.publicRootNodeUid = publicRootNodeUid;
|
|
32
20
|
}
|
|
33
21
|
|
|
34
22
|
// TODO: Rename to getRootIDs everywhere.
|
|
35
23
|
async getOwnVolumeIDs(): Promise<{ volumeId: string; rootNodeId: string; rootNodeUid: string }> {
|
|
36
|
-
const {
|
|
37
|
-
return
|
|
24
|
+
const { volumeId, nodeId: rootNodeId } = splitNodeUid(this.publicRootNodeUid);
|
|
25
|
+
return { volumeId, rootNodeId, rootNodeUid: this.publicRootNodeUid };
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
async getSharePrivateKey(): Promise<PrivateKey> {
|
|
41
|
-
|
|
42
|
-
return shareKey;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private async getPublicLinkRoot(): Promise<{
|
|
46
|
-
rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
|
|
47
|
-
shareKey: PrivateKey;
|
|
48
|
-
}> {
|
|
49
|
-
if (!this.promisePublicLinkRoot) {
|
|
50
|
-
this.promisePublicLinkRoot = (async () => {
|
|
51
|
-
const { encryptedNode, encryptedShare } = await this.apiService.getPublicLinkRoot(this.token);
|
|
52
|
-
|
|
53
|
-
const { volumeId, nodeId: rootNodeId } = splitNodeUid(encryptedNode.uid);
|
|
54
|
-
|
|
55
|
-
const shareKey = await this.cryptoService.decryptPublicLinkShareKey(encryptedShare);
|
|
56
|
-
await this.cryptoCache.setShareKey(shareKey);
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
rootIds: { volumeId, rootNodeId, rootNodeUid: encryptedNode.uid },
|
|
60
|
-
shareKey,
|
|
61
|
-
};
|
|
62
|
-
})();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return this.promisePublicLinkRoot;
|
|
29
|
+
return this.publicShareKey;
|
|
66
30
|
}
|
|
67
31
|
|
|
68
32
|
async getContextShareMemberEmailKey(): Promise<{
|
|
@@ -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: {
|
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
import { AbortError } from '../../errors';
|
|
1
2
|
import { waitForCondition } from '../wait';
|
|
2
3
|
|
|
3
4
|
export class UploadController {
|
|
4
5
|
private paused = false;
|
|
5
|
-
public promise?: Promise<{ nodeRevisionUid: string
|
|
6
|
+
public promise?: Promise<{ nodeRevisionUid: string; nodeUid: string }>;
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
constructor(private signal?: AbortSignal) {
|
|
9
|
+
this.signal = signal;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async waitWhilePaused(): Promise<void> {
|
|
13
|
+
try {
|
|
14
|
+
await waitForCondition(() => !this.paused, this.signal);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof AbortError) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
9
21
|
}
|
|
10
22
|
|
|
11
23
|
pause(): void {
|
|
@@ -16,7 +28,7 @@ export class UploadController {
|
|
|
16
28
|
this.paused = false;
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
async completion(): Promise<{ nodeRevisionUid: string
|
|
31
|
+
async completion(): Promise<{ nodeRevisionUid: string; nodeUid: string }> {
|
|
20
32
|
if (!this.promise) {
|
|
21
33
|
throw new Error('UploadController.completion() called before upload started');
|
|
22
34
|
}
|
|
@@ -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,
|
|
@@ -43,7 +43,7 @@ class Uploader {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
this.controller = new UploadController();
|
|
46
|
+
this.controller = new UploadController(this.abortController.signal);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async uploadFromFile(
|
|
@@ -120,7 +120,7 @@ class Uploader {
|
|
|
120
120
|
this.metadata,
|
|
121
121
|
onFinish,
|
|
122
122
|
this.controller,
|
|
123
|
-
this.
|
|
123
|
+
this.abortController,
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -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',
|