@protontech/drive-sdk 0.9.9 → 0.11.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 +19 -16
- package/dist/crypto/driveCrypto.js +23 -1
- package/dist/crypto/driveCrypto.js.map +1 -1
- package/dist/crypto/driveCrypto.test.js +2 -1
- package/dist/crypto/driveCrypto.test.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/diagnostic/telemetry.js +3 -0
- package/dist/diagnostic/telemetry.js.map +1 -1
- package/dist/interface/index.d.ts +4 -3
- package/dist/interface/index.js +3 -1
- package/dist/interface/index.js.map +1 -1
- package/dist/interface/nodes.d.ts +8 -0
- package/dist/interface/photos.d.ts +31 -2
- package/dist/interface/photos.js +14 -0
- package/dist/interface/photos.js.map +1 -1
- package/dist/interface/sharing.d.ts +2 -0
- package/dist/interface/telemetry.d.ts +13 -1
- package/dist/interface/telemetry.js.map +1 -1
- package/dist/interface/thumbnail.d.ts +2 -2
- package/dist/internal/apiService/apiService.d.ts +1 -1
- package/dist/internal/apiService/apiService.js +27 -14
- 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/apiService.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/nodesAccess.js +1 -1
- package/dist/internal/nodes/nodesAccess.js.map +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 +42 -0
- package/dist/internal/photos/addToAlbum.js +178 -0
- package/dist/internal/photos/addToAlbum.js.map +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/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/{albums.d.ts → albumsManager.d.ts} +6 -4
- package/dist/internal/photos/{albums.js → albumsManager.js} +17 -5
- package/dist/internal/photos/albumsManager.js.map +1 -0
- package/dist/internal/photos/albumsManager.test.d.ts +1 -0
- package/dist/internal/photos/{albums.test.js → albumsManager.test.js} +4 -3
- package/dist/internal/photos/albumsManager.test.js.map +1 -0
- package/dist/internal/photos/apiService.d.ts +22 -2
- package/dist/internal/photos/apiService.js +136 -5
- 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 +5 -3
- package/dist/internal/photos/index.js +5 -2
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/interface.d.ts +6 -15
- package/dist/internal/photos/interface.js +0 -14
- package/dist/internal/photos/interface.js.map +1 -1
- package/dist/internal/photos/nodes.js +33 -3
- 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/photosManager.d.ts +22 -0
- package/dist/internal/photos/photosManager.js +101 -0
- package/dist/internal/photos/photosManager.js.map +1 -0
- package/dist/internal/photos/photosManager.test.d.ts +1 -0
- package/dist/internal/photos/photosManager.test.js +222 -0
- package/dist/internal/photos/photosManager.test.js.map +1 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.d.ts +57 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.js +113 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.js.map +1 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.test.d.ts +1 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.test.js +289 -0
- package/dist/internal/photos/photosTransferPayloadBuilder.test.js.map +1 -0
- package/dist/internal/photos/upload.d.ts +2 -2
- package/dist/internal/photos/upload.js +1 -1
- 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 +3 -7
- package/dist/internal/upload/apiService.js +0 -4
- 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 +4 -4
- 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/upload/streamUploader.js +12 -13
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +28 -4
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/internal/utils.d.ts +1 -1
- package/dist/protonDriveClient.d.ts +8 -0
- package/dist/protonDriveClient.js +12 -1
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +35 -3
- package/dist/protonDrivePhotosClient.js +42 -2
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.js +1 -1
- 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.test.ts +2 -1
- package/src/crypto/driveCrypto.ts +50 -16
- package/src/crypto/hmac.ts +4 -4
- package/src/crypto/interface.ts +58 -27
- package/src/crypto/openPGPCrypto.ts +26 -26
- package/src/diagnostic/telemetry.ts +3 -0
- package/src/interface/index.ts +11 -2
- package/src/interface/nodes.ts +1 -0
- package/src/interface/photos.ts +33 -2
- package/src/interface/sharing.ts +2 -0
- package/src/interface/telemetry.ts +15 -1
- package/src/interface/thumbnail.ts +2 -2
- package/src/internal/apiService/apiService.test.ts +38 -6
- package/src/internal/apiService/apiService.ts +39 -14
- 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/apiService.ts +2 -2
- 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/nodesAccess.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 +234 -0
- package/src/internal/photos/albumsCrypto.ts +54 -3
- package/src/internal/photos/{albums.test.ts → albumsManager.test.ts} +22 -25
- package/src/internal/photos/{albums.ts → albumsManager.ts} +32 -3
- package/src/internal/photos/apiService.test.ts +233 -0
- package/src/internal/photos/apiService.ts +228 -26
- package/src/internal/photos/errors.ts +11 -0
- package/src/internal/photos/index.ts +6 -3
- package/src/internal/photos/interface.ts +8 -18
- package/src/internal/photos/nodes.test.ts +27 -6
- package/src/internal/photos/nodes.ts +35 -3
- package/src/internal/photos/photosManager.test.ts +266 -0
- package/src/internal/photos/photosManager.ts +144 -0
- package/src/internal/photos/photosTransferPayloadBuilder.test.ts +380 -0
- package/src/internal/photos/photosTransferPayloadBuilder.ts +203 -0
- package/src/internal/photos/upload.ts +8 -3
- 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 +10 -12
- 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 +11 -12
- package/src/internal/upload/interface.ts +6 -6
- package/src/internal/upload/manager.ts +2 -2
- package/src/internal/upload/streamUploader.test.ts +33 -4
- package/src/internal/upload/streamUploader.ts +13 -13
- package/src/protonDriveClient.ts +16 -4
- package/src/protonDrivePhotosClient.ts +73 -17
- package/src/protonDrivePublicLinkClient.ts +1 -1
- package/src/transformers.ts +2 -0
- package/dist/internal/photos/albums.js.map +0 -1
- package/dist/internal/photos/albums.test.js.map +0 -1
- /package/dist/internal/photos/{albums.test.d.ts → addToAlbum.test.d.ts} +0 -0
|
@@ -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
|
});
|
|
@@ -137,8 +137,12 @@ export class DriveAPIService {
|
|
|
137
137
|
return this.makeRequest(url, 'PUT', data, signal);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
async delete<
|
|
141
|
-
|
|
140
|
+
async delete<RequestPayload, ResponsePayload>(
|
|
141
|
+
url: string,
|
|
142
|
+
data?: RequestPayload,
|
|
143
|
+
signal?: AbortSignal,
|
|
144
|
+
): Promise<ResponsePayload> {
|
|
145
|
+
return this.makeRequest(url, 'DELETE', data, signal);
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
protected async makeRequest<RequestPayload, ResponsePayload>(
|
|
@@ -245,7 +249,15 @@ export class DriveAPIService {
|
|
|
245
249
|
private async fetch(
|
|
246
250
|
request: { method: string; url: string; signal?: AbortSignal },
|
|
247
251
|
callback: () => Promise<Response>,
|
|
248
|
-
|
|
252
|
+
{
|
|
253
|
+
attempt,
|
|
254
|
+
previousError,
|
|
255
|
+
}: {
|
|
256
|
+
attempt: number;
|
|
257
|
+
previousError?: unknown;
|
|
258
|
+
} = {
|
|
259
|
+
attempt: 0,
|
|
260
|
+
},
|
|
249
261
|
): Promise<Response> {
|
|
250
262
|
if (request.signal?.aborted) {
|
|
251
263
|
throw new AbortError(c('Error').t`Request aborted`);
|
|
@@ -282,19 +294,19 @@ export class DriveAPIService {
|
|
|
282
294
|
this.offlineErrorHappened();
|
|
283
295
|
this.logger.info(`${request.method} ${request.url}: Offline error, retrying`);
|
|
284
296
|
await waitSeconds(OFFLINE_RETRY_DELAY_SECONDS);
|
|
285
|
-
return this.fetch(request, callback, attempt + 1);
|
|
297
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
286
298
|
}
|
|
287
299
|
|
|
288
300
|
if (error.name === 'TimeoutError' && attempt + 1 < MAX_TIMEOUT_ERROR_RETRY_ATTEMPTS) {
|
|
289
301
|
this.logger.warn(`${request.method} ${request.url}: Timeout error, retrying`);
|
|
290
302
|
await waitSeconds(SERVER_ERROR_RETRY_DELAY_SECONDS);
|
|
291
|
-
return this.fetch(request, callback, attempt + 1);
|
|
303
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
292
304
|
}
|
|
293
305
|
}
|
|
294
306
|
if (attempt === 0) {
|
|
295
307
|
this.logger.error(`${request.method} ${request.url}: failed, retrying once`, error);
|
|
296
308
|
await waitSeconds(GENERAL_RETRY_DELAY_SECONDS);
|
|
297
|
-
return this.fetch(request, callback, attempt + 1);
|
|
309
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: error });
|
|
298
310
|
}
|
|
299
311
|
this.logger.error(`${request.method} ${request.url}: failed`, error);
|
|
300
312
|
throw error;
|
|
@@ -315,7 +327,7 @@ export class DriveAPIService {
|
|
|
315
327
|
this.tooManyRequestsErrorHappened();
|
|
316
328
|
const timeout = parseInt(response.headers.get('retry-after') || '0', 10) || DEFAULT_429_RETRY_DELAY_SECONDS;
|
|
317
329
|
await waitSeconds(timeout);
|
|
318
|
-
return this.fetch(request, callback, attempt + 1);
|
|
330
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: response.status });
|
|
319
331
|
} else {
|
|
320
332
|
this.clearSubsequentTooManyRequestsError();
|
|
321
333
|
}
|
|
@@ -329,16 +341,29 @@ export class DriveAPIService {
|
|
|
329
341
|
this.logger.warn(`${request.method} ${request.url}: ${response.status} - retry failed`);
|
|
330
342
|
} else {
|
|
331
343
|
await waitSeconds(SERVER_ERROR_RETRY_DELAY_SECONDS);
|
|
332
|
-
return this.fetch(request, callback, attempt + 1);
|
|
344
|
+
return this.fetch(request, callback, { attempt: attempt + 1, previousError: response.status });
|
|
333
345
|
}
|
|
334
346
|
} else {
|
|
335
347
|
if (attempt > 0) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
348
|
+
const previousErrorMessage =
|
|
349
|
+
previousError instanceof Error ? previousError.message : String(previousError);
|
|
350
|
+
const isWarning =
|
|
351
|
+
!(previousError instanceof Error) ||
|
|
352
|
+
(previousError instanceof Error &&
|
|
353
|
+
previousError.name !== 'TimeoutError' &&
|
|
354
|
+
previousError.name !== 'OfflineError');
|
|
355
|
+
|
|
356
|
+
if (isWarning) {
|
|
357
|
+
this.telemetry.recordMetric({
|
|
358
|
+
eventName: 'apiRetrySucceeded',
|
|
359
|
+
failedAttempts: attempt,
|
|
360
|
+
url: request.url,
|
|
361
|
+
previousError,
|
|
362
|
+
});
|
|
363
|
+
this.logger.warn(`${request.method} ${request.url}: ${previousErrorMessage} - retry helped`);
|
|
364
|
+
} else {
|
|
365
|
+
this.logger.debug(`${request.method} ${request.url}: ${previousErrorMessage} - retry helped`);
|
|
366
|
+
}
|
|
342
367
|
}
|
|
343
368
|
this.clearSubsequentServerErrors();
|
|
344
369
|
}
|