@unito/integration-sdk 1.3.0 → 1.4.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/src/index.cjs
CHANGED
|
@@ -1236,22 +1236,22 @@ class Provider {
|
|
|
1236
1236
|
try {
|
|
1237
1237
|
const body = JSON.parse(responseBody);
|
|
1238
1238
|
if (body.error) {
|
|
1239
|
-
reject(this.handleError(400, body.error.message));
|
|
1239
|
+
reject(this.handleError(400, body.error.message, options));
|
|
1240
1240
|
}
|
|
1241
1241
|
resolve({ status: 201, headers: response.headers, body: body });
|
|
1242
1242
|
}
|
|
1243
1243
|
catch (error) {
|
|
1244
|
-
reject(this.handleError(500, `Failed to parse response body: "${error}"
|
|
1244
|
+
reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
1245
1245
|
}
|
|
1246
1246
|
});
|
|
1247
1247
|
});
|
|
1248
1248
|
request.on('error', error => {
|
|
1249
|
-
reject(this.handleError(400, `Error while calling the provider: "${error}"
|
|
1249
|
+
reject(this.handleError(400, `Error while calling the provider: "${error}"`, options));
|
|
1250
1250
|
});
|
|
1251
1251
|
form.pipe(request);
|
|
1252
1252
|
}
|
|
1253
1253
|
catch (error) {
|
|
1254
|
-
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"
|
|
1254
|
+
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"`, options));
|
|
1255
1255
|
}
|
|
1256
1256
|
});
|
|
1257
1257
|
};
|
|
@@ -1361,17 +1361,17 @@ class Provider {
|
|
|
1361
1361
|
if (error instanceof Error) {
|
|
1362
1362
|
switch (error.name) {
|
|
1363
1363
|
case 'AbortError':
|
|
1364
|
-
throw this.handleError(408, 'Request aborted');
|
|
1364
|
+
throw this.handleError(408, 'Request aborted', options);
|
|
1365
1365
|
case 'TimeoutError':
|
|
1366
|
-
throw this.handleError(408, 'Request timeout');
|
|
1366
|
+
throw this.handleError(408, 'Request timeout', options);
|
|
1367
1367
|
}
|
|
1368
|
-
throw this.handleError(500, `Unexpected error while calling the provider: name: "${error.name}" \n message: "${error.message}" \n stack: ${error.stack}
|
|
1368
|
+
throw this.handleError(500, `Unexpected error while calling the provider: name: "${error.name}" \n message: "${error.message}" \n stack: ${error.stack}`, options);
|
|
1369
1369
|
}
|
|
1370
|
-
throw this.handleError(500, 'Unexpected error while calling the provider - this is not normal, investigate');
|
|
1370
|
+
throw this.handleError(500, 'Unexpected error while calling the provider - this is not normal, investigate', options);
|
|
1371
1371
|
}
|
|
1372
1372
|
if (response.status >= 400) {
|
|
1373
1373
|
const textResult = await response.text();
|
|
1374
|
-
throw this.handleError(response.status, textResult);
|
|
1374
|
+
throw this.handleError(response.status, textResult, options);
|
|
1375
1375
|
}
|
|
1376
1376
|
const responseContentType = response.headers.get('content-type');
|
|
1377
1377
|
let body;
|
|
@@ -1382,13 +1382,13 @@ class Provider {
|
|
|
1382
1382
|
if (responseContentType && !responseContentType.includes('application/json')) {
|
|
1383
1383
|
const textResult = await response.text();
|
|
1384
1384
|
throw this.handleError(500, `Unsupported content-type, expected 'application/json' but got '${responseContentType}'.
|
|
1385
|
-
Original response (${response.status}): ${textResult}
|
|
1385
|
+
Original response (${response.status}): ${textResult}`, options);
|
|
1386
1386
|
}
|
|
1387
1387
|
try {
|
|
1388
1388
|
body = response.body ? await response.json() : undefined;
|
|
1389
1389
|
}
|
|
1390
1390
|
catch (err) {
|
|
1391
|
-
throw this.handleError(500, `Invalid JSON response
|
|
1391
|
+
throw this.handleError(500, `Invalid JSON response`, options);
|
|
1392
1392
|
}
|
|
1393
1393
|
}
|
|
1394
1394
|
else if (headers.Accept == 'application/octet-stream') {
|
|
@@ -1400,14 +1400,14 @@ class Provider {
|
|
|
1400
1400
|
body = (await response.text());
|
|
1401
1401
|
}
|
|
1402
1402
|
else {
|
|
1403
|
-
throw this.handleError(500, 'Unsupported Content-Type');
|
|
1403
|
+
throw this.handleError(500, 'Unsupported Content-Type', options);
|
|
1404
1404
|
}
|
|
1405
1405
|
return { status: response.status, headers: response.headers, body };
|
|
1406
1406
|
};
|
|
1407
1407
|
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
1408
1408
|
}
|
|
1409
|
-
handleError(responseStatus, message) {
|
|
1410
|
-
const customError = this.customErrorHandler?.(responseStatus, message);
|
|
1409
|
+
handleError(responseStatus, message, options) {
|
|
1410
|
+
const customError = this.customErrorHandler?.(responseStatus, message, options);
|
|
1411
1411
|
return customError ?? buildHttpError(responseStatus, message);
|
|
1412
1412
|
}
|
|
1413
1413
|
}
|
|
@@ -95,7 +95,10 @@ export declare class Provider {
|
|
|
95
95
|
*
|
|
96
96
|
* @see buildHttpError for the list of standard errors the SDK can handle.
|
|
97
97
|
*/
|
|
98
|
-
protected customErrorHandler: ((responseStatus: number, message: string
|
|
98
|
+
protected customErrorHandler: ((responseStatus: number, message: string, options: {
|
|
99
|
+
credentials: Credentials;
|
|
100
|
+
logger: Logger;
|
|
101
|
+
}) => HttpErrors.HttpError | undefined) | undefined;
|
|
99
102
|
/**
|
|
100
103
|
* Initializes a Provider with the given options.
|
|
101
104
|
*
|
|
@@ -137,22 +137,22 @@ export class Provider {
|
|
|
137
137
|
try {
|
|
138
138
|
const body = JSON.parse(responseBody);
|
|
139
139
|
if (body.error) {
|
|
140
|
-
reject(this.handleError(400, body.error.message));
|
|
140
|
+
reject(this.handleError(400, body.error.message, options));
|
|
141
141
|
}
|
|
142
142
|
resolve({ status: 201, headers: response.headers, body: body });
|
|
143
143
|
}
|
|
144
144
|
catch (error) {
|
|
145
|
-
reject(this.handleError(500, `Failed to parse response body: "${error}"
|
|
145
|
+
reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
146
146
|
}
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
149
|
request.on('error', error => {
|
|
150
|
-
reject(this.handleError(400, `Error while calling the provider: "${error}"
|
|
150
|
+
reject(this.handleError(400, `Error while calling the provider: "${error}"`, options));
|
|
151
151
|
});
|
|
152
152
|
form.pipe(request);
|
|
153
153
|
}
|
|
154
154
|
catch (error) {
|
|
155
|
-
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"
|
|
155
|
+
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"`, options));
|
|
156
156
|
}
|
|
157
157
|
});
|
|
158
158
|
};
|
|
@@ -262,17 +262,17 @@ export class Provider {
|
|
|
262
262
|
if (error instanceof Error) {
|
|
263
263
|
switch (error.name) {
|
|
264
264
|
case 'AbortError':
|
|
265
|
-
throw this.handleError(408, 'Request aborted');
|
|
265
|
+
throw this.handleError(408, 'Request aborted', options);
|
|
266
266
|
case 'TimeoutError':
|
|
267
|
-
throw this.handleError(408, 'Request timeout');
|
|
267
|
+
throw this.handleError(408, 'Request timeout', options);
|
|
268
268
|
}
|
|
269
|
-
throw this.handleError(500, `Unexpected error while calling the provider: name: "${error.name}" \n message: "${error.message}" \n stack: ${error.stack}
|
|
269
|
+
throw this.handleError(500, `Unexpected error while calling the provider: name: "${error.name}" \n message: "${error.message}" \n stack: ${error.stack}`, options);
|
|
270
270
|
}
|
|
271
|
-
throw this.handleError(500, 'Unexpected error while calling the provider - this is not normal, investigate');
|
|
271
|
+
throw this.handleError(500, 'Unexpected error while calling the provider - this is not normal, investigate', options);
|
|
272
272
|
}
|
|
273
273
|
if (response.status >= 400) {
|
|
274
274
|
const textResult = await response.text();
|
|
275
|
-
throw this.handleError(response.status, textResult);
|
|
275
|
+
throw this.handleError(response.status, textResult, options);
|
|
276
276
|
}
|
|
277
277
|
const responseContentType = response.headers.get('content-type');
|
|
278
278
|
let body;
|
|
@@ -283,13 +283,13 @@ export class Provider {
|
|
|
283
283
|
if (responseContentType && !responseContentType.includes('application/json')) {
|
|
284
284
|
const textResult = await response.text();
|
|
285
285
|
throw this.handleError(500, `Unsupported content-type, expected 'application/json' but got '${responseContentType}'.
|
|
286
|
-
Original response (${response.status}): ${textResult}
|
|
286
|
+
Original response (${response.status}): ${textResult}`, options);
|
|
287
287
|
}
|
|
288
288
|
try {
|
|
289
289
|
body = response.body ? await response.json() : undefined;
|
|
290
290
|
}
|
|
291
291
|
catch (err) {
|
|
292
|
-
throw this.handleError(500, `Invalid JSON response
|
|
292
|
+
throw this.handleError(500, `Invalid JSON response`, options);
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
else if (headers.Accept == 'application/octet-stream') {
|
|
@@ -301,14 +301,14 @@ export class Provider {
|
|
|
301
301
|
body = (await response.text());
|
|
302
302
|
}
|
|
303
303
|
else {
|
|
304
|
-
throw this.handleError(500, 'Unsupported Content-Type');
|
|
304
|
+
throw this.handleError(500, 'Unsupported Content-Type', options);
|
|
305
305
|
}
|
|
306
306
|
return { status: response.status, headers: response.headers, body };
|
|
307
307
|
};
|
|
308
308
|
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
309
309
|
}
|
|
310
|
-
handleError(responseStatus, message) {
|
|
311
|
-
const customError = this.customErrorHandler?.(responseStatus, message);
|
|
310
|
+
handleError(responseStatus, message, options) {
|
|
311
|
+
const customError = this.customErrorHandler?.(responseStatus, message, options);
|
|
312
312
|
return customError ?? buildHttpError(responseStatus, message);
|
|
313
313
|
}
|
|
314
314
|
}
|
|
@@ -314,6 +314,47 @@ describe('Provider', () => {
|
|
|
314
314
|
assert.ok(error instanceof HttpErrors.HttpError);
|
|
315
315
|
assert.equal(error.message, 'Weird provider behavior');
|
|
316
316
|
});
|
|
317
|
+
it('should contain the credential in the custom error handler', async (context) => {
|
|
318
|
+
const provider = new Provider({
|
|
319
|
+
prepareRequest: requestOptions => {
|
|
320
|
+
return {
|
|
321
|
+
url: `www.${requestOptions.credentials.domain ?? 'myApi.com'}`,
|
|
322
|
+
headers: {
|
|
323
|
+
'X-Custom-Provider-Header': 'value',
|
|
324
|
+
'X-Provider-Credential-Header': requestOptions.credentials.apiKey,
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
rateLimiter: undefined,
|
|
329
|
+
customErrorHandler: (responseStatus, _message, options) => {
|
|
330
|
+
if (responseStatus === 400) {
|
|
331
|
+
// What matter is that we have access to the context in the error handler
|
|
332
|
+
throw new HttpErrors.BadRequestError(`Error with API key ${options?.credentials.apiKey}`);
|
|
333
|
+
}
|
|
334
|
+
return undefined;
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
const response = new Response(undefined, {
|
|
338
|
+
status: 400,
|
|
339
|
+
headers: { 'Content-Type': 'application/json' },
|
|
340
|
+
});
|
|
341
|
+
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
342
|
+
const options = {
|
|
343
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
344
|
+
logger: logger,
|
|
345
|
+
signal: new AbortController().signal,
|
|
346
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
347
|
+
};
|
|
348
|
+
let error;
|
|
349
|
+
try {
|
|
350
|
+
await provider.delete('/endpoint/123', options);
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
error = e;
|
|
354
|
+
}
|
|
355
|
+
assert.ok(error instanceof HttpErrors.HttpError);
|
|
356
|
+
assert.equal(error.message, 'Error with API key apikey#1111');
|
|
357
|
+
});
|
|
317
358
|
it('uses default behavior if custom error handler returns undefined', async (context) => {
|
|
318
359
|
const rateLimitedProvider = new Provider({
|
|
319
360
|
prepareRequest: requestOptions => {
|
package/package.json
CHANGED
|
@@ -100,7 +100,14 @@ export class Provider {
|
|
|
100
100
|
* @see buildHttpError for the list of standard errors the SDK can handle.
|
|
101
101
|
*/
|
|
102
102
|
protected customErrorHandler:
|
|
103
|
-
| ((
|
|
103
|
+
| ((
|
|
104
|
+
responseStatus: number,
|
|
105
|
+
message: string,
|
|
106
|
+
options: {
|
|
107
|
+
credentials: Credentials;
|
|
108
|
+
logger: Logger;
|
|
109
|
+
},
|
|
110
|
+
) => HttpErrors.HttpError | undefined)
|
|
104
111
|
| undefined;
|
|
105
112
|
|
|
106
113
|
/**
|
|
@@ -216,22 +223,22 @@ export class Provider {
|
|
|
216
223
|
try {
|
|
217
224
|
const body = JSON.parse(responseBody);
|
|
218
225
|
if (body.error) {
|
|
219
|
-
reject(this.handleError(400, body.error.message));
|
|
226
|
+
reject(this.handleError(400, body.error.message, options));
|
|
220
227
|
}
|
|
221
228
|
resolve({ status: 201, headers: response.headers as unknown as Headers, body: body as T });
|
|
222
229
|
} catch (error) {
|
|
223
|
-
reject(this.handleError(500, `Failed to parse response body: "${error}"
|
|
230
|
+
reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
224
231
|
}
|
|
225
232
|
});
|
|
226
233
|
});
|
|
227
234
|
|
|
228
235
|
request.on('error', error => {
|
|
229
|
-
reject(this.handleError(400, `Error while calling the provider: "${error}"
|
|
236
|
+
reject(this.handleError(400, `Error while calling the provider: "${error}"`, options));
|
|
230
237
|
});
|
|
231
238
|
|
|
232
239
|
form.pipe(request);
|
|
233
240
|
} catch (error) {
|
|
234
|
-
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"
|
|
241
|
+
reject(this.handleError(500, `Unexpected error while calling the provider: "${error}"`, options));
|
|
235
242
|
}
|
|
236
243
|
});
|
|
237
244
|
};
|
|
@@ -358,22 +365,27 @@ export class Provider {
|
|
|
358
365
|
if (error instanceof Error) {
|
|
359
366
|
switch (error.name) {
|
|
360
367
|
case 'AbortError':
|
|
361
|
-
throw this.handleError(408, 'Request aborted');
|
|
368
|
+
throw this.handleError(408, 'Request aborted', options);
|
|
362
369
|
case 'TimeoutError':
|
|
363
|
-
throw this.handleError(408, 'Request timeout');
|
|
370
|
+
throw this.handleError(408, 'Request timeout', options);
|
|
364
371
|
}
|
|
365
372
|
throw this.handleError(
|
|
366
373
|
500,
|
|
367
374
|
`Unexpected error while calling the provider: name: "${error.name}" \n message: "${error.message}" \n stack: ${error.stack}`,
|
|
375
|
+
options,
|
|
368
376
|
);
|
|
369
377
|
}
|
|
370
378
|
|
|
371
|
-
throw this.handleError(
|
|
379
|
+
throw this.handleError(
|
|
380
|
+
500,
|
|
381
|
+
'Unexpected error while calling the provider - this is not normal, investigate',
|
|
382
|
+
options,
|
|
383
|
+
);
|
|
372
384
|
}
|
|
373
385
|
|
|
374
386
|
if (response.status >= 400) {
|
|
375
387
|
const textResult = await response.text();
|
|
376
|
-
throw this.handleError(response.status, textResult);
|
|
388
|
+
throw this.handleError(response.status, textResult, options);
|
|
377
389
|
}
|
|
378
390
|
|
|
379
391
|
const responseContentType = response.headers.get('content-type');
|
|
@@ -389,13 +401,14 @@ export class Provider {
|
|
|
389
401
|
500,
|
|
390
402
|
`Unsupported content-type, expected 'application/json' but got '${responseContentType}'.
|
|
391
403
|
Original response (${response.status}): ${textResult}`,
|
|
404
|
+
options,
|
|
392
405
|
);
|
|
393
406
|
}
|
|
394
407
|
|
|
395
408
|
try {
|
|
396
409
|
body = response.body ? await response.json() : undefined;
|
|
397
410
|
} catch (err) {
|
|
398
|
-
throw this.handleError(500, `Invalid JSON response
|
|
411
|
+
throw this.handleError(500, `Invalid JSON response`, options);
|
|
399
412
|
}
|
|
400
413
|
} else if (headers.Accept == 'application/octet-stream') {
|
|
401
414
|
// When we expect octet-stream, we accept any Content-Type the provider sends us, we just want to stream it.
|
|
@@ -404,7 +417,7 @@ export class Provider {
|
|
|
404
417
|
// Accept text based content types
|
|
405
418
|
body = (await response.text()) as T;
|
|
406
419
|
} else {
|
|
407
|
-
throw this.handleError(500, 'Unsupported Content-Type');
|
|
420
|
+
throw this.handleError(500, 'Unsupported Content-Type', options);
|
|
408
421
|
}
|
|
409
422
|
|
|
410
423
|
return { status: response.status, headers: response.headers, body };
|
|
@@ -413,8 +426,8 @@ export class Provider {
|
|
|
413
426
|
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
414
427
|
}
|
|
415
428
|
|
|
416
|
-
private handleError(responseStatus: number, message: string): HttpErrors.HttpError {
|
|
417
|
-
const customError = this.customErrorHandler?.(responseStatus, message);
|
|
429
|
+
private handleError(responseStatus: number, message: string, options: RequestOptions): HttpErrors.HttpError {
|
|
430
|
+
const customError = this.customErrorHandler?.(responseStatus, message, options);
|
|
418
431
|
|
|
419
432
|
return customError ?? buildHttpError(responseStatus, message);
|
|
420
433
|
}
|
|
@@ -374,6 +374,53 @@ describe('Provider', () => {
|
|
|
374
374
|
assert.equal(error.message, 'Weird provider behavior');
|
|
375
375
|
});
|
|
376
376
|
|
|
377
|
+
it('should contain the credential in the custom error handler', async context => {
|
|
378
|
+
const provider = new Provider({
|
|
379
|
+
prepareRequest: requestOptions => {
|
|
380
|
+
return {
|
|
381
|
+
url: `www.${requestOptions.credentials.domain ?? 'myApi.com'}`,
|
|
382
|
+
headers: {
|
|
383
|
+
'X-Custom-Provider-Header': 'value',
|
|
384
|
+
'X-Provider-Credential-Header': requestOptions.credentials.apiKey as string,
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
},
|
|
388
|
+
rateLimiter: undefined,
|
|
389
|
+
customErrorHandler: (responseStatus: number, _message: string, options) => {
|
|
390
|
+
if (responseStatus === 400) {
|
|
391
|
+
// What matter is that we have access to the context in the error handler
|
|
392
|
+
throw new HttpErrors.BadRequestError(`Error with API key ${options?.credentials.apiKey}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return undefined;
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const response = new Response(undefined, {
|
|
400
|
+
status: 400,
|
|
401
|
+
headers: { 'Content-Type': 'application/json' },
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
405
|
+
|
|
406
|
+
const options = {
|
|
407
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
408
|
+
logger: logger,
|
|
409
|
+
signal: new AbortController().signal,
|
|
410
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
let error;
|
|
414
|
+
try {
|
|
415
|
+
await provider.delete('/endpoint/123', options);
|
|
416
|
+
} catch (e) {
|
|
417
|
+
error = e;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
assert.ok(error instanceof HttpErrors.HttpError);
|
|
421
|
+
assert.equal(error.message, 'Error with API key apikey#1111');
|
|
422
|
+
});
|
|
423
|
+
|
|
377
424
|
it('uses default behavior if custom error handler returns undefined', async context => {
|
|
378
425
|
const rateLimitedProvider = new Provider({
|
|
379
426
|
prepareRequest: requestOptions => {
|