@unito/integration-sdk 2.3.13 → 2.3.15
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 +126 -1
- package/dist/src/resources/provider.d.ts +12 -1
- package/dist/src/resources/provider.js +126 -1
- package/dist/test/resources/provider.test.js +365 -31
- package/package.json +2 -1
- package/src/resources/provider.ts +152 -3
- package/test/resources/provider.test.ts +431 -31
package/dist/src/index.cjs
CHANGED
|
@@ -1359,7 +1359,7 @@ class Provider {
|
|
|
1359
1359
|
if (body.error) {
|
|
1360
1360
|
reject(this.handleError(400, body.error.message, options));
|
|
1361
1361
|
}
|
|
1362
|
-
resolve({ status: 201, headers: response.headers, body
|
|
1362
|
+
resolve({ status: 201, headers: response.headers, body });
|
|
1363
1363
|
}
|
|
1364
1364
|
catch (error) {
|
|
1365
1365
|
reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
@@ -1369,6 +1369,21 @@ class Provider {
|
|
|
1369
1369
|
request.on('error', error => {
|
|
1370
1370
|
reject(this.handleError(400, `Error while calling the provider: "${error}"`, options));
|
|
1371
1371
|
});
|
|
1372
|
+
if (options.signal) {
|
|
1373
|
+
const abortHandler = () => {
|
|
1374
|
+
request.destroy();
|
|
1375
|
+
reject(this.handleError(408, 'Timeout', options));
|
|
1376
|
+
};
|
|
1377
|
+
if (options.signal.aborted) {
|
|
1378
|
+
abortHandler();
|
|
1379
|
+
}
|
|
1380
|
+
options.signal.addEventListener('abort', abortHandler);
|
|
1381
|
+
request.on('close', () => {
|
|
1382
|
+
if (options.signal) {
|
|
1383
|
+
options.signal.removeEventListener('abort', abortHandler);
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1372
1387
|
form.pipe(request);
|
|
1373
1388
|
}
|
|
1374
1389
|
catch (error) {
|
|
@@ -1378,6 +1393,116 @@ class Provider {
|
|
|
1378
1393
|
};
|
|
1379
1394
|
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
1380
1395
|
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Performs a POST request to the provider streaming a Readable directly without loading it into memory.
|
|
1398
|
+
*
|
|
1399
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
1400
|
+
* @param stream The Readable stream containing the binary data to be sent.
|
|
1401
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
1402
|
+
* @returns The {@link Response} extracted from the provider.
|
|
1403
|
+
*/
|
|
1404
|
+
async postStream(endpoint, stream, options) {
|
|
1405
|
+
const { url: providerUrl, headers: providerHeaders } = await this.prepareRequest(options);
|
|
1406
|
+
const absoluteUrl = this.generateAbsoluteUrl(providerUrl, endpoint, options.queryParams);
|
|
1407
|
+
const headers = {
|
|
1408
|
+
'Content-Type': 'application/octet-stream',
|
|
1409
|
+
Accept: 'application/json',
|
|
1410
|
+
...providerHeaders,
|
|
1411
|
+
...options.additionnalheaders,
|
|
1412
|
+
};
|
|
1413
|
+
const callToProvider = async () => {
|
|
1414
|
+
return new Promise((resolve, reject) => {
|
|
1415
|
+
let isSettled = false; // Prevent double rejection
|
|
1416
|
+
const cleanup = () => {
|
|
1417
|
+
if (!stream.destroyed) {
|
|
1418
|
+
stream.destroy();
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
const safeReject = (error) => {
|
|
1422
|
+
if (!isSettled) {
|
|
1423
|
+
isSettled = true;
|
|
1424
|
+
cleanup();
|
|
1425
|
+
reject(error);
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
const safeResolve = (response) => {
|
|
1429
|
+
if (!isSettled) {
|
|
1430
|
+
isSettled = true;
|
|
1431
|
+
resolve(response);
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
try {
|
|
1435
|
+
const urlObj = new URL(absoluteUrl);
|
|
1436
|
+
const requestOptions = {
|
|
1437
|
+
hostname: urlObj.hostname,
|
|
1438
|
+
path: urlObj.pathname + urlObj.search,
|
|
1439
|
+
method: 'POST',
|
|
1440
|
+
headers,
|
|
1441
|
+
timeout: 0,
|
|
1442
|
+
};
|
|
1443
|
+
const request = https.request(requestOptions, response => {
|
|
1444
|
+
response.setEncoding('utf8');
|
|
1445
|
+
let responseBody = '';
|
|
1446
|
+
response.on('data', chunk => {
|
|
1447
|
+
responseBody += chunk;
|
|
1448
|
+
});
|
|
1449
|
+
response.on('error', error => {
|
|
1450
|
+
safeReject(this.handleError(500, `Response stream error: "${error}"`, options));
|
|
1451
|
+
});
|
|
1452
|
+
response.on('end', () => {
|
|
1453
|
+
try {
|
|
1454
|
+
if (response.statusCode && response.statusCode >= 400) {
|
|
1455
|
+
safeReject(this.handleError(response.statusCode, responseBody, options));
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
const body = responseBody ? JSON.parse(responseBody) : undefined;
|
|
1459
|
+
safeResolve({
|
|
1460
|
+
status: response.statusCode || 200,
|
|
1461
|
+
headers: response.headers,
|
|
1462
|
+
body,
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
catch (error) {
|
|
1466
|
+
safeReject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
});
|
|
1470
|
+
request.on('timeout', () => {
|
|
1471
|
+
request.destroy();
|
|
1472
|
+
safeReject(this.handleError(408, 'Request timeout', options));
|
|
1473
|
+
});
|
|
1474
|
+
request.on('error', error => {
|
|
1475
|
+
safeReject(this.handleError(500, `Error while calling the provider: "${error}"`, options));
|
|
1476
|
+
});
|
|
1477
|
+
stream.on('error', error => {
|
|
1478
|
+
request.destroy();
|
|
1479
|
+
safeReject(this.handleError(500, `Stream error: "${error}"`, options));
|
|
1480
|
+
});
|
|
1481
|
+
if (options.signal) {
|
|
1482
|
+
const abortHandler = () => {
|
|
1483
|
+
request.destroy();
|
|
1484
|
+
safeReject(this.handleError(408, 'Timeout', options));
|
|
1485
|
+
};
|
|
1486
|
+
if (options.signal.aborted) {
|
|
1487
|
+
abortHandler();
|
|
1488
|
+
}
|
|
1489
|
+
options.signal.addEventListener('abort', abortHandler);
|
|
1490
|
+
request.on('close', () => {
|
|
1491
|
+
if (options.signal) {
|
|
1492
|
+
options.signal.removeEventListener('abort', abortHandler);
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
// Stream the data directly without buffering
|
|
1497
|
+
stream.pipe(request);
|
|
1498
|
+
}
|
|
1499
|
+
catch (error) {
|
|
1500
|
+
safeReject(this.handleError(500, `Unexpected error while calling the provider: "${error}"`, options));
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
};
|
|
1504
|
+
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
1505
|
+
}
|
|
1381
1506
|
/**
|
|
1382
1507
|
* Performs a PUT request to the provider.
|
|
1383
1508
|
*
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import FormData from 'form-data';
|
|
2
|
+
import * as stream from 'stream';
|
|
3
|
+
import { IncomingHttpHeaders } from 'http';
|
|
2
4
|
import * as HttpErrors from '../httpErrors.js';
|
|
3
5
|
import { Credentials } from '../middlewares/credentials.js';
|
|
4
6
|
import Logger from '../resources/logger.js';
|
|
@@ -54,7 +56,7 @@ export type RequestOptions = {
|
|
|
54
56
|
export type Response<T> = {
|
|
55
57
|
body: T;
|
|
56
58
|
status: number;
|
|
57
|
-
headers: Headers;
|
|
59
|
+
headers: Headers | IncomingHttpHeaders;
|
|
58
60
|
};
|
|
59
61
|
export type PreparedRequest = {
|
|
60
62
|
url: string;
|
|
@@ -152,6 +154,15 @@ export declare class Provider {
|
|
|
152
154
|
*/
|
|
153
155
|
post<T>(endpoint: string, body: RequestBody, options: RequestOptions): Promise<Response<T>>;
|
|
154
156
|
postForm<T>(endpoint: string, form: FormData, options: RequestOptions): Promise<Response<T>>;
|
|
157
|
+
/**
|
|
158
|
+
* Performs a POST request to the provider streaming a Readable directly without loading it into memory.
|
|
159
|
+
*
|
|
160
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
161
|
+
* @param stream The Readable stream containing the binary data to be sent.
|
|
162
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
163
|
+
* @returns The {@link Response} extracted from the provider.
|
|
164
|
+
*/
|
|
165
|
+
postStream<T>(endpoint: string, stream: stream.Readable, options: RequestOptions): Promise<Response<T>>;
|
|
155
166
|
/**
|
|
156
167
|
* Performs a PUT request to the provider.
|
|
157
168
|
*
|
|
@@ -140,7 +140,7 @@ export class Provider {
|
|
|
140
140
|
if (body.error) {
|
|
141
141
|
reject(this.handleError(400, body.error.message, options));
|
|
142
142
|
}
|
|
143
|
-
resolve({ status: 201, headers: response.headers, body
|
|
143
|
+
resolve({ status: 201, headers: response.headers, body });
|
|
144
144
|
}
|
|
145
145
|
catch (error) {
|
|
146
146
|
reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
@@ -150,6 +150,21 @@ export class Provider {
|
|
|
150
150
|
request.on('error', error => {
|
|
151
151
|
reject(this.handleError(400, `Error while calling the provider: "${error}"`, options));
|
|
152
152
|
});
|
|
153
|
+
if (options.signal) {
|
|
154
|
+
const abortHandler = () => {
|
|
155
|
+
request.destroy();
|
|
156
|
+
reject(this.handleError(408, 'Timeout', options));
|
|
157
|
+
};
|
|
158
|
+
if (options.signal.aborted) {
|
|
159
|
+
abortHandler();
|
|
160
|
+
}
|
|
161
|
+
options.signal.addEventListener('abort', abortHandler);
|
|
162
|
+
request.on('close', () => {
|
|
163
|
+
if (options.signal) {
|
|
164
|
+
options.signal.removeEventListener('abort', abortHandler);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
153
168
|
form.pipe(request);
|
|
154
169
|
}
|
|
155
170
|
catch (error) {
|
|
@@ -159,6 +174,116 @@ export class Provider {
|
|
|
159
174
|
};
|
|
160
175
|
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
161
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Performs a POST request to the provider streaming a Readable directly without loading it into memory.
|
|
179
|
+
*
|
|
180
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
181
|
+
* @param stream The Readable stream containing the binary data to be sent.
|
|
182
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
183
|
+
* @returns The {@link Response} extracted from the provider.
|
|
184
|
+
*/
|
|
185
|
+
async postStream(endpoint, stream, options) {
|
|
186
|
+
const { url: providerUrl, headers: providerHeaders } = await this.prepareRequest(options);
|
|
187
|
+
const absoluteUrl = this.generateAbsoluteUrl(providerUrl, endpoint, options.queryParams);
|
|
188
|
+
const headers = {
|
|
189
|
+
'Content-Type': 'application/octet-stream',
|
|
190
|
+
Accept: 'application/json',
|
|
191
|
+
...providerHeaders,
|
|
192
|
+
...options.additionnalheaders,
|
|
193
|
+
};
|
|
194
|
+
const callToProvider = async () => {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
let isSettled = false; // Prevent double rejection
|
|
197
|
+
const cleanup = () => {
|
|
198
|
+
if (!stream.destroyed) {
|
|
199
|
+
stream.destroy();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const safeReject = (error) => {
|
|
203
|
+
if (!isSettled) {
|
|
204
|
+
isSettled = true;
|
|
205
|
+
cleanup();
|
|
206
|
+
reject(error);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const safeResolve = (response) => {
|
|
210
|
+
if (!isSettled) {
|
|
211
|
+
isSettled = true;
|
|
212
|
+
resolve(response);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
try {
|
|
216
|
+
const urlObj = new URL(absoluteUrl);
|
|
217
|
+
const requestOptions = {
|
|
218
|
+
hostname: urlObj.hostname,
|
|
219
|
+
path: urlObj.pathname + urlObj.search,
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers,
|
|
222
|
+
timeout: 0,
|
|
223
|
+
};
|
|
224
|
+
const request = https.request(requestOptions, response => {
|
|
225
|
+
response.setEncoding('utf8');
|
|
226
|
+
let responseBody = '';
|
|
227
|
+
response.on('data', chunk => {
|
|
228
|
+
responseBody += chunk;
|
|
229
|
+
});
|
|
230
|
+
response.on('error', error => {
|
|
231
|
+
safeReject(this.handleError(500, `Response stream error: "${error}"`, options));
|
|
232
|
+
});
|
|
233
|
+
response.on('end', () => {
|
|
234
|
+
try {
|
|
235
|
+
if (response.statusCode && response.statusCode >= 400) {
|
|
236
|
+
safeReject(this.handleError(response.statusCode, responseBody, options));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const body = responseBody ? JSON.parse(responseBody) : undefined;
|
|
240
|
+
safeResolve({
|
|
241
|
+
status: response.statusCode || 200,
|
|
242
|
+
headers: response.headers,
|
|
243
|
+
body,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
safeReject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
request.on('timeout', () => {
|
|
252
|
+
request.destroy();
|
|
253
|
+
safeReject(this.handleError(408, 'Request timeout', options));
|
|
254
|
+
});
|
|
255
|
+
request.on('error', error => {
|
|
256
|
+
safeReject(this.handleError(500, `Error while calling the provider: "${error}"`, options));
|
|
257
|
+
});
|
|
258
|
+
stream.on('error', error => {
|
|
259
|
+
request.destroy();
|
|
260
|
+
safeReject(this.handleError(500, `Stream error: "${error}"`, options));
|
|
261
|
+
});
|
|
262
|
+
if (options.signal) {
|
|
263
|
+
const abortHandler = () => {
|
|
264
|
+
request.destroy();
|
|
265
|
+
safeReject(this.handleError(408, 'Timeout', options));
|
|
266
|
+
};
|
|
267
|
+
if (options.signal.aborted) {
|
|
268
|
+
abortHandler();
|
|
269
|
+
}
|
|
270
|
+
options.signal.addEventListener('abort', abortHandler);
|
|
271
|
+
request.on('close', () => {
|
|
272
|
+
if (options.signal) {
|
|
273
|
+
options.signal.removeEventListener('abort', abortHandler);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Stream the data directly without buffering
|
|
278
|
+
stream.pipe(request);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
safeReject(this.handleError(500, `Unexpected error while calling the provider: "${error}"`, options));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
286
|
+
}
|
|
162
287
|
/**
|
|
163
288
|
* Performs a PUT request to the provider.
|
|
164
289
|
*
|