@protontech/drive-sdk 0.4.1 → 0.5.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/sdkDiagnostic.js +1 -1
- package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
- package/dist/interface/download.d.ts +4 -4
- package/dist/interface/upload.d.ts +6 -3
- package/dist/internal/apiService/apiService.d.ts +3 -0
- package/dist/internal/apiService/apiService.js +25 -2
- package/dist/internal/apiService/apiService.js.map +1 -1
- package/dist/internal/apiService/apiService.test.js +38 -0
- package/dist/internal/apiService/apiService.test.js.map +1 -1
- package/dist/internal/apiService/driveTypes.d.ts +31 -48
- package/dist/internal/apiService/errors.js +3 -0
- package/dist/internal/apiService/errors.js.map +1 -1
- package/dist/internal/apiService/errors.test.js +15 -7
- package/dist/internal/apiService/errors.test.js.map +1 -1
- package/dist/internal/asyncIteratorMap.d.ts +1 -1
- package/dist/internal/asyncIteratorMap.js +6 -1
- package/dist/internal/asyncIteratorMap.js.map +1 -1
- package/dist/internal/asyncIteratorMap.test.js +9 -0
- package/dist/internal/asyncIteratorMap.test.js.map +1 -1
- package/dist/internal/download/fileDownloader.d.ts +3 -3
- package/dist/internal/download/fileDownloader.js +5 -5
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/download/fileDownloader.test.js +8 -8
- package/dist/internal/download/fileDownloader.test.js.map +1 -1
- package/dist/internal/nodes/apiService.d.ts +6 -1
- package/dist/internal/nodes/apiService.js +44 -32
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +148 -17
- package/dist/internal/nodes/apiService.test.js.map +1 -1
- package/dist/internal/nodes/debouncer.d.ts +23 -0
- package/dist/internal/nodes/debouncer.js +80 -0
- package/dist/internal/nodes/debouncer.js.map +1 -0
- package/dist/internal/nodes/debouncer.test.d.ts +1 -0
- package/dist/internal/nodes/debouncer.test.js +100 -0
- package/dist/internal/nodes/debouncer.test.js.map +1 -0
- package/dist/internal/nodes/nodesAccess.d.ts +2 -1
- package/dist/internal/nodes/nodesAccess.js +24 -5
- 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/photos/upload.d.ts +2 -1
- package/dist/internal/photos/upload.js +3 -3
- package/dist/internal/photos/upload.js.map +1 -1
- package/dist/internal/sharingPublic/apiService.d.ts +2 -2
- package/dist/internal/sharingPublic/apiService.js +1 -63
- package/dist/internal/sharingPublic/apiService.js.map +1 -1
- package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -4
- package/dist/internal/sharingPublic/cryptoCache.js +0 -28
- package/dist/internal/sharingPublic/cryptoCache.js.map +1 -1
- package/dist/internal/sharingPublic/cryptoReporter.d.ts +16 -0
- package/dist/internal/sharingPublic/cryptoReporter.js +44 -0
- package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -0
- package/dist/internal/sharingPublic/cryptoService.d.ts +3 -4
- package/dist/internal/sharingPublic/cryptoService.js +5 -43
- package/dist/internal/sharingPublic/cryptoService.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +21 -3
- package/dist/internal/sharingPublic/index.js +43 -12
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/interface.d.ts +0 -1
- package/dist/internal/sharingPublic/nodes.d.ts +13 -0
- package/dist/internal/sharingPublic/nodes.js +28 -0
- package/dist/internal/sharingPublic/nodes.js.map +1 -0
- package/dist/internal/sharingPublic/session/session.d.ts +3 -3
- package/dist/internal/sharingPublic/session/url.test.js +3 -3
- package/dist/internal/sharingPublic/shares.d.ts +34 -0
- package/dist/internal/sharingPublic/shares.js +69 -0
- package/dist/internal/sharingPublic/shares.js.map +1 -0
- package/dist/internal/upload/apiService.js +10 -1
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/controller.d.ts +8 -2
- package/dist/internal/upload/controller.js.map +1 -1
- package/dist/internal/upload/fileUploader.d.ts +6 -3
- package/dist/internal/upload/fileUploader.js +3 -3
- package/dist/internal/upload/fileUploader.js.map +1 -1
- package/dist/internal/upload/fileUploader.test.js +23 -11
- package/dist/internal/upload/fileUploader.test.js.map +1 -1
- package/dist/internal/upload/streamUploader.d.ts +6 -2
- package/dist/internal/upload/streamUploader.js +8 -4
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +10 -6
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +3 -3
- package/dist/protonDriveClient.js +4 -4
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +31 -4
- package/dist/protonDrivePublicLinkClient.js +52 -9
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/package.json +1 -1
- package/src/diagnostic/sdkDiagnostic.ts +1 -1
- package/src/interface/download.ts +4 -4
- package/src/interface/upload.ts +3 -3
- package/src/internal/apiService/apiService.test.ts +50 -0
- package/src/internal/apiService/apiService.ts +33 -2
- package/src/internal/apiService/driveTypes.ts +31 -48
- package/src/internal/apiService/errors.test.ts +10 -0
- package/src/internal/apiService/errors.ts +5 -1
- package/src/internal/asyncIteratorMap.test.ts +12 -0
- package/src/internal/asyncIteratorMap.ts +8 -0
- package/src/internal/download/fileDownloader.test.ts +8 -8
- package/src/internal/download/fileDownloader.ts +5 -5
- package/src/internal/nodes/apiService.test.ts +199 -16
- package/src/internal/nodes/apiService.ts +62 -49
- package/src/internal/nodes/debouncer.test.ts +129 -0
- package/src/internal/nodes/debouncer.ts +93 -0
- package/src/internal/nodes/nodesAccess.test.ts +2 -2
- package/src/internal/nodes/nodesAccess.ts +30 -5
- package/src/internal/photos/upload.ts +4 -1
- package/src/internal/sharingPublic/apiService.ts +4 -87
- package/src/internal/sharingPublic/cryptoCache.ts +0 -34
- package/src/internal/sharingPublic/cryptoReporter.ts +73 -0
- package/src/internal/sharingPublic/cryptoService.ts +4 -80
- package/src/internal/sharingPublic/index.ts +68 -6
- package/src/internal/sharingPublic/interface.ts +0 -9
- package/src/internal/sharingPublic/nodes.ts +37 -0
- package/src/internal/sharingPublic/session/apiService.ts +1 -1
- package/src/internal/sharingPublic/session/session.ts +3 -3
- package/src/internal/sharingPublic/session/url.test.ts +3 -3
- package/src/internal/sharingPublic/shares.ts +86 -0
- package/src/internal/upload/apiService.ts +12 -1
- package/src/internal/upload/controller.ts +2 -2
- package/src/internal/upload/fileUploader.test.ts +25 -11
- package/src/internal/upload/fileUploader.ts +4 -3
- package/src/internal/upload/streamUploader.test.ts +15 -3
- package/src/internal/upload/streamUploader.ts +8 -3
- package/src/protonDriveClient.ts +4 -4
- package/src/protonDrivePublicLinkClient.ts +93 -12
- package/dist/internal/sharingPublic/manager.d.ts +0 -19
- package/dist/internal/sharingPublic/manager.js +0 -81
- package/dist/internal/sharingPublic/manager.js.map +0 -1
- package/src/internal/sharingPublic/manager.ts +0 -86
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ProtonDrivePublicLinkClient = void 0;
|
|
4
|
+
const cache_1 = require("./cache");
|
|
4
5
|
const config_1 = require("./config");
|
|
5
6
|
const crypto_1 = require("./crypto");
|
|
6
7
|
const telemetry_1 = require("./telemetry");
|
|
7
8
|
const transformers_1 = require("./transformers");
|
|
8
9
|
const apiService_1 = require("./internal/apiService");
|
|
10
|
+
const download_1 = require("./internal/download");
|
|
9
11
|
const sdkEvents_1 = require("./internal/sdkEvents");
|
|
10
12
|
const sharingPublic_1 = require("./internal/sharingPublic");
|
|
11
13
|
/**
|
|
@@ -25,26 +27,30 @@ class ProtonDrivePublicLinkClient {
|
|
|
25
27
|
logger;
|
|
26
28
|
sdkEvents;
|
|
27
29
|
sharingPublic;
|
|
30
|
+
download;
|
|
28
31
|
experimental;
|
|
29
|
-
constructor({ httpClient,
|
|
32
|
+
constructor({ httpClient, account, openPGPCryptoModule, srpModule, config, telemetry, url, token, password, }) {
|
|
30
33
|
if (!telemetry) {
|
|
31
34
|
telemetry = new telemetry_1.Telemetry();
|
|
32
35
|
}
|
|
33
36
|
this.logger = telemetry.getLogger('interface');
|
|
37
|
+
// Use only in memory cache for public link as there are no events to keep it up to date if persisted.
|
|
38
|
+
const entitiesCache = new cache_1.MemoryCache();
|
|
39
|
+
const cryptoCache = new cache_1.MemoryCache();
|
|
34
40
|
const fullConfig = (0, config_1.getConfig)(config);
|
|
35
41
|
this.sdkEvents = new sdkEvents_1.SDKEvents(telemetry);
|
|
36
42
|
const apiService = new apiService_1.DriveAPIService(telemetry, this.sdkEvents, httpClient, fullConfig.baseUrl, fullConfig.language);
|
|
37
|
-
const
|
|
38
|
-
this.sharingPublic = (0, sharingPublic_1.initSharingPublicModule)(telemetry, apiService, cryptoCache,
|
|
43
|
+
const cryptoModule = new crypto_1.DriveCrypto(openPGPCryptoModule, srpModule);
|
|
44
|
+
this.sharingPublic = (0, sharingPublic_1.initSharingPublicModule)(telemetry, apiService, entitiesCache, cryptoCache, cryptoModule, account, url, token, password);
|
|
45
|
+
this.download = (0, download_1.initDownloadModule)(telemetry, apiService, cryptoModule, account, this.sharingPublic.shares, this.sharingPublic.nodes.access, this.sharingPublic.nodes.revisions);
|
|
39
46
|
this.experimental = {
|
|
40
47
|
getNodeUrl: async (nodeUid) => {
|
|
41
48
|
this.logger.debug(`Getting node URL for ${(0, transformers_1.getUid)(nodeUid)}`);
|
|
42
|
-
|
|
43
|
-
return '';
|
|
49
|
+
return this.sharingPublic.nodes.access.getNodeUrl((0, transformers_1.getUid)(nodeUid));
|
|
44
50
|
},
|
|
45
51
|
getDocsKey: async (nodeUid) => {
|
|
46
52
|
this.logger.debug(`Getting docs keys for ${(0, transformers_1.getUid)(nodeUid)}`);
|
|
47
|
-
const keys = await this.sharingPublic.getNodeKeys((0, transformers_1.getUid)(nodeUid));
|
|
53
|
+
const keys = await this.sharingPublic.nodes.access.getNodeKeys((0, transformers_1.getUid)(nodeUid));
|
|
48
54
|
if (!keys.contentKeyPacketSessionKey) {
|
|
49
55
|
throw new Error('Node does not have a content key packet session key');
|
|
50
56
|
}
|
|
@@ -57,16 +63,53 @@ class ProtonDrivePublicLinkClient {
|
|
|
57
63
|
*/
|
|
58
64
|
async getRootNode() {
|
|
59
65
|
this.logger.info(`Getting root node`);
|
|
60
|
-
|
|
66
|
+
const { rootNodeUid } = await this.sharingPublic.shares.getOwnVolumeIDs();
|
|
67
|
+
return (0, transformers_1.convertInternalNodePromise)(this.sharingPublic.nodes.access.getNode(rootNodeUid));
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
70
|
* Iterates the children of the given parent node.
|
|
64
71
|
*
|
|
65
72
|
* See `ProtonDriveClient.iterateFolderChildren` for more information.
|
|
66
73
|
*/
|
|
67
|
-
async *iterateFolderChildren(parentUid, signal) {
|
|
74
|
+
async *iterateFolderChildren(parentUid, filterOptions, signal) {
|
|
68
75
|
this.logger.info(`Iterating children of ${(0, transformers_1.getUid)(parentUid)}`);
|
|
69
|
-
yield* (0, transformers_1.convertInternalNodeIterator)(this.sharingPublic.iterateFolderChildren((0, transformers_1.getUid)(parentUid), signal));
|
|
76
|
+
yield* (0, transformers_1.convertInternalNodeIterator)(this.sharingPublic.nodes.access.iterateFolderChildren((0, transformers_1.getUid)(parentUid), filterOptions, signal));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Iterates the nodes by their UIDs.
|
|
80
|
+
*
|
|
81
|
+
* See `ProtonDriveClient.iterateNodes` for more information.
|
|
82
|
+
*/
|
|
83
|
+
async *iterateNodes(nodeUids, signal) {
|
|
84
|
+
this.logger.info(`Iterating ${nodeUids.length} nodes`);
|
|
85
|
+
yield* (0, transformers_1.convertInternalMissingNodeIterator)(this.sharingPublic.nodes.access.iterateNodes((0, transformers_1.getUids)(nodeUids), signal));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the node by its UID.
|
|
89
|
+
*
|
|
90
|
+
* See `ProtonDriveClient.getNode` for more information.
|
|
91
|
+
*/
|
|
92
|
+
async getNode(nodeUid) {
|
|
93
|
+
this.logger.info(`Getting node ${(0, transformers_1.getUid)(nodeUid)}`);
|
|
94
|
+
return (0, transformers_1.convertInternalNodePromise)(this.sharingPublic.nodes.access.getNode((0, transformers_1.getUid)(nodeUid)));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the file downloader to download the node content.
|
|
98
|
+
*
|
|
99
|
+
* See `ProtonDriveClient.getFileDownloader` for more information.
|
|
100
|
+
*/
|
|
101
|
+
async getFileDownloader(nodeUid, signal) {
|
|
102
|
+
this.logger.info(`Getting file downloader for ${(0, transformers_1.getUid)(nodeUid)}`);
|
|
103
|
+
return this.download.getFileDownloader((0, transformers_1.getUid)(nodeUid), signal);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Iterates the thumbnails of the given nodes.
|
|
107
|
+
*
|
|
108
|
+
* See `ProtonDriveClient.iterateThumbnails` for more information.
|
|
109
|
+
*/
|
|
110
|
+
async *iterateThumbnails(nodeUids, thumbnailType, signal) {
|
|
111
|
+
this.logger.info(`Iterating ${nodeUids.length} thumbnails`);
|
|
112
|
+
yield* this.download.iterateThumbnails((0, transformers_1.getUids)(nodeUids), thumbnailType, signal);
|
|
70
113
|
}
|
|
71
114
|
}
|
|
72
115
|
exports.ProtonDrivePublicLinkClient = ProtonDrivePublicLinkClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protonDrivePublicLinkClient.js","sourceRoot":"","sources":["../src/protonDrivePublicLinkClient.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AACrC,qCAA6E;
|
|
1
|
+
{"version":3,"file":"protonDrivePublicLinkClient.js","sourceRoot":"","sources":["../src/protonDrivePublicLinkClient.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -133,7 +133,7 @@ export class SDKDiagnostic implements Diagnostic {
|
|
|
133
133
|
const claimedSizeInBytes = downloader.getClaimedSizeInBytes();
|
|
134
134
|
|
|
135
135
|
const integrityVerificationStream = new IntegrityVerificationStream();
|
|
136
|
-
const controller = downloader.
|
|
136
|
+
const controller = downloader.downloadToStream(integrityVerificationStream);
|
|
137
137
|
|
|
138
138
|
try {
|
|
139
139
|
await controller.completion();
|
|
@@ -15,14 +15,14 @@ export interface FileDownloader {
|
|
|
15
15
|
*
|
|
16
16
|
* @param onProgress - Callback that is called with the number of downloaded bytes
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
downloadToStream(streamFactory: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Same as `
|
|
21
|
+
* Same as `downloadToStream` but without verification checks.
|
|
22
22
|
*
|
|
23
23
|
* Use this only for debugging purposes.
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
unsafeDownloadToStream(
|
|
26
26
|
streamFactory: WritableStream,
|
|
27
27
|
onProgress?: (downloadedBytes: number) => void,
|
|
28
28
|
): DownloadController;
|
|
@@ -34,7 +34,7 @@ export interface FileDownloader {
|
|
|
34
34
|
* need to download the entire file.
|
|
35
35
|
*
|
|
36
36
|
* Stream doesn't verify data integrity. For the full integrity of
|
|
37
|
-
* the file, use `
|
|
37
|
+
* the file, use `downloadToStream` instead.
|
|
38
38
|
*
|
|
39
39
|
* The stream is not opportunitistically downloading the data ahead of
|
|
40
40
|
* the time. It will only download the data when it is requested. To
|
package/src/interface/upload.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface FileRevisionUploader {
|
|
|
41
41
|
*
|
|
42
42
|
* The function will reject if the node with the given name already exists.
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
uploadFromStream(
|
|
45
45
|
stream: ReadableStream,
|
|
46
46
|
thumnbails: Thumbnail[],
|
|
47
47
|
onProgress?: (uploadedBytes: number) => void,
|
|
@@ -57,7 +57,7 @@ export interface FileRevisionUploader {
|
|
|
57
57
|
*
|
|
58
58
|
* The function will reject if the node with the given name already exists.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
uploadFromFile(
|
|
61
61
|
fileObject: File,
|
|
62
62
|
thumnbails: Thumbnail[],
|
|
63
63
|
onProgress?: (uploadedBytes: number) => void,
|
|
@@ -79,5 +79,5 @@ export interface FileUploader extends FileRevisionUploader {
|
|
|
79
79
|
export interface UploadController {
|
|
80
80
|
pause(): void;
|
|
81
81
|
resume(): void;
|
|
82
|
-
completion(): Promise<string>;
|
|
82
|
+
completion(): Promise<{ nodeRevisionUid: string, nodeUid: string }>;
|
|
83
83
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbortError } from '../../errors';
|
|
1
2
|
import { ProtonDriveHTTPClient, SDKEvent } from '../../interface';
|
|
2
3
|
import { getMockTelemetry } from '../../tests/telemetry';
|
|
3
4
|
import { SDKEvents } from '../sdkEvents';
|
|
@@ -112,6 +113,16 @@ describe('DriveAPIService', () => {
|
|
|
112
113
|
});
|
|
113
114
|
|
|
114
115
|
describe('should throw', () => {
|
|
116
|
+
it('AbortError on aborted error from the provided HTTP client', async () => {
|
|
117
|
+
const abortError = new Error('AbortError');
|
|
118
|
+
abortError.name = 'AbortError';
|
|
119
|
+
|
|
120
|
+
httpClient.fetchJson = jest.fn(() => Promise.reject(abortError));
|
|
121
|
+
|
|
122
|
+
await expect(api.get('test')).rejects.toThrow(new AbortError('Request aborted'));
|
|
123
|
+
expectSDKEvents();
|
|
124
|
+
});
|
|
125
|
+
|
|
115
126
|
it('APIHTTPError on 4xx response without JSON body', async () => {
|
|
116
127
|
httpClient.fetchJson = jest.fn(() =>
|
|
117
128
|
Promise.resolve(new Response('Not found', { status: 404, statusText: 'Not found' })),
|
|
@@ -314,5 +325,44 @@ describe('DriveAPIService', () => {
|
|
|
314
325
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(35);
|
|
315
326
|
expectSDKEvents();
|
|
316
327
|
});
|
|
328
|
+
|
|
329
|
+
it('notify about offline error', async () => {
|
|
330
|
+
jest.useFakeTimers();
|
|
331
|
+
const offlineError = new Error('OfflineError');
|
|
332
|
+
offlineError.name = 'OfflineError';
|
|
333
|
+
|
|
334
|
+
let attempt = 0;
|
|
335
|
+
httpClient.fetchJson = jest.fn().mockImplementation(() => {
|
|
336
|
+
if (attempt++ >= 15) {
|
|
337
|
+
return generateOkResponse();
|
|
338
|
+
}
|
|
339
|
+
throw offlineError;
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const promise = api.get('test');
|
|
343
|
+
|
|
344
|
+
// First 9 calls (first is immediate, then 8 with 5 second delay), no events are sent yet
|
|
345
|
+
await jest.advanceTimersByTimeAsync(5 * 8 * 1000);
|
|
346
|
+
expect(httpClient.fetchJson).toHaveBeenCalledTimes(9);
|
|
347
|
+
expectSDKEvents();
|
|
348
|
+
|
|
349
|
+
// 10th call, service sends TransfersPaused event
|
|
350
|
+
await jest.advanceTimersByTimeAsync(5 * 1000);
|
|
351
|
+
expect(httpClient.fetchJson).toHaveBeenCalledTimes(10);
|
|
352
|
+
expectSDKEvents(SDKEvent.TransfersPaused);
|
|
353
|
+
|
|
354
|
+
// Next 5 calls, still offline, no more events are sent
|
|
355
|
+
await jest.advanceTimersByTimeAsync(5 * 5 * 1000);
|
|
356
|
+
expect(httpClient.fetchJson).toHaveBeenCalledTimes(15);
|
|
357
|
+
expectSDKEvents(SDKEvent.TransfersPaused);
|
|
358
|
+
|
|
359
|
+
// 16th call, mock returns OK response, service sends TransfersResumed event
|
|
360
|
+
await jest.advanceTimersByTimeAsync(5 * 1000);
|
|
361
|
+
expect(httpClient.fetchJson).toHaveBeenCalledTimes(16);
|
|
362
|
+
expectSDKEvents(SDKEvent.TransfersPaused, SDKEvent.TransfersResumed);
|
|
363
|
+
|
|
364
|
+
await promise;
|
|
365
|
+
});
|
|
366
|
+
|
|
317
367
|
});
|
|
318
368
|
});
|
|
@@ -40,6 +40,11 @@ const TOO_MANY_SUBSEQUENT_SERVER_ERRORS = 10;
|
|
|
40
40
|
*/
|
|
41
41
|
const TOO_MANY_SUBSEQUENT_SERVER_ERRORS_TIMEOUT_IN_SECONDS = 60;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* How many subsequent offline errors are allowed before we consider the client offline.
|
|
45
|
+
*/
|
|
46
|
+
const TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS = 10;
|
|
47
|
+
|
|
43
48
|
/**
|
|
44
49
|
* After how long to re-try after 5xx or timeout error.
|
|
45
50
|
*/
|
|
@@ -88,6 +93,8 @@ export class DriveAPIService {
|
|
|
88
93
|
private subsequentServerErrorsCounter = 0;
|
|
89
94
|
private lastServerErrorAt?: number;
|
|
90
95
|
|
|
96
|
+
private subsequentOfflineErrorsCounter = 0;
|
|
97
|
+
|
|
91
98
|
private logger: Logger;
|
|
92
99
|
|
|
93
100
|
constructor(
|
|
@@ -219,7 +226,7 @@ export class DriveAPIService {
|
|
|
219
226
|
if (error instanceof ProtonDriveError) {
|
|
220
227
|
throw error;
|
|
221
228
|
}
|
|
222
|
-
throw apiErrorFactory({ response });
|
|
229
|
+
throw apiErrorFactory({ response, error });
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
return response;
|
|
@@ -261,7 +268,13 @@ export class DriveAPIService {
|
|
|
261
268
|
response = await callback();
|
|
262
269
|
} catch (error: unknown) {
|
|
263
270
|
if (error instanceof Error) {
|
|
271
|
+
if (error.name === 'AbortError') {
|
|
272
|
+
this.logger.debug(`${request.method} ${request.url}: Aborted`);
|
|
273
|
+
throw new AbortError(c('Error').t`Request aborted`);
|
|
274
|
+
}
|
|
275
|
+
|
|
264
276
|
if (error.name === 'OfflineError') {
|
|
277
|
+
this.offlineErrorHappened();
|
|
265
278
|
this.logger.info(`${request.method} ${request.url}: Offline error, retrying`);
|
|
266
279
|
await waitSeconds(OFFLINE_RETRY_DELAY_SECONDS);
|
|
267
280
|
return this.fetch(request, callback, attempt + 1);
|
|
@@ -282,6 +295,8 @@ export class DriveAPIService {
|
|
|
282
295
|
throw error;
|
|
283
296
|
}
|
|
284
297
|
|
|
298
|
+
this.clearSubsequentOfflineErrors();
|
|
299
|
+
|
|
285
300
|
const end = Date.now();
|
|
286
301
|
const duration = end - start;
|
|
287
302
|
|
|
@@ -342,7 +357,7 @@ export class DriveAPIService {
|
|
|
342
357
|
// the client is very limited. This is generic event and it doesn't
|
|
343
358
|
// take into account that various endpoints can be rate limited
|
|
344
359
|
// independently.
|
|
345
|
-
if (this.subsequentTooManyRequestsCounter
|
|
360
|
+
if (this.subsequentTooManyRequestsCounter === TOO_MANY_SUBSEQUENT_429_ERRORS) {
|
|
346
361
|
this.sdkEvents.requestsThrottled();
|
|
347
362
|
}
|
|
348
363
|
}
|
|
@@ -373,4 +388,20 @@ export class DriveAPIService {
|
|
|
373
388
|
this.subsequentServerErrorsCounter = 0;
|
|
374
389
|
this.lastServerErrorAt = undefined;
|
|
375
390
|
}
|
|
391
|
+
|
|
392
|
+
private offlineErrorHappened() {
|
|
393
|
+
this.subsequentOfflineErrorsCounter++;
|
|
394
|
+
|
|
395
|
+
if (this.subsequentOfflineErrorsCounter === TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS) {
|
|
396
|
+
this.sdkEvents.transfersPaused();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private clearSubsequentOfflineErrors() {
|
|
401
|
+
if (this.subsequentOfflineErrorsCounter >= TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS) {
|
|
402
|
+
this.sdkEvents.transfersResumed();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
this.subsequentOfflineErrorsCounter = 0;
|
|
406
|
+
}
|
|
376
407
|
}
|
|
@@ -1540,10 +1540,16 @@ export interface paths {
|
|
|
1540
1540
|
path?: never;
|
|
1541
1541
|
cookie?: never;
|
|
1542
1542
|
};
|
|
1543
|
-
/**
|
|
1543
|
+
/**
|
|
1544
|
+
* Get status of migration from legacy photo share on a regular volume into a new Photo Volume
|
|
1545
|
+
* @deprecated
|
|
1546
|
+
*/
|
|
1544
1547
|
get: operations['get_drive-photos-migrate-legacy'];
|
|
1545
1548
|
put?: never;
|
|
1546
|
-
/**
|
|
1549
|
+
/**
|
|
1550
|
+
* DEPRECATED: All shares have been migrated, always returns share not found
|
|
1551
|
+
* @deprecated
|
|
1552
|
+
*/
|
|
1547
1553
|
post: operations['post_drive-photos-migrate-legacy'];
|
|
1548
1554
|
delete?: never;
|
|
1549
1555
|
options?: never;
|
|
@@ -3502,7 +3508,7 @@ export interface components {
|
|
|
3502
3508
|
Thumbnail: number | null;
|
|
3503
3509
|
/**
|
|
3504
3510
|
* @deprecated
|
|
3505
|
-
* @description
|
|
3511
|
+
* @description sha256 hash of thumbnail contents
|
|
3506
3512
|
* @default null
|
|
3507
3513
|
*/
|
|
3508
3514
|
ThumbnailHash: string | null;
|
|
@@ -3679,7 +3685,6 @@ export interface components {
|
|
|
3679
3685
|
*/
|
|
3680
3686
|
Code: 1000;
|
|
3681
3687
|
};
|
|
3682
|
-
MigrateFromLegacyRequest: Record<string, never>;
|
|
3683
3688
|
RemoveTagsRequestDto: {
|
|
3684
3689
|
Tags: components['schemas']['TagType'][];
|
|
3685
3690
|
};
|
|
@@ -5097,7 +5102,10 @@ export interface components {
|
|
|
5097
5102
|
File: components['schemas']['FileDto'];
|
|
5098
5103
|
/** @default null */
|
|
5099
5104
|
Sharing: components['schemas']['SharingDto'] | null;
|
|
5100
|
-
/**
|
|
5105
|
+
/**
|
|
5106
|
+
* @description Will be null if the user is not a member or is the owner.
|
|
5107
|
+
* @default null
|
|
5108
|
+
*/
|
|
5101
5109
|
Membership: components['schemas']['MembershipDto'] | null;
|
|
5102
5110
|
/** @default null */
|
|
5103
5111
|
Folder: null | null;
|
|
@@ -5109,7 +5117,10 @@ export interface components {
|
|
|
5109
5117
|
Folder: components['schemas']['FolderDto'];
|
|
5110
5118
|
/** @default null */
|
|
5111
5119
|
Sharing: components['schemas']['SharingDto'] | null;
|
|
5112
|
-
/**
|
|
5120
|
+
/**
|
|
5121
|
+
* @description Will be null if the user is not a member or is the owner.
|
|
5122
|
+
* @default null
|
|
5123
|
+
*/
|
|
5113
5124
|
Membership: components['schemas']['MembershipDto'] | null;
|
|
5114
5125
|
/** @default null */
|
|
5115
5126
|
File: null | null;
|
|
@@ -5235,7 +5246,7 @@ export interface components {
|
|
|
5235
5246
|
Size: number;
|
|
5236
5247
|
/** @description Index of block in list (must be consecutive starting at 1) */
|
|
5237
5248
|
Index: number;
|
|
5238
|
-
/** @description
|
|
5249
|
+
/** @description sha256 hash of encrypted block, base64 encoded */
|
|
5239
5250
|
Hash: string;
|
|
5240
5251
|
/** @default null */
|
|
5241
5252
|
Verifier: components['schemas']['Verifier'] | null;
|
|
@@ -5249,7 +5260,7 @@ export interface components {
|
|
|
5249
5260
|
/** @description Block size in bytes. WARNING: when type is NOT 2=HDPreview(1920) then the max size is 65536 */
|
|
5250
5261
|
Size: number;
|
|
5251
5262
|
Type: components['schemas']['ThumbnailType'];
|
|
5252
|
-
/** @description
|
|
5263
|
+
/** @description sha256 hash of encrypted block, base64 encoded */
|
|
5253
5264
|
Hash: string;
|
|
5254
5265
|
};
|
|
5255
5266
|
BlockURL: {
|
|
@@ -5330,7 +5341,7 @@ export interface components {
|
|
|
5330
5341
|
Size: number;
|
|
5331
5342
|
/** @description Index of block in list (must be consecutive starting at 1) */
|
|
5332
5343
|
Index: number;
|
|
5333
|
-
/** @description
|
|
5344
|
+
/** @description sha256 hash of encrypted block, base64 encoded */
|
|
5334
5345
|
Hash: string;
|
|
5335
5346
|
Verifier: components['schemas']['Verifier'];
|
|
5336
5347
|
/**
|
|
@@ -5387,7 +5398,10 @@ export interface components {
|
|
|
5387
5398
|
Folder: components['schemas']['FolderDto2'];
|
|
5388
5399
|
/** @default null */
|
|
5389
5400
|
Sharing: components['schemas']['SharingDto2'] | null;
|
|
5390
|
-
/**
|
|
5401
|
+
/**
|
|
5402
|
+
* @description Will be null if the user is not a member or is the owner.
|
|
5403
|
+
* @default null
|
|
5404
|
+
*/
|
|
5391
5405
|
Membership: components['schemas']['MembershipDto2'] | null;
|
|
5392
5406
|
/** @default null */
|
|
5393
5407
|
File: null | null;
|
|
@@ -5400,10 +5414,10 @@ export interface components {
|
|
|
5400
5414
|
*/
|
|
5401
5415
|
ShareType: 1 | 2 | 3 | 4;
|
|
5402
5416
|
/**
|
|
5403
|
-
* @description <p>1=Active, 3=Restored</p><details><summary>See values descriptions</summary><details><summary>See values descriptions</summary><table><tr><th>Value</th><th>Description</th></tr><tr><td>1</td><td>Active</td></tr><tr><td>2</td><td>Deleted</td></tr><tr><td>3</td><td>Restored</td></tr><tr><td>
|
|
5417
|
+
* @description <p>1=Active, 3=Restored</p><details><summary>See values descriptions</summary><details><summary>See values descriptions</summary><table><tr><th>Value</th><th>Description</th></tr><tr><td>1</td><td>Active</td></tr><tr><td>2</td><td>Deleted</td></tr><tr><td>3</td><td>Restored</td></tr><tr><td>5</td><td>Migrated</td></tr><tr><td>6</td><td>Locked</td></tr></table></details></details>
|
|
5404
5418
|
* @enum {integer}
|
|
5405
5419
|
*/
|
|
5406
|
-
ShareState: 1 | 2 | 3 |
|
|
5420
|
+
ShareState: 1 | 2 | 3 | 5 | 6;
|
|
5407
5421
|
/**
|
|
5408
5422
|
* @description <p>1=Regular, 2=Photo</p><details><summary>See values descriptions</summary><details><summary>See values descriptions</summary><table><tr><th>Value</th><th>Description</th></tr><tr><td>1</td><td>Regular</td></tr><tr><td>2</td><td>Photo</td></tr></table></details></details>
|
|
5409
5423
|
* @enum {integer}
|
|
@@ -5518,6 +5532,7 @@ export interface components {
|
|
|
5518
5532
|
*/
|
|
5519
5533
|
Token: string;
|
|
5520
5534
|
LinkType: components['schemas']['NodeType3'];
|
|
5535
|
+
VolumeID: components['schemas']['Id2'];
|
|
5521
5536
|
LinkID: components['schemas']['Id2'];
|
|
5522
5537
|
SharePasswordSalt: components['schemas']['BinaryString2'];
|
|
5523
5538
|
SharePassphrase: components['schemas']['PGPMessage2'];
|
|
@@ -6921,7 +6936,6 @@ export interface operations {
|
|
|
6921
6936
|
'application/json': {
|
|
6922
6937
|
/** @description Potential codes and their meaning:
|
|
6923
6938
|
* - 2500: A volume is already active
|
|
6924
|
-
* - 2500: Cannot create the new Photo volume. Should be migrated from current Photo stream
|
|
6925
6939
|
* - 2001: Invalid PGP message
|
|
6926
6940
|
* - 200501: Operation failed: Please retry
|
|
6927
6941
|
* - 200200: Address not found
|
|
@@ -8717,6 +8731,7 @@ export interface operations {
|
|
|
8717
8731
|
| {
|
|
8718
8732
|
/** @description Potential codes and their meaning:
|
|
8719
8733
|
* - 200003: Max file size limited to 100 MB on your plan. Please upgrade.
|
|
8734
|
+
* - 200303: Cannot commit related photo with main already in album
|
|
8720
8735
|
* */
|
|
8721
8736
|
Code: number;
|
|
8722
8737
|
}
|
|
@@ -8837,6 +8852,7 @@ export interface operations {
|
|
|
8837
8852
|
| {
|
|
8838
8853
|
/** @description Potential codes and their meaning:
|
|
8839
8854
|
* - 200003: Max file size limited to 100 MB on your plan. Please upgrade.
|
|
8855
|
+
* - 200303: Cannot commit related photo with main already in album
|
|
8840
8856
|
* */
|
|
8841
8857
|
Code: number;
|
|
8842
8858
|
}
|
|
@@ -10257,22 +10273,8 @@ export interface operations {
|
|
|
10257
10273
|
path?: never;
|
|
10258
10274
|
cookie?: never;
|
|
10259
10275
|
};
|
|
10260
|
-
requestBody?:
|
|
10261
|
-
content: {
|
|
10262
|
-
'application/json': components['schemas']['MigrateFromLegacyRequest'];
|
|
10263
|
-
};
|
|
10264
|
-
};
|
|
10276
|
+
requestBody?: never;
|
|
10265
10277
|
responses: {
|
|
10266
|
-
/** @description Accepted */
|
|
10267
|
-
202: {
|
|
10268
|
-
headers: {
|
|
10269
|
-
'x-pm-code': 1002;
|
|
10270
|
-
[name: string]: unknown;
|
|
10271
|
-
};
|
|
10272
|
-
content: {
|
|
10273
|
-
'application/json': components['schemas']['AcceptedResponse'];
|
|
10274
|
-
};
|
|
10275
|
-
};
|
|
10276
10278
|
/** @description Unprocessable Entity */
|
|
10277
10279
|
422: {
|
|
10278
10280
|
headers: {
|
|
@@ -10281,32 +10283,12 @@ export interface operations {
|
|
|
10281
10283
|
content: {
|
|
10282
10284
|
'application/json': {
|
|
10283
10285
|
/** @description Potential codes and their meaning:
|
|
10284
|
-
* - 2500: Migration in progress
|
|
10285
10286
|
* - 2501: Share not found
|
|
10286
|
-
* - 2501: Volume not found
|
|
10287
|
-
* - 2501: Address not found
|
|
10288
10287
|
* */
|
|
10289
10288
|
Code: number;
|
|
10290
10289
|
};
|
|
10291
10290
|
};
|
|
10292
10291
|
};
|
|
10293
|
-
/** @description Failed dependency */
|
|
10294
|
-
424: {
|
|
10295
|
-
headers: {
|
|
10296
|
-
[name: string]: unknown;
|
|
10297
|
-
};
|
|
10298
|
-
content: {
|
|
10299
|
-
'application/json': {
|
|
10300
|
-
/**
|
|
10301
|
-
* @description Potential codes:
|
|
10302
|
-
* - 2032
|
|
10303
|
-
*
|
|
10304
|
-
* @enum {integer}
|
|
10305
|
-
*/
|
|
10306
|
-
Code: 2032;
|
|
10307
|
-
};
|
|
10308
|
-
};
|
|
10309
|
-
};
|
|
10310
10292
|
};
|
|
10311
10293
|
};
|
|
10312
10294
|
'get_drive-volumes-{volumeID}-photos': {
|
|
@@ -10482,6 +10464,7 @@ export interface operations {
|
|
|
10482
10464
|
/** @description Potential codes and their meaning:
|
|
10483
10465
|
* - 2011: The current ShareURL does not have read+write permissions.
|
|
10484
10466
|
* - 200003: Max file size limited to 100 MB on your plan. Please upgrade.
|
|
10467
|
+
* - 200303: Cannot commit related photo with main already in album
|
|
10485
10468
|
* */
|
|
10486
10469
|
Code: number;
|
|
10487
10470
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbortError } from '../../errors';
|
|
1
2
|
import { apiErrorFactory } from './errors';
|
|
2
3
|
import * as errors from './errors';
|
|
3
4
|
import { ErrorCode } from './errorCodes';
|
|
@@ -17,6 +18,15 @@ function mockAPIResponseAndResult(options: {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
describe('apiErrorFactory should return', () => {
|
|
21
|
+
it('AbortError on aborted error', () => {
|
|
22
|
+
const abortError = new Error('AbortError');
|
|
23
|
+
abortError.name = 'AbortError';
|
|
24
|
+
|
|
25
|
+
const error = apiErrorFactory({ response: new Response(), error: abortError });
|
|
26
|
+
expect(error).toBeInstanceOf(AbortError);
|
|
27
|
+
expect(error.message).toBe('Request aborted');
|
|
28
|
+
});
|
|
29
|
+
|
|
20
30
|
it('generic APIHTTPError when there is no specifc body', () => {
|
|
21
31
|
const response = new Response('', { status: 404, statusText: 'Not found' });
|
|
22
32
|
const error = apiErrorFactory({ response });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c } from 'ttag';
|
|
2
2
|
|
|
3
|
-
import { ServerError, ValidationError } from '../../errors';
|
|
3
|
+
import { AbortError, ServerError, ValidationError } from '../../errors';
|
|
4
4
|
import { ErrorCode, HTTPErrorCode } from './errorCodes';
|
|
5
5
|
|
|
6
6
|
export function apiErrorFactory({
|
|
@@ -12,6 +12,10 @@ export function apiErrorFactory({
|
|
|
12
12
|
result?: unknown;
|
|
13
13
|
error?: unknown;
|
|
14
14
|
}): ServerError {
|
|
15
|
+
if (error && error instanceof Error && error.name === 'AbortError') {
|
|
16
|
+
return new AbortError(c('Error').t`Request aborted`);
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
// Backend responses with 404 both in the response and body code.
|
|
16
20
|
// In such a case we want to stick to APIHTTPError to be very clear
|
|
17
21
|
// it is not NotFoundAPIError.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbortError } from '../errors';
|
|
1
2
|
import { asyncIteratorMap } from './asyncIteratorMap';
|
|
2
3
|
|
|
3
4
|
// Helper function to create an async generator from array
|
|
@@ -147,4 +148,15 @@ describe('asyncIteratorMap', () => {
|
|
|
147
148
|
expect(maxConcurrentExecutions).toBe(concurrencyLimit);
|
|
148
149
|
expect(results).toEqual([2, 4, 6, 8, 10, 12, 14, 16]);
|
|
149
150
|
});
|
|
151
|
+
|
|
152
|
+
test('throws AbortError if signal is aborted', async () => {
|
|
153
|
+
const inputGen = createAsyncGenerator([1, 2, 3, 4, 5]);
|
|
154
|
+
const mapper = async (x: number) => x * 2;
|
|
155
|
+
|
|
156
|
+
const ac = new AbortController();
|
|
157
|
+
ac.abort();
|
|
158
|
+
|
|
159
|
+
const mappedGen = asyncIteratorMap(inputGen, mapper, 1, ac.signal);
|
|
160
|
+
await expect(collectResults(mappedGen)).rejects.toThrow(AbortError);
|
|
161
|
+
});
|
|
150
162
|
});
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { c } from 'ttag';
|
|
2
|
+
|
|
3
|
+
import { AbortError } from '../errors';
|
|
4
|
+
|
|
1
5
|
const DEFAULT_CONCURRENCY = 10;
|
|
2
6
|
|
|
3
7
|
/**
|
|
@@ -18,6 +22,7 @@ export async function* asyncIteratorMap<I, O>(
|
|
|
18
22
|
inputIterator: AsyncGenerator<I>,
|
|
19
23
|
mapper: (item: I) => Promise<O>,
|
|
20
24
|
concurrency: number = DEFAULT_CONCURRENCY,
|
|
25
|
+
signal?: AbortSignal,
|
|
21
26
|
): AsyncGenerator<O> {
|
|
22
27
|
let done = false;
|
|
23
28
|
|
|
@@ -50,6 +55,9 @@ export async function* asyncIteratorMap<I, O>(
|
|
|
50
55
|
};
|
|
51
56
|
|
|
52
57
|
while (!done || executing.size > 0 || results.length > 0) {
|
|
58
|
+
if (signal?.aborted) {
|
|
59
|
+
throw new AbortError(c('Error').t`Operation aborted`);
|
|
60
|
+
}
|
|
53
61
|
while (!done && executing.size < concurrency) {
|
|
54
62
|
await pump();
|
|
55
63
|
}
|