@protontech/drive-sdk 0.9.8 → 0.10.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/crypto/driveCrypto.d.ts +15 -15
- package/dist/crypto/driveCrypto.js.map +1 -1
- package/dist/crypto/hmac.d.ts +3 -3
- package/dist/crypto/hmac.js.map +1 -1
- package/dist/crypto/interface.d.ts +45 -25
- package/dist/crypto/interface.js.map +1 -1
- package/dist/crypto/openPGPCrypto.d.ts +37 -37
- package/dist/crypto/openPGPCrypto.js.map +1 -1
- package/dist/crypto/utils.d.ts +1 -1
- package/dist/interface/index.d.ts +3 -3
- package/dist/interface/index.js.map +1 -1
- package/dist/interface/nodes.d.ts +8 -0
- package/dist/interface/photos.d.ts +18 -1
- package/dist/interface/sharing.d.ts +2 -0
- package/dist/interface/telemetry.d.ts +1 -0
- package/dist/interface/telemetry.js.map +1 -1
- package/dist/interface/thumbnail.d.ts +2 -2
- package/dist/internal/apiService/apiService.js +25 -12
- package/dist/internal/apiService/apiService.js.map +1 -1
- package/dist/internal/apiService/apiService.test.js +33 -5
- package/dist/internal/apiService/apiService.test.js.map +1 -1
- package/dist/internal/apiService/driveTypes.d.ts +2942 -3187
- package/dist/internal/apiService/errors.test.js +17 -7
- package/dist/internal/apiService/errors.test.js.map +1 -1
- package/dist/internal/devices/manager.d.ts +1 -0
- package/dist/internal/devices/manager.js +11 -0
- package/dist/internal/devices/manager.js.map +1 -1
- package/dist/internal/download/apiService.d.ts +1 -1
- package/dist/internal/download/cryptoService.d.ts +4 -4
- package/dist/internal/download/cryptoService.js.map +1 -1
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/download/fileDownloader.test.js.map +1 -1
- package/dist/internal/download/thumbnailDownloader.js.map +1 -1
- package/dist/internal/nodes/cryptoService.d.ts +4 -4
- package/dist/internal/nodes/cryptoService.js +5 -3
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/cryptoService.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +1 -1
- package/dist/internal/nodes/nodesManagement.js +0 -1
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/photos/addToAlbum.d.ts +46 -0
- package/dist/internal/photos/addToAlbum.js +257 -0
- package/dist/internal/photos/addToAlbum.js.map +1 -0
- package/dist/internal/photos/addToAlbum.test.d.ts +1 -0
- package/dist/internal/photos/addToAlbum.test.js +409 -0
- package/dist/internal/photos/addToAlbum.test.js.map +1 -0
- package/dist/internal/photos/albums.d.ts +7 -2
- package/dist/internal/photos/albums.js +24 -1
- package/dist/internal/photos/albums.js.map +1 -1
- package/dist/internal/photos/albums.test.js +26 -1
- package/dist/internal/photos/albums.test.js.map +1 -1
- package/dist/internal/photos/albumsCrypto.d.ts +20 -3
- package/dist/internal/photos/albumsCrypto.js +27 -0
- package/dist/internal/photos/albumsCrypto.js.map +1 -1
- package/dist/internal/photos/apiService.d.ts +20 -0
- package/dist/internal/photos/apiService.js +142 -0
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/apiService.test.d.ts +1 -0
- package/dist/internal/photos/apiService.test.js +199 -0
- package/dist/internal/photos/apiService.test.js.map +1 -0
- package/dist/internal/photos/errors.d.ts +4 -0
- package/dist/internal/photos/errors.js +17 -0
- package/dist/internal/photos/errors.js.map +1 -0
- package/dist/internal/photos/index.d.ts +1 -1
- package/dist/internal/photos/index.js +1 -1
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/interface.d.ts +36 -1
- package/dist/internal/photos/interface.js +14 -0
- package/dist/internal/photos/interface.js.map +1 -1
- package/dist/internal/photos/nodes.js +32 -2
- package/dist/internal/photos/nodes.js.map +1 -1
- package/dist/internal/photos/nodes.test.js +25 -5
- package/dist/internal/photos/nodes.test.js.map +1 -1
- package/dist/internal/photos/timeline.d.ts +2 -5
- package/dist/internal/photos/timeline.js.map +1 -1
- package/dist/internal/photos/upload.d.ts +2 -2
- package/dist/internal/photos/upload.js.map +1 -1
- package/dist/internal/shares/apiService.js +1 -0
- package/dist/internal/shares/apiService.js.map +1 -1
- package/dist/internal/shares/interface.d.ts +1 -0
- package/dist/internal/sharing/apiService.d.ts +8 -1
- package/dist/internal/sharing/apiService.js +23 -1
- package/dist/internal/sharing/apiService.js.map +1 -1
- package/dist/internal/sharing/cryptoService.js +8 -4
- package/dist/internal/sharing/cryptoService.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.d.ts +1 -0
- package/dist/internal/sharing/sharingManagement.js +15 -2
- package/dist/internal/sharing/sharingManagement.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.test.js +30 -5
- package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
- package/dist/internal/sharingPublic/nodes.d.ts +2 -2
- package/dist/internal/sharingPublic/nodes.js.map +1 -1
- package/dist/internal/upload/apiService.d.ts +5 -5
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/blockVerifier.d.ts +2 -2
- package/dist/internal/upload/blockVerifier.js.map +1 -1
- package/dist/internal/upload/chunkStreamReader.d.ts +2 -2
- package/dist/internal/upload/chunkStreamReader.js.map +1 -1
- package/dist/internal/upload/chunkStreamReader.test.js.map +1 -1
- package/dist/internal/upload/cryptoService.d.ts +7 -7
- package/dist/internal/upload/cryptoService.js.map +1 -1
- package/dist/internal/upload/interface.d.ts +6 -6
- package/dist/internal/upload/manager.d.ts +1 -1
- package/dist/internal/upload/manager.js.map +1 -1
- package/dist/internal/upload/streamUploader.d.ts +1 -1
- package/dist/internal/utils.d.ts +1 -1
- package/dist/protonDriveClient.d.ts +8 -0
- package/dist/protonDriveClient.js +11 -0
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +42 -7
- package/dist/protonDrivePhotosClient.js +50 -2
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +9 -0
- package/dist/protonDrivePublicLinkClient.js +12 -0
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/dist/transformers.js +2 -0
- package/dist/transformers.js.map +1 -1
- package/package.json +4 -4
- package/src/crypto/driveCrypto.ts +15 -15
- package/src/crypto/hmac.ts +4 -4
- package/src/crypto/interface.ts +58 -27
- package/src/crypto/openPGPCrypto.ts +26 -26
- package/src/interface/index.ts +10 -2
- package/src/interface/nodes.ts +1 -0
- package/src/interface/photos.ts +19 -1
- package/src/interface/sharing.ts +2 -0
- package/src/interface/telemetry.ts +1 -0
- package/src/interface/thumbnail.ts +2 -2
- package/src/internal/apiService/apiService.test.ts +38 -6
- package/src/internal/apiService/apiService.ts +33 -12
- package/src/internal/apiService/driveTypes.ts +2942 -3187
- package/src/internal/devices/manager.ts +14 -0
- package/src/internal/download/apiService.ts +1 -1
- package/src/internal/download/cryptoService.ts +4 -4
- package/src/internal/download/fileDownloader.test.ts +4 -4
- package/src/internal/download/fileDownloader.ts +6 -6
- package/src/internal/download/thumbnailDownloader.ts +4 -4
- package/src/internal/nodes/cryptoService.test.ts +2 -2
- package/src/internal/nodes/cryptoService.ts +11 -8
- package/src/internal/nodes/interface.ts +1 -1
- package/src/internal/nodes/nodesManagement.ts +0 -1
- package/src/internal/photos/addToAlbum.test.ts +515 -0
- package/src/internal/photos/addToAlbum.ts +341 -0
- package/src/internal/photos/albums.test.ts +46 -22
- package/src/internal/photos/albums.ts +48 -2
- package/src/internal/photos/albumsCrypto.ts +54 -3
- package/src/internal/photos/apiService.test.ts +233 -0
- package/src/internal/photos/apiService.ts +234 -15
- package/src/internal/photos/errors.ts +11 -0
- package/src/internal/photos/index.ts +2 -2
- package/src/internal/photos/interface.ts +40 -1
- package/src/internal/photos/nodes.test.ts +27 -6
- package/src/internal/photos/nodes.ts +34 -2
- package/src/internal/photos/timeline.ts +2 -5
- package/src/internal/photos/upload.ts +2 -2
- package/src/internal/shares/apiService.ts +1 -0
- package/src/internal/shares/interface.ts +1 -0
- package/src/internal/sharing/apiService.ts +49 -5
- package/src/internal/sharing/cryptoService.ts +10 -4
- package/src/internal/sharing/sharingManagement.test.ts +33 -5
- package/src/internal/sharing/sharingManagement.ts +28 -6
- package/src/internal/sharingPublic/nodes.ts +1 -1
- package/src/internal/upload/apiService.ts +5 -5
- package/src/internal/upload/blockVerifier.ts +3 -3
- package/src/internal/upload/chunkStreamReader.test.ts +7 -7
- package/src/internal/upload/chunkStreamReader.ts +3 -3
- package/src/internal/upload/cryptoService.ts +9 -9
- package/src/internal/upload/interface.ts +6 -6
- package/src/internal/upload/manager.ts +2 -2
- package/src/internal/upload/streamUploader.ts +1 -1
- package/src/protonDriveClient.ts +15 -3
- package/src/protonDrivePhotosClient.ts +78 -22
- package/src/protonDrivePublicLinkClient.ts +13 -0
- package/src/transformers.ts +2 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type Thumbnail = {
|
|
2
2
|
type: ThumbnailType;
|
|
3
|
-
thumbnail: Uint8Array
|
|
3
|
+
thumbnail: Uint8Array<ArrayBuffer>;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
6
|
export enum ThumbnailType {
|
|
@@ -9,5 +9,5 @@ export enum ThumbnailType {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export type ThumbnailResult =
|
|
12
|
-
| { nodeUid: string; ok: true; thumbnail: Uint8Array }
|
|
12
|
+
| { nodeUid: string; ok: true; thumbnail: Uint8Array<ArrayBuffer> }
|
|
13
13
|
| { nodeUid: string; ok: false; error: string };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbortError } from '../../errors';
|
|
2
|
-
import { ProtonDriveHTTPClient, SDKEvent } from '../../interface';
|
|
2
|
+
import { ProtonDriveHTTPClient, SDKEvent, Telemetry, MetricEvent } from '../../interface';
|
|
3
3
|
import { getMockTelemetry } from '../../tests/telemetry';
|
|
4
4
|
import { SDKEvents } from '../sdkEvents';
|
|
5
5
|
import { DriveAPIService } from './apiService';
|
|
@@ -12,13 +12,17 @@ function generateOkResponse() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
describe('DriveAPIService', () => {
|
|
15
|
+
let telemetry: Telemetry<MetricEvent>;
|
|
15
16
|
let sdkEvents: SDKEvents;
|
|
16
17
|
let httpClient: ProtonDriveHTTPClient;
|
|
17
18
|
let api: DriveAPIService;
|
|
18
19
|
|
|
20
|
+
const baseUrl = 'https://drive.proton.me';
|
|
21
|
+
|
|
19
22
|
beforeEach(() => {
|
|
20
23
|
void jest.runAllTimersAsync();
|
|
21
24
|
|
|
25
|
+
telemetry = getMockTelemetry();
|
|
22
26
|
// @ts-expect-error: No need to implement all methods for mocking
|
|
23
27
|
sdkEvents = {
|
|
24
28
|
transfersPaused: jest.fn(),
|
|
@@ -30,7 +34,7 @@ describe('DriveAPIService', () => {
|
|
|
30
34
|
fetchJson: jest.fn(() => Promise.resolve(generateOkResponse())),
|
|
31
35
|
fetchBlob: jest.fn(() => Promise.resolve(new Response(new Uint8Array([1, 2, 3])))),
|
|
32
36
|
};
|
|
33
|
-
api = new DriveAPIService(
|
|
37
|
+
api = new DriveAPIService(telemetry, sdkEvents, httpClient, baseUrl, 'en');
|
|
34
38
|
});
|
|
35
39
|
|
|
36
40
|
function expectSDKEvents(...events: SDKEvent[]) {
|
|
@@ -42,6 +46,15 @@ describe('DriveAPIService', () => {
|
|
|
42
46
|
);
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
function expectMetricEvent(previousError: unknown, failedAttempts: number) {
|
|
50
|
+
expect(telemetry.recordMetric).toHaveBeenCalledWith({
|
|
51
|
+
eventName: 'apiRetrySucceeded',
|
|
52
|
+
failedAttempts,
|
|
53
|
+
url: `${baseUrl}/test`,
|
|
54
|
+
previousError,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
describe('should make', () => {
|
|
46
59
|
it('GET request', async () => {
|
|
47
60
|
const result = await api.get('test');
|
|
@@ -78,6 +91,7 @@ describe('DriveAPIService', () => {
|
|
|
78
91
|
);
|
|
79
92
|
expect(await request.json).toEqual(data);
|
|
80
93
|
expectSDKEvents();
|
|
94
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
81
95
|
}
|
|
82
96
|
|
|
83
97
|
it('storage GET request', async () => {
|
|
@@ -109,6 +123,7 @@ describe('DriveAPIService', () => {
|
|
|
109
123
|
);
|
|
110
124
|
expect(request.body).toEqual(data);
|
|
111
125
|
expectSDKEvents();
|
|
126
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
112
127
|
}
|
|
113
128
|
});
|
|
114
129
|
|
|
@@ -121,6 +136,7 @@ describe('DriveAPIService', () => {
|
|
|
121
136
|
|
|
122
137
|
await expect(api.get('test')).rejects.toThrow(new AbortError('Request aborted'));
|
|
123
138
|
expectSDKEvents();
|
|
139
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
124
140
|
});
|
|
125
141
|
|
|
126
142
|
it('APIHTTPError on 4xx response without JSON body', async () => {
|
|
@@ -129,6 +145,7 @@ describe('DriveAPIService', () => {
|
|
|
129
145
|
);
|
|
130
146
|
await expect(api.get('test')).rejects.toThrow(new Error('Not found'));
|
|
131
147
|
expectSDKEvents();
|
|
148
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
132
149
|
});
|
|
133
150
|
|
|
134
151
|
it('APIError on 4xx response with JSON body', async () => {
|
|
@@ -137,6 +154,7 @@ describe('DriveAPIService', () => {
|
|
|
137
154
|
);
|
|
138
155
|
await expect(api.get('test')).rejects.toThrow('General error');
|
|
139
156
|
expectSDKEvents();
|
|
157
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
140
158
|
});
|
|
141
159
|
});
|
|
142
160
|
|
|
@@ -155,6 +173,7 @@ describe('DriveAPIService', () => {
|
|
|
155
173
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
156
174
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(3);
|
|
157
175
|
expectSDKEvents();
|
|
176
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
158
177
|
});
|
|
159
178
|
|
|
160
179
|
it('on timeout error', async () => {
|
|
@@ -171,19 +190,19 @@ describe('DriveAPIService', () => {
|
|
|
171
190
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
172
191
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(3);
|
|
173
192
|
expectSDKEvents();
|
|
193
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
174
194
|
});
|
|
175
195
|
|
|
176
196
|
it('on general error', async () => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
.mockRejectedValueOnce(new Error('Error'))
|
|
180
|
-
.mockResolvedValueOnce(generateOkResponse());
|
|
197
|
+
const error = new Error('Error');
|
|
198
|
+
httpClient.fetchJson = jest.fn().mockRejectedValueOnce(error).mockResolvedValueOnce(generateOkResponse());
|
|
181
199
|
|
|
182
200
|
const result = api.get('test');
|
|
183
201
|
|
|
184
202
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
185
203
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(2);
|
|
186
204
|
expectSDKEvents();
|
|
205
|
+
expectMetricEvent(error, 1);
|
|
187
206
|
});
|
|
188
207
|
|
|
189
208
|
it('only once on general error', async () => {
|
|
@@ -198,6 +217,7 @@ describe('DriveAPIService', () => {
|
|
|
198
217
|
await expect(result).rejects.toThrow('Second error');
|
|
199
218
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(2);
|
|
200
219
|
expectSDKEvents();
|
|
220
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
201
221
|
});
|
|
202
222
|
|
|
203
223
|
it('on 429 response with default timeout', async () => {
|
|
@@ -231,6 +251,7 @@ describe('DriveAPIService', () => {
|
|
|
231
251
|
|
|
232
252
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
233
253
|
expectSDKEvents();
|
|
254
|
+
expectMetricEvent(429, 2);
|
|
234
255
|
});
|
|
235
256
|
|
|
236
257
|
it('on 429 response with retry-after header', async () => {
|
|
@@ -272,6 +293,7 @@ describe('DriveAPIService', () => {
|
|
|
272
293
|
|
|
273
294
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
274
295
|
expectSDKEvents();
|
|
296
|
+
expectMetricEvent(429, 2);
|
|
275
297
|
});
|
|
276
298
|
|
|
277
299
|
it('on 5xx response', async () => {
|
|
@@ -287,6 +309,7 @@ describe('DriveAPIService', () => {
|
|
|
287
309
|
await expect(result).resolves.toEqual({ Code: ErrorCode.OK });
|
|
288
310
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(2);
|
|
289
311
|
expectSDKEvents();
|
|
312
|
+
expectMetricEvent(500, 1);
|
|
290
313
|
});
|
|
291
314
|
|
|
292
315
|
it('only once on 5xx response', async () => {
|
|
@@ -301,6 +324,7 @@ describe('DriveAPIService', () => {
|
|
|
301
324
|
await expect(result).rejects.toThrow('Some error');
|
|
302
325
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(2);
|
|
303
326
|
expectSDKEvents();
|
|
327
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
304
328
|
});
|
|
305
329
|
});
|
|
306
330
|
|
|
@@ -314,6 +338,7 @@ describe('DriveAPIService', () => {
|
|
|
314
338
|
await expect(api.get('test')).rejects.toThrow(error);
|
|
315
339
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(3);
|
|
316
340
|
expectSDKEvents();
|
|
341
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
317
342
|
});
|
|
318
343
|
|
|
319
344
|
it('limit 429 errors', async () => {
|
|
@@ -336,6 +361,8 @@ describe('DriveAPIService', () => {
|
|
|
336
361
|
httpClient.fetchJson = jest.fn().mockResolvedValue(generateOkResponse());
|
|
337
362
|
await api.get('test');
|
|
338
363
|
expect(sdkEvents.requestsThrottled).toHaveBeenCalledTimes(1);
|
|
364
|
+
|
|
365
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
339
366
|
});
|
|
340
367
|
|
|
341
368
|
it('do not limit 429s when some pass', async () => {
|
|
@@ -355,6 +382,7 @@ describe('DriveAPIService', () => {
|
|
|
355
382
|
// 20 calls * 5 retries till OK response + 1 last successful call
|
|
356
383
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(101);
|
|
357
384
|
expectSDKEvents();
|
|
385
|
+
expectMetricEvent(429, 4);
|
|
358
386
|
});
|
|
359
387
|
|
|
360
388
|
it('limit server errors', async () => {
|
|
@@ -371,6 +399,7 @@ describe('DriveAPIService', () => {
|
|
|
371
399
|
await expect(api.get('test')).rejects.toThrow('Too many server errors, please try again later');
|
|
372
400
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(10);
|
|
373
401
|
expectSDKEvents();
|
|
402
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
374
403
|
});
|
|
375
404
|
|
|
376
405
|
it('do not limit server errors when some pass', async () => {
|
|
@@ -390,6 +419,7 @@ describe('DriveAPIService', () => {
|
|
|
390
419
|
// 15 erroring calls * 2 attempts + 5 successful calls
|
|
391
420
|
expect(httpClient.fetchJson).toHaveBeenCalledTimes(35);
|
|
392
421
|
expectSDKEvents();
|
|
422
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
393
423
|
});
|
|
394
424
|
|
|
395
425
|
it('notify about offline error', async () => {
|
|
@@ -428,6 +458,8 @@ describe('DriveAPIService', () => {
|
|
|
428
458
|
expectSDKEvents(SDKEvent.TransfersPaused, SDKEvent.TransfersResumed);
|
|
429
459
|
|
|
430
460
|
await promise;
|
|
461
|
+
|
|
462
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
431
463
|
});
|
|
432
464
|
});
|
|
433
465
|
});
|
|
@@ -245,7 +245,15 @@ export class DriveAPIService {
|
|
|
245
245
|
private async fetch(
|
|
246
246
|
request: { method: string; url: string; signal?: AbortSignal },
|
|
247
247
|
callback: () => Promise<Response>,
|
|
248
|
-
|
|
248
|
+
{
|
|
249
|
+
attempt,
|
|
250
|
+
previousError,
|
|
251
|
+
}: {
|
|
252
|
+
attempt: number;
|
|
253
|
+
previousError?: unknown;
|
|
254
|
+
} = {
|
|
255
|
+
attempt: 0,
|
|
256
|
+
},
|
|
249
257
|
): Promise<Response> {
|
|
250
258
|
if (request.signal?.aborted) {
|
|
251
259
|
throw new AbortError(c('Error').t`Request aborted`);
|
|
@@ -282,19 +290,19 @@ export class DriveAPIService {
|
|
|
282
290
|
this.offlineErrorHappened();
|
|
283
291
|
this.logger.info(`${request.method} ${request.url}: Offline error, retrying`);
|
|
284
292
|
await waitSeconds(OFFLINE_RETRY_DELAY_SECONDS);
|
|
285
|
-
return this.fetch(request, callback, attempt + 1);
|
|
293
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
286
294
|
}
|
|
287
295
|
|
|
288
296
|
if (error.name === 'TimeoutError' && attempt + 1 < MAX_TIMEOUT_ERROR_RETRY_ATTEMPTS) {
|
|
289
297
|
this.logger.warn(`${request.method} ${request.url}: Timeout error, retrying`);
|
|
290
298
|
await waitSeconds(SERVER_ERROR_RETRY_DELAY_SECONDS);
|
|
291
|
-
return this.fetch(request, callback, attempt + 1);
|
|
299
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
302
|
if (attempt === 0) {
|
|
295
303
|
this.logger.error(`${request.method} ${request.url}: failed, retrying once`, error);
|
|
296
304
|
await waitSeconds(GENERAL_RETRY_DELAY_SECONDS);
|
|
297
|
-
return this.fetch(request, callback, attempt + 1);
|
|
305
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
298
306
|
}
|
|
299
307
|
this.logger.error(`${request.method} ${request.url}: failed`, error);
|
|
300
308
|
throw error;
|
|
@@ -315,7 +323,7 @@ export class DriveAPIService {
|
|
|
315
323
|
this.tooManyRequestsErrorHappened();
|
|
316
324
|
const timeout = parseInt(response.headers.get('retry-after') || '0', 10) || DEFAULT_429_RETRY_DELAY_SECONDS;
|
|
317
325
|
await waitSeconds(timeout);
|
|
318
|
-
return this.fetch(request, callback, attempt + 1);
|
|
326
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: response.status });
|
|
319
327
|
} else {
|
|
320
328
|
this.clearSubsequentTooManyRequestsError();
|
|
321
329
|
}
|
|
@@ -329,16 +337,29 @@ export class DriveAPIService {
|
|
|
329
337
|
this.logger.warn(`${request.method} ${request.url}: ${response.status} - retry failed`);
|
|
330
338
|
} else {
|
|
331
339
|
await waitSeconds(SERVER_ERROR_RETRY_DELAY_SECONDS);
|
|
332
|
-
return this.fetch(request, callback, attempt + 1);
|
|
340
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: response.status });
|
|
333
341
|
}
|
|
334
342
|
} else {
|
|
335
343
|
if (attempt > 0) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
344
|
+
const previousErrorMessage =
|
|
345
|
+
previousError instanceof Error ? previousError.message : String(previousError);
|
|
346
|
+
const isWarning =
|
|
347
|
+
!(previousError instanceof Error) ||
|
|
348
|
+
(previousError instanceof Error &&
|
|
349
|
+
previousError.name !== 'TimeoutError' &&
|
|
350
|
+
previousError.name !== 'OfflineError');
|
|
351
|
+
|
|
352
|
+
if (isWarning) {
|
|
353
|
+
this.telemetry.recordMetric({
|
|
354
|
+
eventName: 'apiRetrySucceeded',
|
|
355
|
+
failedAttempts: attempt,
|
|
356
|
+
url: request.url,
|
|
357
|
+
previousError,
|
|
358
|
+
});
|
|
359
|
+
this.logger.warn(`${request.method} ${request.url}: ${previousErrorMessage} - retry helped`);
|
|
360
|
+
} else {
|
|
361
|
+
this.logger.debug(`${request.method} ${request.url}: ${previousErrorMessage} - retry helped`);
|
|
362
|
+
}
|
|
342
363
|
}
|
|
343
364
|
this.clearSubsequentServerErrors();
|
|
344
365
|
}
|