@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,11 +1,23 @@
|
|
|
1
|
+
import { AbortError } from '../../errors';
|
|
1
2
|
import { waitForCondition } from '../wait';
|
|
2
3
|
|
|
3
4
|
export class DownloadController {
|
|
4
5
|
private paused = false;
|
|
5
6
|
public promise?: Promise<void>;
|
|
6
7
|
|
|
8
|
+
constructor(private signal?: AbortSignal) {
|
|
9
|
+
this.signal = signal;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
async waitWhilePaused(): Promise<void> {
|
|
8
|
-
|
|
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 {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { c } from 'ttag';
|
|
2
|
+
|
|
1
3
|
import { PrivateKey, SessionKey, base64StringToUint8Array } from '../../crypto';
|
|
4
|
+
import { AbortError } from '../../errors';
|
|
2
5
|
import { Logger } from '../../interface';
|
|
3
6
|
import { LoggerWithPrefix } from '../../telemetry';
|
|
4
7
|
import { APIHTTPError, HTTPErrorCode } from '../apiService';
|
|
@@ -48,7 +51,7 @@ export class FileDownloader {
|
|
|
48
51
|
this.revision = revision;
|
|
49
52
|
this.signal = signal;
|
|
50
53
|
this.onFinish = onFinish;
|
|
51
|
-
this.controller = new DownloadController();
|
|
54
|
+
this.controller = new DownloadController(this.signal);
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
getClaimedSizeInBytes(): number | undefined {
|
|
@@ -289,6 +292,10 @@ export class FileDownloader {
|
|
|
289
292
|
logger.debug(`Decrypting`);
|
|
290
293
|
decryptedBlock = await this.cryptoService.decryptBlock(encryptedBlock, cryptoKeys);
|
|
291
294
|
} catch (error) {
|
|
295
|
+
if (this.signal?.aborted) {
|
|
296
|
+
throw new AbortError(c('Error').t`Operation aborted`);
|
|
297
|
+
}
|
|
298
|
+
|
|
292
299
|
if (blockProgress !== 0) {
|
|
293
300
|
onProgress?.(-blockProgress);
|
|
294
301
|
blockProgress = 0;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NodeWithSameNameExistsValidationError, ValidationError } from '../../errors';
|
|
1
2
|
import { MemberRole, NodeType } from '../../interface';
|
|
2
3
|
import { getMockLogger } from '../../tests/logger';
|
|
3
4
|
import { DriveAPIService, ErrorCode, InvalidRequirementsAPIError } from '../apiService';
|
|
@@ -172,7 +173,7 @@ describe('nodeAPIService', () => {
|
|
|
172
173
|
put: jest.fn(),
|
|
173
174
|
};
|
|
174
175
|
|
|
175
|
-
api = new NodeAPIService(getMockLogger(), apiMock);
|
|
176
|
+
api = new NodeAPIService(getMockLogger(), apiMock, 'clientUid');
|
|
176
177
|
});
|
|
177
178
|
|
|
178
179
|
describe('getNode', () => {
|
|
@@ -627,6 +628,69 @@ describe('nodeAPIService', () => {
|
|
|
627
628
|
});
|
|
628
629
|
});
|
|
629
630
|
|
|
631
|
+
describe('createFolder', () => {
|
|
632
|
+
it('should create folder', async () => {
|
|
633
|
+
apiMock.post = jest.fn().mockResolvedValue({
|
|
634
|
+
Code: ErrorCode.OK,
|
|
635
|
+
Folder: {
|
|
636
|
+
ID: 'newNodeId',
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const result = await api.createFolder('volumeId~parentNodeId', {
|
|
641
|
+
armoredKey: 'armoredKey',
|
|
642
|
+
armoredHashKey: 'armoredHashKey',
|
|
643
|
+
armoredNodePassphrase: 'armoredNodePassphrase',
|
|
644
|
+
armoredNodePassphraseSignature: 'armoredNodePassphraseSignature',
|
|
645
|
+
signatureEmail: 'signatureEmail',
|
|
646
|
+
encryptedName: 'encryptedName',
|
|
647
|
+
hash: 'hash',
|
|
648
|
+
armoredExtendedAttributes: 'armoredExtendedAttributes',
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
expect(result).toEqual('volumeId~newNodeId');
|
|
652
|
+
expect(apiMock.post).toHaveBeenCalledWith('drive/v2/volumes/volumeId/folders', {
|
|
653
|
+
ParentLinkID: 'parentNodeId',
|
|
654
|
+
NodeKey: 'armoredKey',
|
|
655
|
+
NodeHashKey: 'armoredHashKey',
|
|
656
|
+
NodePassphrase: 'armoredNodePassphrase',
|
|
657
|
+
NodePassphraseSignature: 'armoredNodePassphraseSignature',
|
|
658
|
+
SignatureEmail: 'signatureEmail',
|
|
659
|
+
Name: 'encryptedName',
|
|
660
|
+
Hash: 'hash',
|
|
661
|
+
XAttr: 'armoredExtendedAttributes',
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('should throw NodeWithSameNameExistsValidationError if node already exists', async () => {
|
|
666
|
+
apiMock.post = jest.fn().mockRejectedValue(
|
|
667
|
+
new ValidationError('Node already exists', ErrorCode.ALREADY_EXISTS, {
|
|
668
|
+
ConflictLinkID: 'existingNodeId',
|
|
669
|
+
}),
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
await api.createFolder('volumeId~parentNodeId', {
|
|
674
|
+
armoredKey: 'armoredKey',
|
|
675
|
+
armoredHashKey: 'armoredHashKey',
|
|
676
|
+
armoredNodePassphrase: 'armoredNodePassphrase',
|
|
677
|
+
armoredNodePassphraseSignature: 'armoredNodePassphraseSignature',
|
|
678
|
+
signatureEmail: 'signatureEmail',
|
|
679
|
+
encryptedName: 'encryptedName',
|
|
680
|
+
hash: 'hash',
|
|
681
|
+
armoredExtendedAttributes: 'armoredExtendedAttributes',
|
|
682
|
+
});
|
|
683
|
+
expect(false).toBeTruthy();
|
|
684
|
+
} catch (error: unknown) {
|
|
685
|
+
expect(error).toBeInstanceOf(NodeWithSameNameExistsValidationError);
|
|
686
|
+
if (error instanceof NodeWithSameNameExistsValidationError) {
|
|
687
|
+
expect(error.code).toEqual(ErrorCode.ALREADY_EXISTS);
|
|
688
|
+
expect(error.existingNodeUid).toEqual('volumeId~existingNodeId');
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
|
|
630
694
|
describe('renameNode', () => {
|
|
631
695
|
it('should rename node', async () => {
|
|
632
696
|
await api.renameNode(
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { c } from 'ttag';
|
|
2
2
|
|
|
3
|
-
import { ProtonDriveError, ValidationError } from '../../errors';
|
|
3
|
+
import { NodeWithSameNameExistsValidationError, ProtonDriveError, ValidationError } from '../../errors';
|
|
4
4
|
import { Logger, NodeResult } from '../../interface';
|
|
5
5
|
import { MemberRole, RevisionState } from '../../interface/nodes';
|
|
6
6
|
import {
|
|
7
7
|
DriveAPIService,
|
|
8
8
|
drivePaths,
|
|
9
|
+
ErrorCode,
|
|
9
10
|
InvalidRequirementsAPIError,
|
|
10
11
|
isCodeOk,
|
|
11
12
|
nodeTypeNumberToNodeType,
|
|
@@ -101,6 +102,14 @@ type PostRestoreRevisionResponse =
|
|
|
101
102
|
type DeleteRevisionResponse =
|
|
102
103
|
drivePaths['/drive/v2/volumes/{volumeID}/files/{linkID}/revisions/{revisionID}']['delete']['responses']['200']['content']['application/json'];
|
|
103
104
|
|
|
105
|
+
|
|
106
|
+
type PostCheckAvailableHashesRequest = Extract<
|
|
107
|
+
drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['requestBody'],
|
|
108
|
+
{ content: object }
|
|
109
|
+
>['content']['application/json'];
|
|
110
|
+
type PostCheckAvailableHashesResponse =
|
|
111
|
+
drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['responses']['200']['content']['application/json'];
|
|
112
|
+
|
|
104
113
|
/**
|
|
105
114
|
* Provides API communication for fetching and manipulating nodes metadata.
|
|
106
115
|
*
|
|
@@ -111,9 +120,11 @@ export class NodeAPIService {
|
|
|
111
120
|
constructor(
|
|
112
121
|
private logger: Logger,
|
|
113
122
|
private apiService: DriveAPIService,
|
|
123
|
+
private clientUid: string | undefined,
|
|
114
124
|
) {
|
|
115
125
|
this.logger = logger;
|
|
116
126
|
this.apiService = apiService;
|
|
127
|
+
this.clientUid = clientUid;
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
async getNode(nodeUid: string, ownVolumeId: string, signal?: AbortSignal): Promise<EncryptedNode> {
|
|
@@ -400,7 +411,7 @@ export class NodeAPIService {
|
|
|
400
411
|
);
|
|
401
412
|
|
|
402
413
|
// TODO: remove `as` when backend fixes OpenAPI schema.
|
|
403
|
-
yield
|
|
414
|
+
yield* handleResponseErrors(batchNodeUids, volumeId, response.Responses as LinkResponse[]);
|
|
404
415
|
}
|
|
405
416
|
}
|
|
406
417
|
|
|
@@ -449,21 +460,41 @@ export class NodeAPIService {
|
|
|
449
460
|
): Promise<string> {
|
|
450
461
|
const { volumeId, nodeId: parentId } = splitNodeUid(parentUid);
|
|
451
462
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
463
|
+
let response: PostCreateFolderResponse;
|
|
464
|
+
try {
|
|
465
|
+
response = await this.apiService.post<PostCreateFolderRequest, PostCreateFolderResponse>(
|
|
466
|
+
`drive/v2/volumes/${volumeId}/folders`,
|
|
467
|
+
{
|
|
468
|
+
ParentLinkID: parentId,
|
|
469
|
+
NodeKey: newNode.armoredKey,
|
|
470
|
+
NodeHashKey: newNode.armoredHashKey,
|
|
471
|
+
NodePassphrase: newNode.armoredNodePassphrase,
|
|
472
|
+
NodePassphraseSignature: newNode.armoredNodePassphraseSignature,
|
|
473
|
+
SignatureEmail: newNode.signatureEmail,
|
|
474
|
+
Name: newNode.encryptedName,
|
|
475
|
+
Hash: newNode.hash,
|
|
476
|
+
// @ts-expect-error: XAttr is optional as undefined.
|
|
477
|
+
XAttr: newNode.armoredExtendedAttributes,
|
|
478
|
+
},
|
|
479
|
+
);
|
|
480
|
+
} catch (error: unknown) {
|
|
481
|
+
if (error instanceof ValidationError) {
|
|
482
|
+
if (error.code === ErrorCode.ALREADY_EXISTS) {
|
|
483
|
+
const typedDetails = error.details as
|
|
484
|
+
| {
|
|
485
|
+
ConflictLinkID: string;
|
|
486
|
+
}
|
|
487
|
+
| undefined;
|
|
488
|
+
|
|
489
|
+
const existingNodeUid = typedDetails?.ConflictLinkID
|
|
490
|
+
? makeNodeUid(volumeId, typedDetails.ConflictLinkID)
|
|
491
|
+
: undefined;
|
|
492
|
+
|
|
493
|
+
throw new NodeWithSameNameExistsValidationError(error.message, error.code, existingNodeUid);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
467
498
|
|
|
468
499
|
return makeNodeUid(volumeId, response.Folder.ID);
|
|
469
500
|
}
|
|
@@ -505,6 +536,38 @@ export class NodeAPIService {
|
|
|
505
536
|
`drive/v2/volumes/${volumeId}/files/${nodeId}/revisions/${revisionId}`,
|
|
506
537
|
);
|
|
507
538
|
}
|
|
539
|
+
|
|
540
|
+
async checkAvailableHashes(
|
|
541
|
+
parentNodeUid: string,
|
|
542
|
+
hashes: string[],
|
|
543
|
+
): Promise<{
|
|
544
|
+
availableHashes: string[];
|
|
545
|
+
pendingHashes: {
|
|
546
|
+
hash: string;
|
|
547
|
+
nodeUid: string;
|
|
548
|
+
revisionUid: string;
|
|
549
|
+
clientUid?: string;
|
|
550
|
+
}[];
|
|
551
|
+
}> {
|
|
552
|
+
const { volumeId, nodeId: parentNodeId } = splitNodeUid(parentNodeUid);
|
|
553
|
+
const result = await this.apiService.post<PostCheckAvailableHashesRequest, PostCheckAvailableHashesResponse>(
|
|
554
|
+
`drive/v2/volumes/${volumeId}/links/${parentNodeId}/checkAvailableHashes`,
|
|
555
|
+
{
|
|
556
|
+
Hashes: hashes,
|
|
557
|
+
ClientUID: this.clientUid ? [this.clientUid] : null,
|
|
558
|
+
},
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
availableHashes: result.AvailableHashes,
|
|
563
|
+
pendingHashes: result.PendingHashes.map((hash) => ({
|
|
564
|
+
hash: hash.Hash,
|
|
565
|
+
nodeUid: makeNodeUid(volumeId, hash.LinkID),
|
|
566
|
+
revisionUid: makeNodeRevisionUid(volumeId, hash.LinkID, hash.RevisionID),
|
|
567
|
+
clientUid: hash.ClientUID || undefined,
|
|
568
|
+
})),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
508
571
|
}
|
|
509
572
|
|
|
510
573
|
type LinkResponse = {
|
|
@@ -645,6 +645,15 @@ export class NodesCryptoService {
|
|
|
645
645
|
nameSignatureEmail: email,
|
|
646
646
|
};
|
|
647
647
|
}
|
|
648
|
+
|
|
649
|
+
async generateNameHashes(parentHashKey: Uint8Array, names: string[]): Promise<{ name: string; hash: string }[]> {
|
|
650
|
+
return Promise.all(
|
|
651
|
+
names.map(async (name) => ({
|
|
652
|
+
name,
|
|
653
|
+
hash: await this.driveCrypto.generateLookupHash(name, parentHashKey),
|
|
654
|
+
})),
|
|
655
|
+
);
|
|
656
|
+
}
|
|
648
657
|
}
|
|
649
658
|
|
|
650
659
|
function getClaimedAuthor(
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
+
import { ProtonDriveTelemetry } from '../../interface';
|
|
2
|
+
import { getMockTelemetry } from '../../tests/telemetry';
|
|
1
3
|
import { NodesDebouncer } from './debouncer';
|
|
2
|
-
import { Logger } from '../../interface';
|
|
3
4
|
|
|
4
5
|
describe('NodesDebouncer', () => {
|
|
5
6
|
let debouncer: NodesDebouncer;
|
|
6
|
-
let
|
|
7
|
+
let mockTelemetry: ReturnType<typeof getMockTelemetry>;
|
|
7
8
|
|
|
8
9
|
beforeEach(() => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
info: jest.fn(),
|
|
12
|
-
warn: jest.fn(),
|
|
13
|
-
error: jest.fn(),
|
|
14
|
-
};
|
|
15
|
-
debouncer = new NodesDebouncer(mockLogger);
|
|
10
|
+
mockTelemetry = getMockTelemetry();
|
|
11
|
+
debouncer = new NodesDebouncer(mockTelemetry);
|
|
16
12
|
|
|
17
13
|
jest.useFakeTimers();
|
|
18
14
|
});
|
|
@@ -77,7 +73,22 @@ describe('NodesDebouncer', () => {
|
|
|
77
73
|
debouncer.loadingNode(nodeUid);
|
|
78
74
|
debouncer.loadingNode(nodeUid);
|
|
79
75
|
|
|
80
|
-
expect(mockLogger.warn).toHaveBeenCalledWith(`
|
|
76
|
+
expect(mockTelemetry.mockLogger.warn).toHaveBeenCalledWith(`Loading twice for: ${nodeUid}`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should send metric when waiting for a long time', async () => {
|
|
80
|
+
const nodeUid = 'test-node-1';
|
|
81
|
+
debouncer.loadingNode(nodeUid);
|
|
82
|
+
|
|
83
|
+
const waitPromise = debouncer.waitForLoadingNode(nodeUid);
|
|
84
|
+
expect(mockTelemetry.recordMetric).not.toHaveBeenCalled();
|
|
85
|
+
jest.advanceTimersByTime(1500);
|
|
86
|
+
expect(mockTelemetry.recordMetric).toHaveBeenCalledWith({
|
|
87
|
+
eventName: 'debounceLongWait',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
debouncer.finishedLoadingNode(nodeUid);
|
|
91
|
+
await waitPromise;
|
|
81
92
|
});
|
|
82
93
|
|
|
83
94
|
it('should timeout', async () => {
|
|
@@ -85,7 +96,7 @@ describe('NodesDebouncer', () => {
|
|
|
85
96
|
debouncer.loadingNode(nodeUid);
|
|
86
97
|
|
|
87
98
|
jest.advanceTimersByTime(6000);
|
|
88
|
-
expect(mockLogger.warn).toHaveBeenCalledWith(`
|
|
99
|
+
expect(mockTelemetry.mockLogger.warn).toHaveBeenCalledWith(`Timeout for: ${nodeUid}`);
|
|
89
100
|
await expect(debouncer.waitForLoadingNode(nodeUid)).resolves.toBeUndefined();
|
|
90
101
|
});
|
|
91
102
|
|
|
@@ -112,7 +123,7 @@ describe('NodesDebouncer', () => {
|
|
|
112
123
|
|
|
113
124
|
const result = await debouncer.waitForLoadingNode(nodeUid);
|
|
114
125
|
expect(result).toBeUndefined();
|
|
115
|
-
expect(mockLogger.debug).not.toHaveBeenCalled();
|
|
126
|
+
expect(mockTelemetry.mockLogger.debug).not.toHaveBeenCalled();
|
|
116
127
|
});
|
|
117
128
|
|
|
118
129
|
it('should wait for registered node and log debug message', async () => {
|
|
@@ -121,7 +132,8 @@ describe('NodesDebouncer', () => {
|
|
|
121
132
|
|
|
122
133
|
const waitPromise = debouncer.waitForLoadingNode(nodeUid);
|
|
123
134
|
|
|
124
|
-
expect(mockLogger.debug).toHaveBeenCalledWith(`
|
|
135
|
+
expect(mockTelemetry.mockLogger.debug).toHaveBeenCalledWith(`Wait for: ${nodeUid}`);
|
|
136
|
+
|
|
125
137
|
debouncer.finishedLoadingNode(nodeUid);
|
|
126
138
|
await waitPromise;
|
|
127
139
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Logger } from
|
|
2
|
-
import { LoggerWithPrefix } from '../../telemetry';
|
|
1
|
+
import { Logger, ProtonDriveTelemetry } from '../../interface';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* The timeout for which the node is considered to be loading.
|
|
@@ -11,6 +10,12 @@ import { LoggerWithPrefix } from '../../telemetry';
|
|
|
11
10
|
*/
|
|
12
11
|
const DEBOUNCE_TIMEOUT = 5000;
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* The timeout for which the node is considered to be waiting for a long time.
|
|
15
|
+
* After this timeout the metric is sent.
|
|
16
|
+
*/
|
|
17
|
+
const DEBOUNCE_LONG_WAIT_TIMEOUT = 1000;
|
|
18
|
+
|
|
14
19
|
/**
|
|
15
20
|
* Helper to avoid loading the same node twice.
|
|
16
21
|
*
|
|
@@ -23,6 +28,8 @@ const DEBOUNCE_TIMEOUT = 5000;
|
|
|
23
28
|
* the node to be loaded if that is the case.
|
|
24
29
|
*/
|
|
25
30
|
export class NodesDebouncer {
|
|
31
|
+
private logger: Logger;
|
|
32
|
+
|
|
26
33
|
private promises: Map<
|
|
27
34
|
string,
|
|
28
35
|
{
|
|
@@ -32,8 +39,9 @@ export class NodesDebouncer {
|
|
|
32
39
|
}
|
|
33
40
|
> = new Map();
|
|
34
41
|
|
|
35
|
-
constructor(private
|
|
36
|
-
this.logger =
|
|
42
|
+
constructor(private telemetry: ProtonDriveTelemetry) {
|
|
43
|
+
this.logger = telemetry.getLogger('nodes-debouncer');
|
|
44
|
+
this.telemetry = telemetry;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
loadingNodes(nodeUids: string[]) {
|
|
@@ -79,8 +87,16 @@ export class NodesDebouncer {
|
|
|
79
87
|
return;
|
|
80
88
|
}
|
|
81
89
|
|
|
90
|
+
const metricTimeout = setTimeout(() => {
|
|
91
|
+
this.telemetry.recordMetric({
|
|
92
|
+
eventName: 'debounceLongWait',
|
|
93
|
+
});
|
|
94
|
+
}, DEBOUNCE_LONG_WAIT_TIMEOUT);
|
|
95
|
+
|
|
82
96
|
this.logger.debug(`Wait for: ${nodeUid}`);
|
|
83
97
|
await result.promise;
|
|
98
|
+
|
|
99
|
+
clearTimeout(metricTimeout);
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
clear() {
|
|
@@ -208,11 +208,11 @@ function parseBlockSizes(
|
|
|
208
208
|
return undefined;
|
|
209
209
|
}
|
|
210
210
|
if (!Array.isArray(blockSizes)) {
|
|
211
|
-
logger.warn(`XAttr block sizes "${blockSizes}" is not valid`);
|
|
211
|
+
logger.warn(`XAttr block sizes "${JSON.stringify(blockSizes)}" is not valid`);
|
|
212
212
|
return undefined;
|
|
213
213
|
}
|
|
214
214
|
if (blockSizes.some((size) => typeof size !== 'number' || size <= 0)) {
|
|
215
|
-
logger.warn(`XAttr block sizes "${blockSizes}" is not valid`);
|
|
215
|
+
logger.warn(`XAttr block sizes "${JSON.stringify(blockSizes)}" is not valid`);
|
|
216
216
|
return undefined;
|
|
217
217
|
}
|
|
218
218
|
if (blockSizes.length === 0) {
|
|
@@ -37,20 +37,14 @@ export function initNodesModule(
|
|
|
37
37
|
account: ProtonDriveAccount,
|
|
38
38
|
driveCrypto: DriveCrypto,
|
|
39
39
|
sharesService: SharesService,
|
|
40
|
+
clientUid: string | undefined,
|
|
40
41
|
) {
|
|
41
|
-
const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService);
|
|
42
|
+
const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService, clientUid);
|
|
42
43
|
const cache = new NodesCache(telemetry.getLogger('nodes-cache'), driveEntitiesCache);
|
|
43
44
|
const cryptoCache = new NodesCryptoCache(telemetry.getLogger('nodes-cache'), driveCryptoCache);
|
|
44
45
|
const cryptoReporter = new NodesCryptoReporter(telemetry, sharesService);
|
|
45
46
|
const cryptoService = new NodesCryptoService(telemetry, driveCrypto, account, cryptoReporter);
|
|
46
|
-
const nodesAccess = new NodesAccess(
|
|
47
|
-
telemetry.getLogger('nodes'),
|
|
48
|
-
api,
|
|
49
|
-
cache,
|
|
50
|
-
cryptoCache,
|
|
51
|
-
cryptoService,
|
|
52
|
-
sharesService,
|
|
53
|
-
);
|
|
47
|
+
const nodesAccess = new NodesAccess(telemetry, api, cache, cryptoCache, cryptoService, sharesService);
|
|
54
48
|
const nodesEventHandler = new NodesEventsHandler(telemetry.getLogger('nodes-events'), cache);
|
|
55
49
|
const nodesManagement = new NodesManagement(api, cryptoCache, cryptoService, nodesAccess);
|
|
56
50
|
const nodesRevisions = new NodesRevisons(telemetry.getLogger('nodes'), api, cryptoService, nodesAccess);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { splitExtension, joinNameAndExtension } from './nodeName';
|
|
2
|
+
|
|
3
|
+
describe('nodeName', () => {
|
|
4
|
+
describe('splitExtension', () => {
|
|
5
|
+
it('should handle empty string', () => {
|
|
6
|
+
const result = splitExtension('');
|
|
7
|
+
expect(result).toEqual(['', '']);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should split filename with extension correctly', () => {
|
|
11
|
+
const result = splitExtension('document.pdf');
|
|
12
|
+
expect(result).toEqual(['document', 'pdf']);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should handle filename without extension', () => {
|
|
16
|
+
const result = splitExtension('folder');
|
|
17
|
+
expect(result).toEqual(['folder', '']);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should split filename with multiple dots correctly', () => {
|
|
21
|
+
const result = splitExtension('my.file.name.txt');
|
|
22
|
+
expect(result).toEqual(['my.file.name', 'txt']);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should handle filename ending with dot', () => {
|
|
26
|
+
const result = splitExtension('dot.');
|
|
27
|
+
expect(result).toEqual(['dot.', '']);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle filename with only extension', () => {
|
|
31
|
+
const result = splitExtension('.gitignore');
|
|
32
|
+
expect(result).toEqual(['.gitignore', '']);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('joinNameAndExtension', () => {
|
|
37
|
+
it('should join name, index, and extension correctly', () => {
|
|
38
|
+
const result = joinNameAndExtension('document', 1, 'pdf');
|
|
39
|
+
expect(result).toBe('document (1).pdf');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle empty name with extension', () => {
|
|
43
|
+
const result = joinNameAndExtension('', 2, 'txt');
|
|
44
|
+
expect(result).toBe('(2).txt');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle name with empty extension', () => {
|
|
48
|
+
const result = joinNameAndExtension('document', 3, '');
|
|
49
|
+
expect(result).toBe('document (3)');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should handle both name and extension empty', () => {
|
|
53
|
+
const result = joinNameAndExtension('', 4, '');
|
|
54
|
+
expect(result).toBe('(4)');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a filename into `[name, extension]`
|
|
3
|
+
*/
|
|
4
|
+
export function splitExtension(filename = ''): [string, string] {
|
|
5
|
+
const endIdx = filename.lastIndexOf('.');
|
|
6
|
+
if (endIdx === -1 || endIdx === 0 || endIdx === filename.length - 1) {
|
|
7
|
+
return [filename, ''];
|
|
8
|
+
}
|
|
9
|
+
return [filename.slice(0, endIdx), filename.slice(endIdx + 1)];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Join a filename into `name (index).extension`
|
|
14
|
+
*/
|
|
15
|
+
export function joinNameAndExtension(name: string, index: number, extension: string): string {
|
|
16
|
+
if (!name && !extension) {
|
|
17
|
+
return `(${index})`;
|
|
18
|
+
}
|
|
19
|
+
if (!name) {
|
|
20
|
+
return `(${index}).${extension}`;
|
|
21
|
+
}
|
|
22
|
+
if (!extension) {
|
|
23
|
+
return `${name} (${index})`;
|
|
24
|
+
}
|
|
25
|
+
return `${name} (${index}).${extension}`;
|
|
26
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getMockTelemetry } from '../../tests/telemetry';
|
|
2
2
|
import { PrivateKey } from '../../crypto';
|
|
3
3
|
import { DecryptionError } from '../../errors';
|
|
4
4
|
import { NodeAPIService } from './apiService';
|
|
@@ -50,7 +50,7 @@ describe('nodesAccess', () => {
|
|
|
50
50
|
getSharePrivateKey: jest.fn(),
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
access = new NodesAccess(
|
|
53
|
+
access = new NodesAccess(getMockTelemetry(), apiService, cache, cryptoCache, cryptoService, shareService);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
describe('getNode', () => {
|
|
@@ -569,7 +569,11 @@ describe('nodesAccess', () => {
|
|
|
569
569
|
it('should get share parent keys', async () => {
|
|
570
570
|
shareService.getSharePrivateKey = jest.fn(() => Promise.resolve('shareKey' as any as PrivateKey));
|
|
571
571
|
|
|
572
|
-
const result = await access.getParentKeys({
|
|
572
|
+
const result = await access.getParentKeys({
|
|
573
|
+
uid: 'volumeId~nodeId',
|
|
574
|
+
shareId: 'shareId',
|
|
575
|
+
parentUid: undefined,
|
|
576
|
+
});
|
|
573
577
|
expect(result).toEqual({ key: 'shareKey' });
|
|
574
578
|
expect(cryptoCache.getNodeKeys).not.toHaveBeenCalled();
|
|
575
579
|
});
|
|
@@ -577,7 +581,11 @@ describe('nodesAccess', () => {
|
|
|
577
581
|
it('should get node parent keys', async () => {
|
|
578
582
|
cryptoCache.getNodeKeys = jest.fn(() => Promise.resolve({ key: 'parentKey' } as any as DecryptedNodeKeys));
|
|
579
583
|
|
|
580
|
-
const result = await access.getParentKeys({
|
|
584
|
+
const result = await access.getParentKeys({
|
|
585
|
+
uid: 'volumeId~nodeId',
|
|
586
|
+
shareId: undefined,
|
|
587
|
+
parentUid: 'volumeId~parentNodeid',
|
|
588
|
+
});
|
|
581
589
|
expect(result).toEqual({ key: 'parentKey' });
|
|
582
590
|
expect(shareService.getSharePrivateKey).not.toHaveBeenCalled();
|
|
583
591
|
});
|
|
@@ -585,7 +593,11 @@ describe('nodesAccess', () => {
|
|
|
585
593
|
it('should get node parent keys even if share is set', async () => {
|
|
586
594
|
cryptoCache.getNodeKeys = jest.fn(() => Promise.resolve({ key: 'parentKey' } as any as DecryptedNodeKeys));
|
|
587
595
|
|
|
588
|
-
const result = await access.getParentKeys({
|
|
596
|
+
const result = await access.getParentKeys({
|
|
597
|
+
uid: 'volume1~nodeId',
|
|
598
|
+
shareId: 'shareId',
|
|
599
|
+
parentUid: 'volume1~parentNodeid',
|
|
600
|
+
});
|
|
589
601
|
expect(result).toEqual({ key: 'parentKey' });
|
|
590
602
|
expect(shareService.getSharePrivateKey).not.toHaveBeenCalled();
|
|
591
603
|
});
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { c } from 'ttag';
|
|
2
2
|
|
|
3
3
|
import { PrivateKey, SessionKey } from '../../crypto';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
InvalidNameError,
|
|
6
|
+
Logger,
|
|
7
|
+
MissingNode,
|
|
8
|
+
NodeType,
|
|
9
|
+
ProtonDriveTelemetry,
|
|
10
|
+
Result,
|
|
11
|
+
resultError,
|
|
12
|
+
resultOk,
|
|
13
|
+
} from '../../interface';
|
|
5
14
|
import { DecryptionError, ProtonDriveError } from '../../errors';
|
|
6
15
|
import { asyncIteratorMap } from '../asyncIteratorMap';
|
|
7
16
|
import { getErrorMessage } from '../errors';
|
|
@@ -41,10 +50,11 @@ const DECRYPTION_CONCURRENCY = 30;
|
|
|
41
50
|
* nodes metadata.
|
|
42
51
|
*/
|
|
43
52
|
export class NodesAccess {
|
|
53
|
+
private logger: Logger;
|
|
44
54
|
private debouncer: NodesDebouncer;
|
|
45
55
|
|
|
46
56
|
constructor(
|
|
47
|
-
private
|
|
57
|
+
private telemetry: ProtonDriveTelemetry,
|
|
48
58
|
private apiService: NodeAPIService,
|
|
49
59
|
private cache: NodesCache,
|
|
50
60
|
private cryptoCache: NodesCryptoCache,
|
|
@@ -54,13 +64,13 @@ export class NodesAccess {
|
|
|
54
64
|
'getOwnVolumeIDs' | 'getSharePrivateKey' | 'getContextShareMemberEmailKey'
|
|
55
65
|
>,
|
|
56
66
|
) {
|
|
57
|
-
this.logger =
|
|
67
|
+
this.logger = telemetry.getLogger('nodes');
|
|
58
68
|
this.apiService = apiService;
|
|
59
69
|
this.cache = cache;
|
|
60
70
|
this.cryptoCache = cryptoCache;
|
|
61
71
|
this.cryptoService = cryptoService;
|
|
62
72
|
this.shareService = shareService;
|
|
63
|
-
this.debouncer = new NodesDebouncer(this.
|
|
73
|
+
this.debouncer = new NodesDebouncer(this.telemetry);
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
async getVolumeRootFolder() {
|
|
@@ -357,7 +367,7 @@ export class NodesAccess {
|
|
|
357
367
|
}
|
|
358
368
|
|
|
359
369
|
async getParentKeys(
|
|
360
|
-
node: Pick<DecryptedNode, 'parentUid' | 'shareId'>,
|
|
370
|
+
node: Pick<DecryptedNode, 'uid' | 'parentUid' | 'shareId'>,
|
|
361
371
|
): Promise<Pick<DecryptedNodeKeys, 'key' | 'hashKey'>> {
|
|
362
372
|
if (node.parentUid) {
|
|
363
373
|
try {
|