@rainbow-o23/n3 1.0.57 → 1.0.58-alpha.2
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/README.md +65 -19
- package/index.cjs +67 -4
- package/index.js +67 -4
- package/lib/http/fetch-step.d.ts +7 -0
- package/package.json +3 -2
- package/src/lib/http/fetch-step.ts +76 -4
- package/test/step/zstd.test.ts +65 -0
package/README.md
CHANGED
|
@@ -592,13 +592,17 @@ step set. Additionally, nested transactions are also supported, which means Tran
|
|
|
592
592
|
|
|
593
593
|
#### Environment Parameters
|
|
594
594
|
|
|
595
|
-
| Name
|
|
596
|
-
|
|
597
|
-
| `endpoints.SYSTEM.ENDPOINT.url`
|
|
598
|
-
| `endpoints.SYSTEM.ENDPOINT.headers`
|
|
599
|
-
| `endpoints.SYSTEM.global.headers`
|
|
600
|
-
| `endpoints.SYSTEM.ENDPOINT.
|
|
601
|
-
| `endpoints.SYSTEM.global.
|
|
595
|
+
| Name | Type | Default Value | Comments |
|
|
596
|
+
|---------------------------------------------------------|--------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
597
|
+
| `endpoints.SYSTEM.ENDPOINT.url` | string | | Endpoint URL. |
|
|
598
|
+
| `endpoints.SYSTEM.ENDPOINT.headers` | string | | Endpoint request headers, use global headers if this parameter doesn't present.<br>Format follows `name=value[;name=value[...]]`. |
|
|
599
|
+
| `endpoints.SYSTEM.global.headers` | string | | Endpoint system global request headers.<br>Format follows `name=value[;name=value[...]]`. |
|
|
600
|
+
| `endpoints.SYSTEM.ENDPOINT.headers.transparent` | string | | Endpoint request transparent-passed headers names, use global headers if this parameter doesn't present.<br>Format follows `name1[;name2[...]]`. |
|
|
601
|
+
| `endpoints.SYSTEM.global.headers.transparent` | string | | Endpoint system global request transparent-passed headers names.<br>Format follows `name1[;name2[...]]`. |
|
|
602
|
+
| `endpoints.SYSTEM.ENDPOINT.headers.transparent.omitted` | string | | Endpoint request headers names which omitted from transparent-passed headers, use global headers if this parameter doesn't present.<br>Format follows `name1[;name2[...]]`. |
|
|
603
|
+
| `endpoints.SYSTEM.global.headers.transparent.omitted` | string | | Endpoint system global request headers names which omitted from transparent-passed headers.<br>Format follows `name1[;name2[...]]`. |
|
|
604
|
+
| `endpoints.SYSTEM.ENDPOINT.timeout` | number | | Endpoint request timeout, in seconds, use global timeout if this parameter doesn't present. |
|
|
605
|
+
| `endpoints.SYSTEM.global.timeout` | number | -1 | Endpoint system global timeout, in seconds, `-1` represents no timeout. |
|
|
602
606
|
|
|
603
607
|
`SYSTEM` represents endpoint system, `ENDPOINT` represents endpoint url. For example:
|
|
604
608
|
|
|
@@ -611,18 +615,60 @@ CFG_ENDPOINTS_ORDER_PAYMENT_URL=https://order.com/payment
|
|
|
611
615
|
|
|
612
616
|
#### Constructor Parameters
|
|
613
617
|
|
|
614
|
-
| Name
|
|
615
|
-
|
|
616
|
-
| endpointSystemCode
|
|
617
|
-
| endpointName
|
|
618
|
-
| urlGenerate
|
|
619
|
-
| method
|
|
620
|
-
| timeout
|
|
621
|
-
|
|
|
622
|
-
|
|
|
623
|
-
|
|
|
624
|
-
|
|
|
625
|
-
|
|
|
618
|
+
| Name | Type | Default Value | Comments |
|
|
619
|
+
|-------------------------------|--------------------------------------------------------------------------------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------|
|
|
620
|
+
| endpointSystemCode | string | | Endpoint system code. |
|
|
621
|
+
| endpointName | string | | Endpoint name. |
|
|
622
|
+
| urlGenerate | ScriptFuncOrBody\<HttpGenerateUrl> | | Endpoint url generator, `$endpointUrl`. |
|
|
623
|
+
| method | string | | Http method, default `post`. |
|
|
624
|
+
| timeout | number | | Endpoint timeout, in seconds. |
|
|
625
|
+
| transparentHeaderNames | Array<string> | | Transparent-passed request headers names. |
|
|
626
|
+
| omittedTransparentHeaderNames | Array<string> | | Omitted names of transparently-passed request headers. |
|
|
627
|
+
| headersGenerate | ScriptFuncOrBody\<HttpGenerateHeaders> | | Endpoint request headers generator. |
|
|
628
|
+
| bodyUsed | boolean | | Send request with body or not, or automatically disregards the body when sending a `get` request when not specified. |
|
|
629
|
+
| bodyGenerate | ScriptFuncOrBody\<HttpGenerateBody> | | Endpoint request body generator. |
|
|
630
|
+
| responseGenerate | ScriptFuncOrBody\<HttpGenerateResponse> | | Endpoint response body generator, `$response`. |
|
|
631
|
+
| responseErrorHandles | ScriptFuncOrBody\<HttpHandleError><br>or<br>{[key: HttpErrorCode]: ScriptFuncOrBody\<HttpHandleError>} | | Endpoint response error handlers. |
|
|
632
|
+
|
|
633
|
+
- `transparentHeaderNames` and `omittedTransparentHeaderNames`:
|
|
634
|
+
Use `transparentHeaderNames` to specify the names of request headers whose values need to be transparently passed from the input
|
|
635
|
+
parameters to the downstream service. Separate the names with ';'. The names support using `.` for connection so that values from
|
|
636
|
+
multi-level objects can be directly retrieved. For example, `account.name` will retrieve the value of the `name` property from the
|
|
637
|
+
`account` property of the input object. When writing the values into the header values, the following rules apply:
|
|
638
|
+
- If the value is an array, use `, ` to connect the elements. `null` and empty strings will be filtered out.
|
|
639
|
+
- If the value is an object, use the object's keys to generate multiple headers. `null` and empty strings will be filtered out.
|
|
640
|
+
- For other values, convert them to strings. `null` and empty strings will be filtered out.
|
|
641
|
+
- Note that an empty string does not include blank strings, and no automatic trimming will be performed.
|
|
642
|
+
|
|
643
|
+
If the `transparentHeaderNames` at the step level is not defined, use the definition in
|
|
644
|
+
`endpoints.SYSTEM.ENDPOINT.headers.transparent`. If it is also not defined at the endpoint level, use the definition in
|
|
645
|
+
`endpoints.SYSTEM.global.headers.transparent`.
|
|
646
|
+
|
|
647
|
+
After obtaining the transparently passed request headers, check the definition of `omittedTransparentHeaderNames`. If it is defined,
|
|
648
|
+
remove the corresponding headers from the headers. `omittedTransparentHeaderNames` is case-insensitive. Similarly, if the
|
|
649
|
+
`omittedTransparentHeaderNames` at the step level is not defined, use the definition in
|
|
650
|
+
`endpoints.SYSTEM.ENDPOINT.headers.transparent.omitted`. If it is also not defined at the endpoint level, use the definition in
|
|
651
|
+
`endpoints.SYSTEM.global.headers.transparent.omitted`.
|
|
652
|
+
|
|
653
|
+
For example:
|
|
654
|
+
If the input data contains `{account: {name: 'John', token: '******'}}` and `transparentHeaderNames` is defined as `account`, then two
|
|
655
|
+
transparently passed headers will be obtained: `name=John` and `token=******`. At this time, if `omittedTransparentHeaderNames` is defined
|
|
656
|
+
as `name`, the headers that will be finally transparently passed to the downstream service are `token=******`, and `name` will be ignored.
|
|
657
|
+
|
|
658
|
+
## Request headers
|
|
659
|
+
|
|
660
|
+
There are three ways to transmit request headers to downstream services. In order of priority from high to low, they are `headersGenerate`,
|
|
661
|
+
`headers.transparent`, and `headers`. If a header appears in a higher-priority method, the header with the same name generated by a
|
|
662
|
+
lower-priority method will be ignored. Note that the matching is case-sensitive.
|
|
663
|
+
|
|
664
|
+
Normally, if you need to transparently pass the request headers from the client to the downstream service, you should use `headers: true` in
|
|
665
|
+
the pipeline definition. Then you can directly use `transparentHeaderNames: headers` to obtain all the request headers, and then use
|
|
666
|
+
`omittedTransparentHeaderNames` for necessary filtering.
|
|
667
|
+
|
|
668
|
+
> It should be noted that since the fetch step initiates a new request to the downstream service, its request structure and data will be
|
|
669
|
+
> modified or reset according to requirements. Therefore, even if you need to transparently pass the request headers, some of the headers
|
|
670
|
+
> are still not applicable. So by default, the two headers `content-encoding` and `content-length` will be filtered out. No matter how the
|
|
671
|
+
> request headers are generated in the above process, these two headers are always automatically generated by `node-fetch`.
|
|
626
672
|
|
|
627
673
|
## Installation
|
|
628
674
|
|
package/index.cjs
CHANGED
|
@@ -1281,6 +1281,8 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1281
1281
|
_endpointTimeout;
|
|
1282
1282
|
_urlGenerateSnippet;
|
|
1283
1283
|
_urlGenerateFunc;
|
|
1284
|
+
_transparentHeaderNames;
|
|
1285
|
+
_omittedTransparentHeaderNames;
|
|
1284
1286
|
_headersGenerateSnippet;
|
|
1285
1287
|
_headersGenerateFunc;
|
|
1286
1288
|
_bodyUsed;
|
|
@@ -1311,6 +1313,10 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1311
1313
|
throw e;
|
|
1312
1314
|
}
|
|
1313
1315
|
});
|
|
1316
|
+
this._transparentHeaderNames = options.transparentHeaderNames
|
|
1317
|
+
?? this.generateTransparentHeaderNames(config.getString(`endpoints.${endpointKey}.headers.transparent`), this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent`)));
|
|
1318
|
+
this._omittedTransparentHeaderNames = options.omittedTransparentHeaderNames
|
|
1319
|
+
?? this.generateTransparentHeaderNames(config.getString(`endpoints.${endpointKey}.headers.transparent.omitted`), this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent.omitted`)));
|
|
1314
1320
|
this._headersGenerateSnippet = options.headersGenerate;
|
|
1315
1321
|
this._headersGenerateFunc = Utils.createAsyncFunction(this.getHeadersGenerateSnippet(), {
|
|
1316
1322
|
createDefault: () => async (_$factor, _$request, _$helpers, _$) => (void 0),
|
|
@@ -1334,8 +1340,15 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1334
1340
|
});
|
|
1335
1341
|
this._responseGenerateSnippet = options.responseGenerate;
|
|
1336
1342
|
this._responseGenerateFunc = Utils.createAsyncFunction(this.getResponseGenerateSnippet(), {
|
|
1337
|
-
createDefault: () => async ($response, _$factor, _$request,
|
|
1338
|
-
|
|
1343
|
+
createDefault: () => async ($response, _$factor, _$request, $helpers, _$) => {
|
|
1344
|
+
const contentEncoding = $response.headers?.get('content-encoding');
|
|
1345
|
+
if (contentEncoding === 'zstd') {
|
|
1346
|
+
const buffer = await $response.buffer();
|
|
1347
|
+
return JSON.parse(await $helpers.$zstd(buffer));
|
|
1348
|
+
}
|
|
1349
|
+
else {
|
|
1350
|
+
return await $response.json();
|
|
1351
|
+
}
|
|
1339
1352
|
},
|
|
1340
1353
|
getVariableNames: () => this.getResponseGenerateVariableName(),
|
|
1341
1354
|
error: (e) => {
|
|
@@ -1427,6 +1440,20 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1427
1440
|
getUrlGenerateVariableName() {
|
|
1428
1441
|
return ['$endpointUrl', '$factor', '$request', ...this.getHelpersVariableNames()];
|
|
1429
1442
|
}
|
|
1443
|
+
getTransparentHeaderNames() {
|
|
1444
|
+
return this._transparentHeaderNames ?? [];
|
|
1445
|
+
}
|
|
1446
|
+
getOmittedTransparentHeaderNames() {
|
|
1447
|
+
return this._omittedTransparentHeaderNames ?? [];
|
|
1448
|
+
}
|
|
1449
|
+
generateTransparentHeaderNames(headerNames, base) {
|
|
1450
|
+
return [
|
|
1451
|
+
...(base ?? []),
|
|
1452
|
+
...new Set(`${headerNames || ''}`.split(';')
|
|
1453
|
+
.map(x => x.trim())
|
|
1454
|
+
.filter(x => x.length !== 0))
|
|
1455
|
+
];
|
|
1456
|
+
}
|
|
1430
1457
|
getHeadersGenerateSnippet() {
|
|
1431
1458
|
return this._headersGenerateSnippet;
|
|
1432
1459
|
}
|
|
@@ -1458,7 +1485,37 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1458
1485
|
url = await this._urlGenerateFunc(this.getEndpointUrl(), data, request, $helpers, $helpers);
|
|
1459
1486
|
const method = this.getEndpointMethod();
|
|
1460
1487
|
const staticHeaders = this.getEndpointHeaders() ?? {};
|
|
1461
|
-
const
|
|
1488
|
+
const transparentHeaders = (this.getTransparentHeaderNames() ?? []).reduce((headers, name) => {
|
|
1489
|
+
const value = Utils.getValue(data, name);
|
|
1490
|
+
if (value == null) {
|
|
1491
|
+
}
|
|
1492
|
+
else if (Array.isArray(value)) {
|
|
1493
|
+
const headerValue = value.filter(v => v != null && `${v}`.length !== 0).join(', ');
|
|
1494
|
+
if (headerValue.length !== 0) {
|
|
1495
|
+
headers[name] = headerValue;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
else if (typeof value === 'object') {
|
|
1499
|
+
Object.keys(value).forEach(key => {
|
|
1500
|
+
const headerValue = value[key];
|
|
1501
|
+
if (headerValue != null) {
|
|
1502
|
+
const s = `${headerValue}`;
|
|
1503
|
+
if (s.length !== 0) {
|
|
1504
|
+
headers[key] = s;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
else {
|
|
1510
|
+
const headerValue = `${value}`;
|
|
1511
|
+
if (headerValue.length !== 0) {
|
|
1512
|
+
headers[name] = headerValue;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return headers;
|
|
1516
|
+
}, {});
|
|
1517
|
+
(this.getOmittedTransparentHeaderNames() ?? []).forEach(name => delete transparentHeaders[name]);
|
|
1518
|
+
const generatedHeaders = await this._headersGenerateFunc(data, request, $helpers, $helpers) ?? {};
|
|
1462
1519
|
let body;
|
|
1463
1520
|
const bodyUsed = this.isBodyUsed();
|
|
1464
1521
|
if (bodyUsed === true || (bodyUsed == null && method !== 'get')) {
|
|
@@ -1470,8 +1527,14 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1470
1527
|
if (body != null && typeof body !== 'string') {
|
|
1471
1528
|
body = JSON.stringify(body);
|
|
1472
1529
|
}
|
|
1530
|
+
const headers = { ...staticHeaders, ...transparentHeaders, ...generatedHeaders };
|
|
1531
|
+
Object.keys(headers).filter(name => {
|
|
1532
|
+
return ['content-encoding', 'content-length'].includes(name.toLowerCase());
|
|
1533
|
+
}).forEach(name => {
|
|
1534
|
+
delete headers[name];
|
|
1535
|
+
});
|
|
1473
1536
|
const response = await fetch(url, {
|
|
1474
|
-
method, headers
|
|
1537
|
+
method, headers, body,
|
|
1475
1538
|
signal: this.needTimeout() ? (() => {
|
|
1476
1539
|
const controller = new AbortController();
|
|
1477
1540
|
setTimeout(() => controller.abort(), this.getEndpointTimeout());
|
package/index.js
CHANGED
|
@@ -1279,6 +1279,8 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1279
1279
|
_endpointTimeout;
|
|
1280
1280
|
_urlGenerateSnippet;
|
|
1281
1281
|
_urlGenerateFunc;
|
|
1282
|
+
_transparentHeaderNames;
|
|
1283
|
+
_omittedTransparentHeaderNames;
|
|
1282
1284
|
_headersGenerateSnippet;
|
|
1283
1285
|
_headersGenerateFunc;
|
|
1284
1286
|
_bodyUsed;
|
|
@@ -1309,6 +1311,10 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1309
1311
|
throw e;
|
|
1310
1312
|
}
|
|
1311
1313
|
});
|
|
1314
|
+
this._transparentHeaderNames = options.transparentHeaderNames
|
|
1315
|
+
?? this.generateTransparentHeaderNames(config.getString(`endpoints.${endpointKey}.headers.transparent`), this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent`)));
|
|
1316
|
+
this._omittedTransparentHeaderNames = options.omittedTransparentHeaderNames
|
|
1317
|
+
?? this.generateTransparentHeaderNames(config.getString(`endpoints.${endpointKey}.headers.transparent.omitted`), this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent.omitted`)));
|
|
1312
1318
|
this._headersGenerateSnippet = options.headersGenerate;
|
|
1313
1319
|
this._headersGenerateFunc = Utils.createAsyncFunction(this.getHeadersGenerateSnippet(), {
|
|
1314
1320
|
createDefault: () => async (_$factor, _$request, _$helpers, _$) => (void 0),
|
|
@@ -1332,8 +1338,15 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1332
1338
|
});
|
|
1333
1339
|
this._responseGenerateSnippet = options.responseGenerate;
|
|
1334
1340
|
this._responseGenerateFunc = Utils.createAsyncFunction(this.getResponseGenerateSnippet(), {
|
|
1335
|
-
createDefault: () => async ($response, _$factor, _$request,
|
|
1336
|
-
|
|
1341
|
+
createDefault: () => async ($response, _$factor, _$request, $helpers, _$) => {
|
|
1342
|
+
const contentEncoding = $response.headers?.get('content-encoding');
|
|
1343
|
+
if (contentEncoding === 'zstd') {
|
|
1344
|
+
const buffer = await $response.buffer();
|
|
1345
|
+
return JSON.parse(await $helpers.$zstd(buffer));
|
|
1346
|
+
}
|
|
1347
|
+
else {
|
|
1348
|
+
return await $response.json();
|
|
1349
|
+
}
|
|
1337
1350
|
},
|
|
1338
1351
|
getVariableNames: () => this.getResponseGenerateVariableName(),
|
|
1339
1352
|
error: (e) => {
|
|
@@ -1425,6 +1438,20 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1425
1438
|
getUrlGenerateVariableName() {
|
|
1426
1439
|
return ['$endpointUrl', '$factor', '$request', ...this.getHelpersVariableNames()];
|
|
1427
1440
|
}
|
|
1441
|
+
getTransparentHeaderNames() {
|
|
1442
|
+
return this._transparentHeaderNames ?? [];
|
|
1443
|
+
}
|
|
1444
|
+
getOmittedTransparentHeaderNames() {
|
|
1445
|
+
return this._omittedTransparentHeaderNames ?? [];
|
|
1446
|
+
}
|
|
1447
|
+
generateTransparentHeaderNames(headerNames, base) {
|
|
1448
|
+
return [
|
|
1449
|
+
...(base ?? []),
|
|
1450
|
+
...new Set(`${headerNames || ''}`.split(';')
|
|
1451
|
+
.map(x => x.trim())
|
|
1452
|
+
.filter(x => x.length !== 0))
|
|
1453
|
+
];
|
|
1454
|
+
}
|
|
1428
1455
|
getHeadersGenerateSnippet() {
|
|
1429
1456
|
return this._headersGenerateSnippet;
|
|
1430
1457
|
}
|
|
@@ -1456,7 +1483,37 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1456
1483
|
url = await this._urlGenerateFunc(this.getEndpointUrl(), data, request, $helpers, $helpers);
|
|
1457
1484
|
const method = this.getEndpointMethod();
|
|
1458
1485
|
const staticHeaders = this.getEndpointHeaders() ?? {};
|
|
1459
|
-
const
|
|
1486
|
+
const transparentHeaders = (this.getTransparentHeaderNames() ?? []).reduce((headers, name) => {
|
|
1487
|
+
const value = Utils.getValue(data, name);
|
|
1488
|
+
if (value == null) {
|
|
1489
|
+
}
|
|
1490
|
+
else if (Array.isArray(value)) {
|
|
1491
|
+
const headerValue = value.filter(v => v != null && `${v}`.length !== 0).join(', ');
|
|
1492
|
+
if (headerValue.length !== 0) {
|
|
1493
|
+
headers[name] = headerValue;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
else if (typeof value === 'object') {
|
|
1497
|
+
Object.keys(value).forEach(key => {
|
|
1498
|
+
const headerValue = value[key];
|
|
1499
|
+
if (headerValue != null) {
|
|
1500
|
+
const s = `${headerValue}`;
|
|
1501
|
+
if (s.length !== 0) {
|
|
1502
|
+
headers[key] = s;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
else {
|
|
1508
|
+
const headerValue = `${value}`;
|
|
1509
|
+
if (headerValue.length !== 0) {
|
|
1510
|
+
headers[name] = headerValue;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return headers;
|
|
1514
|
+
}, {});
|
|
1515
|
+
(this.getOmittedTransparentHeaderNames() ?? []).forEach(name => delete transparentHeaders[name]);
|
|
1516
|
+
const generatedHeaders = await this._headersGenerateFunc(data, request, $helpers, $helpers) ?? {};
|
|
1460
1517
|
let body;
|
|
1461
1518
|
const bodyUsed = this.isBodyUsed();
|
|
1462
1519
|
if (bodyUsed === true || (bodyUsed == null && method !== 'get')) {
|
|
@@ -1468,8 +1525,14 @@ class FetchPipelineStep extends AbstractFragmentaryPipelineStep {
|
|
|
1468
1525
|
if (body != null && typeof body !== 'string') {
|
|
1469
1526
|
body = JSON.stringify(body);
|
|
1470
1527
|
}
|
|
1528
|
+
const headers = { ...staticHeaders, ...transparentHeaders, ...generatedHeaders };
|
|
1529
|
+
Object.keys(headers).filter(name => {
|
|
1530
|
+
return ['content-encoding', 'content-length'].includes(name.toLowerCase());
|
|
1531
|
+
}).forEach(name => {
|
|
1532
|
+
delete headers[name];
|
|
1533
|
+
});
|
|
1471
1534
|
const response = await fetch(url, {
|
|
1472
|
-
method, headers
|
|
1535
|
+
method, headers, body,
|
|
1473
1536
|
signal: this.needTimeout() ? (() => {
|
|
1474
1537
|
const controller = new AbortController();
|
|
1475
1538
|
setTimeout(() => controller.abort(), this.getEndpointTimeout());
|
package/lib/http/fetch-step.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface FetchPipelineStepOptions<In = PipelineStepPayload, Out = Pipeli
|
|
|
7
7
|
urlGenerate?: ScriptFuncOrBody<HttpGenerateUrl<In, InFragment>>;
|
|
8
8
|
method?: string;
|
|
9
9
|
timeout?: number;
|
|
10
|
+
transparentHeaderNames?: Array<string>;
|
|
11
|
+
omittedTransparentHeaderNames?: Array<string>;
|
|
10
12
|
headersGenerate?: ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>>;
|
|
11
13
|
bodyUsed?: boolean;
|
|
12
14
|
bodyGenerate?: ScriptFuncOrBody<HttpGenerateBody<In, InFragment, BodyData>>;
|
|
@@ -24,6 +26,8 @@ export declare class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineS
|
|
|
24
26
|
private readonly _endpointTimeout;
|
|
25
27
|
private readonly _urlGenerateSnippet;
|
|
26
28
|
private readonly _urlGenerateFunc;
|
|
29
|
+
private readonly _transparentHeaderNames;
|
|
30
|
+
private readonly _omittedTransparentHeaderNames;
|
|
27
31
|
private readonly _headersGenerateSnippet;
|
|
28
32
|
private readonly _headersGenerateFunc;
|
|
29
33
|
private readonly _bodyUsed;
|
|
@@ -44,6 +48,9 @@ export declare class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineS
|
|
|
44
48
|
needTimeout(): boolean;
|
|
45
49
|
getUrlGenerateSnippet(): ScriptFuncOrBody<HttpGenerateUrl<In, InFragment>>;
|
|
46
50
|
protected getUrlGenerateVariableName(): Array<string>;
|
|
51
|
+
getTransparentHeaderNames(): Array<string>;
|
|
52
|
+
getOmittedTransparentHeaderNames(): Array<string>;
|
|
53
|
+
protected generateTransparentHeaderNames(headerNames?: string, base?: Array<string>): Array<string>;
|
|
47
54
|
getHeadersGenerateSnippet(): ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>>;
|
|
48
55
|
protected getHeadersGenerateVariableNames(): Array<string>;
|
|
49
56
|
protected isBodyUsed(): boolean | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainbow-o23/n3",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.58-alpha.2",
|
|
4
4
|
"description": "o23 pipelines",
|
|
5
5
|
"main": "index.cjs",
|
|
6
6
|
"module": "index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"url": "https://github.com/InsureMO/rainbow-o23/issues"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@rainbow-o23/n1": "1.0.
|
|
24
|
+
"@rainbow-o23/n1": "1.0.58-alpha.2",
|
|
25
25
|
"node-fetch": "2.6.7",
|
|
26
26
|
"typeorm": "^0.3.20",
|
|
27
27
|
"typescript": "5.5.4"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@babel/core": "^7.23.9",
|
|
31
31
|
"@babel/preset-env": "^7.23.9",
|
|
32
32
|
"@babel/preset-typescript": "^7.23.3",
|
|
33
|
+
"@oneidentity/zstd-js": "^1.0.3",
|
|
33
34
|
"@rollup/plugin-babel": "^6.0.4",
|
|
34
35
|
"@rollup/plugin-eslint": "^9.0.3",
|
|
35
36
|
"@types/better-sqlite3": "^7.6.6",
|
|
@@ -23,6 +23,8 @@ export interface FetchPipelineStepOptions<In = PipelineStepPayload, Out = Pipeli
|
|
|
23
23
|
method?: string;
|
|
24
24
|
/** on seconds */
|
|
25
25
|
timeout?: number;
|
|
26
|
+
transparentHeaderNames?: Array<string>;
|
|
27
|
+
omittedTransparentHeaderNames?: Array<string>;
|
|
26
28
|
headersGenerate?: ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>>;
|
|
27
29
|
bodyUsed?: boolean;
|
|
28
30
|
bodyGenerate?: ScriptFuncOrBody<HttpGenerateBody<In, InFragment, BodyData>>;
|
|
@@ -42,6 +44,8 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
42
44
|
private readonly _endpointTimeout: number;
|
|
43
45
|
private readonly _urlGenerateSnippet: ScriptFuncOrBody<HttpGenerateUrl<In, InFragment>>;
|
|
44
46
|
private readonly _urlGenerateFunc: HttpGenerateUrl<In, InFragment>;
|
|
47
|
+
private readonly _transparentHeaderNames: Array<string>;
|
|
48
|
+
private readonly _omittedTransparentHeaderNames: Array<string>;
|
|
45
49
|
private readonly _headersGenerateSnippet: ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>>;
|
|
46
50
|
private readonly _headersGenerateFunc: HttpGenerateHeaders<In, InFragment>;
|
|
47
51
|
private readonly _bodyUsed: boolean;
|
|
@@ -78,6 +82,14 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
78
82
|
throw e;
|
|
79
83
|
}
|
|
80
84
|
});
|
|
85
|
+
this._transparentHeaderNames = options.transparentHeaderNames
|
|
86
|
+
?? this.generateTransparentHeaderNames(
|
|
87
|
+
config.getString(`endpoints.${endpointKey}.headers.transparent`),
|
|
88
|
+
this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent`)));
|
|
89
|
+
this._omittedTransparentHeaderNames = options.omittedTransparentHeaderNames
|
|
90
|
+
?? this.generateTransparentHeaderNames(
|
|
91
|
+
config.getString(`endpoints.${endpointKey}.headers.transparent.omitted`),
|
|
92
|
+
this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent.omitted`)));
|
|
81
93
|
this._headersGenerateSnippet = options.headersGenerate;
|
|
82
94
|
this._headersGenerateFunc = Utils.createAsyncFunction(this.getHeadersGenerateSnippet(), {
|
|
83
95
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -104,8 +116,14 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
104
116
|
this._responseGenerateSnippet = options.responseGenerate;
|
|
105
117
|
this._responseGenerateFunc = Utils.createAsyncFunction(this.getResponseGenerateSnippet(), {
|
|
106
118
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
107
|
-
createDefault: () => async ($response: Response, _$factor: InFragment, _$request: PipelineStepData<In>,
|
|
108
|
-
|
|
119
|
+
createDefault: () => async ($response: Response, _$factor: InFragment, _$request: PipelineStepData<In>, $helpers: PipelineStepHelpers, _$: PipelineStepHelpers) => {
|
|
120
|
+
const contentEncoding = $response.headers?.get('content-encoding');
|
|
121
|
+
if (contentEncoding === 'zstd') {
|
|
122
|
+
const buffer = await $response.buffer();
|
|
123
|
+
return JSON.parse(await $helpers.$zstd(buffer));
|
|
124
|
+
} else {
|
|
125
|
+
return await $response.json();
|
|
126
|
+
}
|
|
109
127
|
},
|
|
110
128
|
getVariableNames: () => this.getResponseGenerateVariableName(),
|
|
111
129
|
error: (e: Error) => {
|
|
@@ -211,6 +229,23 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
211
229
|
return ['$endpointUrl', '$factor', '$request', ...this.getHelpersVariableNames()];
|
|
212
230
|
}
|
|
213
231
|
|
|
232
|
+
public getTransparentHeaderNames(): Array<string> {
|
|
233
|
+
return this._transparentHeaderNames ?? [];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
public getOmittedTransparentHeaderNames(): Array<string> {
|
|
237
|
+
return this._omittedTransparentHeaderNames ?? [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
protected generateTransparentHeaderNames(headerNames?: string, base?: Array<string>): Array<string> {
|
|
241
|
+
return [
|
|
242
|
+
...(base ?? []),
|
|
243
|
+
...new Set(`${headerNames || ''}`.split(';')
|
|
244
|
+
.map(x => x.trim())
|
|
245
|
+
.filter(x => x.length !== 0))
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
|
|
214
249
|
public getHeadersGenerateSnippet(): ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>> {
|
|
215
250
|
return this._headersGenerateSnippet;
|
|
216
251
|
}
|
|
@@ -256,7 +291,36 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
256
291
|
url = await this._urlGenerateFunc(this.getEndpointUrl(), data, request, $helpers, $helpers);
|
|
257
292
|
const method = this.getEndpointMethod();
|
|
258
293
|
const staticHeaders = this.getEndpointHeaders() ?? {};
|
|
259
|
-
const
|
|
294
|
+
const transparentHeaders = (this.getTransparentHeaderNames() ?? []).reduce((headers, name) => {
|
|
295
|
+
const value = Utils.getValue(data, name);
|
|
296
|
+
if (value == null) {
|
|
297
|
+
// no value of given header name, ignored
|
|
298
|
+
} else if (Array.isArray(value)) {
|
|
299
|
+
const headerValue = value.filter(v => v != null && `${v}`.length !== 0).join(', ');
|
|
300
|
+
if (headerValue.length !== 0) {
|
|
301
|
+
headers[name] = headerValue;
|
|
302
|
+
}
|
|
303
|
+
} else if (typeof value === 'object') {
|
|
304
|
+
Object.keys(value).forEach(key => {
|
|
305
|
+
const headerValue = value[key];
|
|
306
|
+
if (headerValue != null) {
|
|
307
|
+
const s = `${headerValue}`;
|
|
308
|
+
if (s.length !== 0) {
|
|
309
|
+
headers[key] = s;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
} else {
|
|
314
|
+
const headerValue = `${value}`;
|
|
315
|
+
if (headerValue.length !== 0) {
|
|
316
|
+
headers[name] = headerValue;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return headers;
|
|
320
|
+
}, {});
|
|
321
|
+
(this.getOmittedTransparentHeaderNames() ?? []).forEach(name => delete transparentHeaders[name]);
|
|
322
|
+
|
|
323
|
+
const generatedHeaders = await this._headersGenerateFunc(data, request, $helpers, $helpers) ?? {};
|
|
260
324
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
261
325
|
let body: any;
|
|
262
326
|
const bodyUsed = this.isBodyUsed();
|
|
@@ -268,8 +332,16 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
268
332
|
if (body != null && typeof body !== 'string') {
|
|
269
333
|
body = JSON.stringify(body);
|
|
270
334
|
}
|
|
335
|
+
const headers = {...staticHeaders, ...transparentHeaders, ...generatedHeaders};
|
|
336
|
+
// remove some headers, leave them to fetch to calculate automatically.
|
|
337
|
+
Object.keys(headers).filter(name => {
|
|
338
|
+
return ['content-encoding', 'content-length'].includes(name.toLowerCase());
|
|
339
|
+
}).forEach(name => {
|
|
340
|
+
delete headers[name];
|
|
341
|
+
});
|
|
342
|
+
|
|
271
343
|
const response = await fetch(url, {
|
|
272
|
-
method, headers
|
|
344
|
+
method, headers, body,
|
|
273
345
|
signal: this.needTimeout() ? (() => {
|
|
274
346
|
const controller = new AbortController();
|
|
275
347
|
setTimeout(() => controller.abort(), this.getEndpointTimeout());
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {ZstdInit} from '@oneidentity/zstd-js';
|
|
2
|
+
import {ZSTDDecoder} from 'zstddec';
|
|
3
|
+
|
|
4
|
+
test('ZStandard compression test', async () => {
|
|
5
|
+
const data = JSON.stringify({a: new Array(10).fill('1234567890').join('')});
|
|
6
|
+
const inputArray = new Uint8Array(Buffer.from(data));
|
|
7
|
+
|
|
8
|
+
const {ZstdSimple, ZstdStream} = await ZstdInit();
|
|
9
|
+
// Create some sample data to compress
|
|
10
|
+
/*
|
|
11
|
+
* The required parameter is the data
|
|
12
|
+
* It must be a Uint8Array
|
|
13
|
+
* */
|
|
14
|
+
const compressedSimpleData: Uint8Array = ZstdSimple.compress(inputArray, 3);
|
|
15
|
+
{
|
|
16
|
+
// console.time('@oneidentity/zstd-js');
|
|
17
|
+
// for (let i = 0; i < 10000; i++) {
|
|
18
|
+
// const {ZstdSimple} = await ZstdInit();
|
|
19
|
+
// const decompressedArray = ZstdSimple.decompress(compressedSimpleData);
|
|
20
|
+
// new TextDecoder().decode(decompressedArray);
|
|
21
|
+
// }
|
|
22
|
+
// console.timeEnd('@oneidentity/zstd-js');
|
|
23
|
+
|
|
24
|
+
const {ZstdSimple} = await ZstdInit();
|
|
25
|
+
const decompressedArray = ZstdSimple.decompress(compressedSimpleData);
|
|
26
|
+
const decompressedStr = new TextDecoder().decode(decompressedArray);
|
|
27
|
+
console.log(decompressedStr);
|
|
28
|
+
console.log(decompressedStr.length);
|
|
29
|
+
console.log(JSON.parse(decompressedStr));
|
|
30
|
+
}
|
|
31
|
+
{
|
|
32
|
+
console.time('zstddec');
|
|
33
|
+
for (let i = 0; i < 10; i++) {
|
|
34
|
+
const decoder = new ZSTDDecoder();
|
|
35
|
+
await decoder.init();
|
|
36
|
+
const decompressedArray = decoder.decode(compressedSimpleData);
|
|
37
|
+
const removeTrailingZeros = (arr: Uint8Array): Uint8Array => {
|
|
38
|
+
let i = arr.length;
|
|
39
|
+
while (i > 0 && arr[i - 1] === 0) {
|
|
40
|
+
i--;
|
|
41
|
+
}
|
|
42
|
+
return arr.slice(0, i);
|
|
43
|
+
};
|
|
44
|
+
new TextDecoder().decode(removeTrailingZeros(decompressedArray));
|
|
45
|
+
}
|
|
46
|
+
console.timeEnd('zstddec');
|
|
47
|
+
|
|
48
|
+
const decoder = new ZSTDDecoder();
|
|
49
|
+
await decoder.init();
|
|
50
|
+
const decompressedArray = decoder.decode(compressedSimpleData);
|
|
51
|
+
const removeTrailingZeros = (arr: Uint8Array): Uint8Array => {
|
|
52
|
+
let i = arr.length;
|
|
53
|
+
while (i > 0 && arr[i - 1] === 0) {
|
|
54
|
+
i--;
|
|
55
|
+
}
|
|
56
|
+
return arr.slice(0, i);
|
|
57
|
+
};
|
|
58
|
+
const decompressedStr = new TextDecoder().decode(removeTrailingZeros(decompressedArray));
|
|
59
|
+
console.log(decompressedStr);
|
|
60
|
+
console.log(decompressedStr.length);
|
|
61
|
+
console.log(JSON.parse(decompressedStr));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// const compressedStreamData: Uint8Array = ZstdStream.compress(inputArray);
|
|
65
|
+
}, 30000);
|