@rainbow-o23/n3 1.0.56 → 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 +75 -22
- package/index.cjs +78 -6
- package/index.js +78 -6
- package/lib/http/fetch-step.d.ts +7 -0
- package/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.d.ts +3 -0
- package/package.json +3 -2
- package/src/lib/http/fetch-step.ts +76 -4
- package/src/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.ts +16 -2
- package/test/step/typeorm-by-cursor.test.ts +2 -1
- package/test/step/zstd.test.ts +65 -0
package/README.md
CHANGED
|
@@ -492,9 +492,16 @@ Array<TypeOrmEntityToLoad>;
|
|
|
492
492
|
|
|
493
493
|
##### Environment Parameters
|
|
494
494
|
|
|
495
|
-
| Name
|
|
496
|
-
|
|
497
|
-
| `typeorm.DB.fetch.size`
|
|
495
|
+
| Name | Type | Default Value | Comments |
|
|
496
|
+
|-----------------------------------|---------|---------------|----------------------------------------|
|
|
497
|
+
| `typeorm.DB.fetch.size` | number | 20 | Fetch size. |
|
|
498
|
+
| `typeorm.DB.stream.pause.enabled` | boolean | false | Pause and resume result stream or not. |
|
|
499
|
+
|
|
500
|
+
##### Constructor Parameters
|
|
501
|
+
|
|
502
|
+
| Name | Type | Default Value | Comments |
|
|
503
|
+
|--------------------|---------|---------------|----------------------------------------|
|
|
504
|
+
| pauseStreamEnabled | boolean | | Pause and resume result stream or not. |
|
|
498
505
|
|
|
499
506
|
##### Request and Response
|
|
500
507
|
|
|
@@ -585,13 +592,17 @@ step set. Additionally, nested transactions are also supported, which means Tran
|
|
|
585
592
|
|
|
586
593
|
#### Environment Parameters
|
|
587
594
|
|
|
588
|
-
| Name
|
|
589
|
-
|
|
590
|
-
| `endpoints.SYSTEM.ENDPOINT.url`
|
|
591
|
-
| `endpoints.SYSTEM.ENDPOINT.headers`
|
|
592
|
-
| `endpoints.SYSTEM.global.headers`
|
|
593
|
-
| `endpoints.SYSTEM.ENDPOINT.
|
|
594
|
-
| `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. |
|
|
595
606
|
|
|
596
607
|
`SYSTEM` represents endpoint system, `ENDPOINT` represents endpoint url. For example:
|
|
597
608
|
|
|
@@ -604,18 +615,60 @@ CFG_ENDPOINTS_ORDER_PAYMENT_URL=https://order.com/payment
|
|
|
604
615
|
|
|
605
616
|
#### Constructor Parameters
|
|
606
617
|
|
|
607
|
-
| Name
|
|
608
|
-
|
|
609
|
-
| endpointSystemCode
|
|
610
|
-
| endpointName
|
|
611
|
-
| urlGenerate
|
|
612
|
-
| method
|
|
613
|
-
| timeout
|
|
614
|
-
|
|
|
615
|
-
|
|
|
616
|
-
|
|
|
617
|
-
|
|
|
618
|
-
|
|
|
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`.
|
|
619
672
|
|
|
620
673
|
## Installation
|
|
621
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());
|
|
@@ -2223,6 +2286,7 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2223
2286
|
_streamToSnippet;
|
|
2224
2287
|
_streamToFunc;
|
|
2225
2288
|
_stepBuilders;
|
|
2289
|
+
_pauseStreamEnabled;
|
|
2226
2290
|
constructor(options) {
|
|
2227
2291
|
super(options);
|
|
2228
2292
|
const config = this.getConfig();
|
|
@@ -2237,6 +2301,7 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2237
2301
|
}
|
|
2238
2302
|
});
|
|
2239
2303
|
this._stepBuilders = options.steps;
|
|
2304
|
+
this._pauseStreamEnabled = options.pauseStreamEnabled ?? config.getBoolean(`typeorm.${this.getDataSourceName()}.stream.pause.enabled`, false);
|
|
2240
2305
|
}
|
|
2241
2306
|
getFetchSize() {
|
|
2242
2307
|
return this._fetchSize;
|
|
@@ -2250,6 +2315,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2250
2315
|
getStepBuilders() {
|
|
2251
2316
|
return this._stepBuilders ?? [];
|
|
2252
2317
|
}
|
|
2318
|
+
isPauseStreamEnabled() {
|
|
2319
|
+
return this._pauseStreamEnabled;
|
|
2320
|
+
}
|
|
2253
2321
|
async doPerform(basis, request) {
|
|
2254
2322
|
const { sql, params } = this.getSql(basis, basis?.params);
|
|
2255
2323
|
return await this.autoTrans(async (runner) => {
|
|
@@ -2312,7 +2380,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2312
2380
|
reject(e);
|
|
2313
2381
|
});
|
|
2314
2382
|
readable.on('data', async (data) => {
|
|
2315
|
-
|
|
2383
|
+
if (this.isPauseStreamEnabled()) {
|
|
2384
|
+
readable.pause();
|
|
2385
|
+
}
|
|
2316
2386
|
rows.push(data);
|
|
2317
2387
|
await pipe({
|
|
2318
2388
|
resolve, reject: async (e) => {
|
|
@@ -2320,7 +2390,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2320
2390
|
reject(e);
|
|
2321
2391
|
}, end: false
|
|
2322
2392
|
});
|
|
2323
|
-
|
|
2393
|
+
if (this.isPauseStreamEnabled()) {
|
|
2394
|
+
readable.resume();
|
|
2395
|
+
}
|
|
2324
2396
|
});
|
|
2325
2397
|
};
|
|
2326
2398
|
return new Promise((resolve, reject) => read({ resolve, reject }));
|
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());
|
|
@@ -2221,6 +2284,7 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2221
2284
|
_streamToSnippet;
|
|
2222
2285
|
_streamToFunc;
|
|
2223
2286
|
_stepBuilders;
|
|
2287
|
+
_pauseStreamEnabled;
|
|
2224
2288
|
constructor(options) {
|
|
2225
2289
|
super(options);
|
|
2226
2290
|
const config = this.getConfig();
|
|
@@ -2235,6 +2299,7 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2235
2299
|
}
|
|
2236
2300
|
});
|
|
2237
2301
|
this._stepBuilders = options.steps;
|
|
2302
|
+
this._pauseStreamEnabled = options.pauseStreamEnabled ?? config.getBoolean(`typeorm.${this.getDataSourceName()}.stream.pause.enabled`, false);
|
|
2238
2303
|
}
|
|
2239
2304
|
getFetchSize() {
|
|
2240
2305
|
return this._fetchSize;
|
|
@@ -2248,6 +2313,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2248
2313
|
getStepBuilders() {
|
|
2249
2314
|
return this._stepBuilders ?? [];
|
|
2250
2315
|
}
|
|
2316
|
+
isPauseStreamEnabled() {
|
|
2317
|
+
return this._pauseStreamEnabled;
|
|
2318
|
+
}
|
|
2251
2319
|
async doPerform(basis, request) {
|
|
2252
2320
|
const { sql, params } = this.getSql(basis, basis?.params);
|
|
2253
2321
|
return await this.autoTrans(async (runner) => {
|
|
@@ -2310,7 +2378,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2310
2378
|
reject(e);
|
|
2311
2379
|
});
|
|
2312
2380
|
readable.on('data', async (data) => {
|
|
2313
|
-
|
|
2381
|
+
if (this.isPauseStreamEnabled()) {
|
|
2382
|
+
readable.pause();
|
|
2383
|
+
}
|
|
2314
2384
|
rows.push(data);
|
|
2315
2385
|
await pipe({
|
|
2316
2386
|
resolve, reject: async (e) => {
|
|
@@ -2318,7 +2388,9 @@ class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipe
|
|
|
2318
2388
|
reject(e);
|
|
2319
2389
|
}, end: false
|
|
2320
2390
|
});
|
|
2321
|
-
|
|
2391
|
+
if (this.isPauseStreamEnabled()) {
|
|
2392
|
+
readable.resume();
|
|
2393
|
+
}
|
|
2322
2394
|
});
|
|
2323
2395
|
};
|
|
2324
2396
|
return new Promise((resolve, reject) => read({ resolve, reject }));
|
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;
|
|
@@ -8,17 +8,20 @@ export interface TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In = PipelineS
|
|
|
8
8
|
fetchSize?: number;
|
|
9
9
|
streamTo?: ScriptFuncOrBody<StreamToFunc<In, Item>>;
|
|
10
10
|
steps?: Array<PipelineStepBuilder>;
|
|
11
|
+
pauseStreamEnabled?: boolean;
|
|
11
12
|
}
|
|
12
13
|
export declare class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = Undefinable<TypeOrmLoadBasis>, OutFragment = Out, Item = any> extends AbstractTypeOrmBySQLPipelineStep<In, Out, Undefinable<TypeOrmLoadBasis>, OutFragment> {
|
|
13
14
|
private readonly _fetchSize;
|
|
14
15
|
private readonly _streamToSnippet;
|
|
15
16
|
private readonly _streamToFunc;
|
|
16
17
|
private readonly _stepBuilders;
|
|
18
|
+
private readonly _pauseStreamEnabled;
|
|
17
19
|
constructor(options: TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In, Out, InFragment, OutFragment>);
|
|
18
20
|
protected getFetchSize(): number;
|
|
19
21
|
getStreamToSnippet(): ScriptFuncOrBody<StreamToFunc<In, Item>>;
|
|
20
22
|
protected generateVariableNames(): Array<string>;
|
|
21
23
|
protected getStepBuilders(): Array<PipelineStepBuilder>;
|
|
24
|
+
protected isPauseStreamEnabled(): boolean;
|
|
22
25
|
protected doPerform(basis: Undefinable<TypeOrmLoadBasis>, request: PipelineStepData<In>): Promise<Undefinable<OutFragment>>;
|
|
23
26
|
protected getFetchDataVariableName(): string;
|
|
24
27
|
protected getRequestVariableName(): string;
|
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());
|
|
@@ -22,6 +22,7 @@ export interface TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In = PipelineS
|
|
|
22
22
|
fetchSize?: number;
|
|
23
23
|
streamTo?: ScriptFuncOrBody<StreamToFunc<In, Item>>;
|
|
24
24
|
steps?: Array<PipelineStepBuilder>;
|
|
25
|
+
pauseStreamEnabled?: boolean;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -31,6 +32,7 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
31
32
|
private readonly _streamToSnippet: ScriptFuncOrBody<StreamToFunc<In, Item>>;
|
|
32
33
|
private readonly _streamToFunc: StreamToFunc<In, Item>;
|
|
33
34
|
private readonly _stepBuilders: Array<PipelineStepBuilder>;
|
|
35
|
+
private readonly _pauseStreamEnabled: boolean;
|
|
34
36
|
|
|
35
37
|
public constructor(options: TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In, Out, InFragment, OutFragment>) {
|
|
36
38
|
super(options);
|
|
@@ -47,6 +49,10 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
47
49
|
}
|
|
48
50
|
});
|
|
49
51
|
this._stepBuilders = options.steps;
|
|
52
|
+
// for unknown reason, in some environment if the pause and resume functions of readable invoked,
|
|
53
|
+
// only the first row are returns and end event invoked immediately
|
|
54
|
+
// so default disable it.
|
|
55
|
+
this._pauseStreamEnabled = options.pauseStreamEnabled ?? config.getBoolean(`typeorm.${this.getDataSourceName()}.stream.pause.enabled`, false);
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
protected getFetchSize(): number {
|
|
@@ -65,6 +71,10 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
65
71
|
return this._stepBuilders ?? [];
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
protected isPauseStreamEnabled(): boolean {
|
|
75
|
+
return this._pauseStreamEnabled;
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
protected async doPerform(basis: Undefinable<TypeOrmLoadBasis>, request: PipelineStepData<In>): Promise<Undefinable<OutFragment>> {
|
|
69
79
|
const {sql, params} = this.getSql(basis, basis?.params);
|
|
70
80
|
return await this.autoTrans<OutFragment>(async (runner) => {
|
|
@@ -136,7 +146,9 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
136
146
|
reject(e);
|
|
137
147
|
});
|
|
138
148
|
readable.on('data', async (data) => {
|
|
139
|
-
|
|
149
|
+
if (this.isPauseStreamEnabled()) {
|
|
150
|
+
readable.pause();
|
|
151
|
+
}
|
|
140
152
|
rows.push(data);
|
|
141
153
|
await pipe({
|
|
142
154
|
resolve, reject: async (e: Error) => {
|
|
@@ -144,7 +156,9 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
144
156
|
reject(e);
|
|
145
157
|
}, end: false
|
|
146
158
|
});
|
|
147
|
-
|
|
159
|
+
if (this.isPauseStreamEnabled()) {
|
|
160
|
+
readable.resume();
|
|
161
|
+
}
|
|
148
162
|
});
|
|
149
163
|
};
|
|
150
164
|
return new Promise<OutFragment>((resolve, reject) => read({resolve, reject}));
|
|
@@ -9,7 +9,8 @@ describe('TypeORM Cursor Suite', () => {
|
|
|
9
9
|
process.env.CFG_TYPEORM_TEST_HOST = 'localhost';
|
|
10
10
|
process.env.CFG_TYPEORM_TEST_USERNAME = 'o23';
|
|
11
11
|
process.env.CFG_TYPEORM_TEST_PASSWORD = 'o23';
|
|
12
|
-
|
|
12
|
+
// process.env.CFG_TYPEORM_TEST_STREAM_PAUSE_ENABLED = 'true'
|
|
13
|
+
const type = 'my' + 'sql';
|
|
13
14
|
if (type === 'mysql') {
|
|
14
15
|
process.env.CFG_TYPEORM_TEST_TYPE = 'mysql';
|
|
15
16
|
process.env.CFG_TYPEORM_TEST_PORT = '3306';
|
|
@@ -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);
|