@rainbow-o23/n3 1.0.47 → 1.0.49
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 +28 -38
- package/index.cjs +121 -1
- package/index.js +121 -3
- package/lib/error-codes.d.ts +1 -0
- package/lib/typeorm-step/index.d.ts +1 -0
- package/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.d.ts +25 -0
- package/package.json +2 -2
- package/src/lib/error-codes.ts +2 -0
- package/src/lib/step/utils.ts +1 -1
- package/src/lib/typeorm-step/index.ts +1 -0
- package/src/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.ts +162 -0
- package/test/step/typeorm-by-cursor.test.ts +55 -0
- package/test/step/typeorm-by-sql-transactional.test.ts +11 -11
- package/test/step/typeorm-entity.test.ts +0 -50
package/README.md
CHANGED
|
@@ -398,44 +398,6 @@ Autonomous transactions take precedence over the transaction name, meaning that
|
|
|
398
398
|
specified by the transaction name will be ignored. If you need to use the transaction name, you must nest the pipeline steps within
|
|
399
399
|
transactional step sets, and ensure that the datasource name and transaction name remain the same.
|
|
400
400
|
|
|
401
|
-
### By Entity (Deprecated, not recommended)
|
|
402
|
-
|
|
403
|
-
#### Load Entity by ID
|
|
404
|
-
|
|
405
|
-
##### Constructor Parameters
|
|
406
|
-
|
|
407
|
-
| Name | Type | Default Value | Comments |
|
|
408
|
-
|------------|--------|---------------|----------------------|
|
|
409
|
-
| entityName | string | | TypeOrm entity name. |
|
|
410
|
-
|
|
411
|
-
##### Request and Response
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
// request
|
|
415
|
-
export type TypeOrmIdType = string | number | bigint;
|
|
416
|
-
// response
|
|
417
|
-
export type TypeOrmEntityToLoad = Undefinable<DeepPartial<ObjectLiteral>>;
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
#### Save Entity
|
|
421
|
-
|
|
422
|
-
##### Constructor Parameters
|
|
423
|
-
|
|
424
|
-
| Name | Type | Default Value | Comments |
|
|
425
|
-
|------------------------|----------------------------------------|---------------|----------------------|
|
|
426
|
-
| entityName | string | | TypeOrm entity name. |
|
|
427
|
-
| fillIdBySnowflake | boolean | false | |
|
|
428
|
-
| uniquenessCheckSnippet | ScriptFuncOrBody\<UniquenessCheckFunc> | | |
|
|
429
|
-
|
|
430
|
-
##### Request and Response
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
// request
|
|
434
|
-
export type EntityToSave = DeepPartial<ObjectLiteral>;
|
|
435
|
-
// response
|
|
436
|
-
export type EntityToSave = DeepPartial<ObjectLiteral>;
|
|
437
|
-
```
|
|
438
|
-
|
|
439
401
|
### By SQL
|
|
440
402
|
|
|
441
403
|
#### Environment Parameters
|
|
@@ -526,6 +488,34 @@ export interface TypeOrmLoadBasis extends TypeOrmBasis {
|
|
|
526
488
|
Array<TypeOrmEntityToLoad>;
|
|
527
489
|
```
|
|
528
490
|
|
|
491
|
+
#### Load Many by SQL, Use Cursor
|
|
492
|
+
|
|
493
|
+
##### Environment Parameters
|
|
494
|
+
|
|
495
|
+
| Name | Type | Default Value | Comments |
|
|
496
|
+
|-------------------------|--------|---------------|-------------|
|
|
497
|
+
| `typeorm.DB.fetch.size` | number | 20 | Fetch size. |
|
|
498
|
+
|
|
499
|
+
##### Request and Response
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// request
|
|
503
|
+
export interface TypeOrmLoadBasis extends TypeOrmBasis {
|
|
504
|
+
params?: Array<TypeOrmEntityValue> | TypeOrmEntityToSave;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// response
|
|
508
|
+
Array<any>;
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
By specifying `fetchSize`, each batch of data retrieved will execute sub-steps. Before executing the sub-steps, the data to be passed to it
|
|
512
|
+
will be calculated using the `streamTo` function. If `streamTo` is not specified, the batch of data retrieved itself will be passed to the
|
|
513
|
+
sub-steps. If the sub-steps is not specified, all retrieved data will be merged and returned.
|
|
514
|
+
|
|
515
|
+
Therefore, the number of times the sub-step is executed is related to the quantity of data and the `fetchSize`. Meanwhile, each time the
|
|
516
|
+
sub-step is invoked, the context will include a `$$typeOrmCursorRound` variable indicating the current batch (starting from 0), and a
|
|
517
|
+
`$typeOrmCursorEnd` variable indicating whether it is the last batch.
|
|
518
|
+
|
|
529
519
|
#### Save by SQL
|
|
530
520
|
|
|
531
521
|
##### Request and Response
|
package/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ const ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL = 'O03-00017';
|
|
|
25
25
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS = 'O03-00018';
|
|
26
26
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL = 'O03-00019';
|
|
27
27
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION = 'O03-00020';
|
|
28
|
+
const ERR_TYPEORM_STREAM = 'O03-00021';
|
|
28
29
|
n1.ErrorCodes.ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY;
|
|
29
30
|
n1.ErrorCodes.ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY;
|
|
30
31
|
n1.ErrorCodes.ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND = ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND;
|
|
@@ -45,6 +46,7 @@ n1.ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL = ERR_PIPELINE_SNIPPET_CANN
|
|
|
45
46
|
n1.ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS = ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS;
|
|
46
47
|
n1.ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL = ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL;
|
|
47
48
|
n1.ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION = ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION;
|
|
49
|
+
n1.ErrorCodes.ERR_TYPEORM_STREAM = ERR_TYPEORM_STREAM;
|
|
48
50
|
|
|
49
51
|
class AbstractTypeOrmDataSource {
|
|
50
52
|
_name;
|
|
@@ -392,7 +394,7 @@ class Utils {
|
|
|
392
394
|
suppressOutputPathCheck: false,
|
|
393
395
|
skipLibCheck: true,
|
|
394
396
|
skipDefaultLibCheck: true,
|
|
395
|
-
moduleResolution: ts.ModuleResolutionKind.
|
|
397
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext
|
|
396
398
|
}
|
|
397
399
|
});
|
|
398
400
|
snippet = transpiled.outputText;
|
|
@@ -2216,6 +2218,122 @@ class TypeOrmLoadManyBySQLPipelineStep extends AbstractTypeOrmLoadBySQLPipelineS
|
|
|
2216
2218
|
}
|
|
2217
2219
|
}
|
|
2218
2220
|
|
|
2221
|
+
class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2222
|
+
_fetchSize;
|
|
2223
|
+
_streamToSnippet;
|
|
2224
|
+
_streamToFunc;
|
|
2225
|
+
_stepBuilders;
|
|
2226
|
+
constructor(options) {
|
|
2227
|
+
super(options);
|
|
2228
|
+
const config = this.getConfig();
|
|
2229
|
+
this._fetchSize = options.fetchSize ?? config.getNumber(`typeorm.${this.getDataSourceName()}.fetch.size`, 20);
|
|
2230
|
+
this._streamToSnippet = options.streamTo;
|
|
2231
|
+
this._streamToFunc = Utils.createAsyncFunction(this.getStreamToSnippet(), {
|
|
2232
|
+
createDefault: () => (async ($factor) => $factor),
|
|
2233
|
+
getVariableNames: () => this.generateVariableNames(),
|
|
2234
|
+
error: (e) => {
|
|
2235
|
+
this.getLogger().error(`Failed on create function for snippet[${this.getStreamToSnippet()}].`);
|
|
2236
|
+
throw e;
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
this._stepBuilders = options.steps;
|
|
2240
|
+
}
|
|
2241
|
+
getFetchSize() {
|
|
2242
|
+
return this._fetchSize;
|
|
2243
|
+
}
|
|
2244
|
+
getStreamToSnippet() {
|
|
2245
|
+
return this._streamToSnippet;
|
|
2246
|
+
}
|
|
2247
|
+
generateVariableNames() {
|
|
2248
|
+
return [this.getFetchDataVariableName(), this.getRequestVariableName(), ...this.getHelpersVariableNames()];
|
|
2249
|
+
}
|
|
2250
|
+
getStepBuilders() {
|
|
2251
|
+
return this._stepBuilders ?? [];
|
|
2252
|
+
}
|
|
2253
|
+
async doPerform(basis, request) {
|
|
2254
|
+
const { sql, params } = this.getSql(basis, basis?.params);
|
|
2255
|
+
return await this.autoTrans(async (runner) => {
|
|
2256
|
+
const results = [];
|
|
2257
|
+
const rows = [];
|
|
2258
|
+
let cursorRound = 0;
|
|
2259
|
+
const pipe = async ({ resolve, reject, end }) => {
|
|
2260
|
+
if (!end && rows.length < this.getFetchSize()) {
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
try {
|
|
2264
|
+
const contentForSub = await this._streamToFunc([...rows], request, this.getHelpers(), this.getHelpers());
|
|
2265
|
+
rows.length = 0;
|
|
2266
|
+
let resultContent;
|
|
2267
|
+
if (this.getStepBuilders().length === 0) {
|
|
2268
|
+
resultContent = contentForSub;
|
|
2269
|
+
}
|
|
2270
|
+
else {
|
|
2271
|
+
const sets = new PipelineStepSets({
|
|
2272
|
+
...this.buildStepOptions(), name: this.getName(), steps: this.getStepBuilders()
|
|
2273
|
+
});
|
|
2274
|
+
const { content: _, $context, ...rest } = request;
|
|
2275
|
+
const contextForSub = { ...$context, $typeOrmCursorRound: cursorRound, $typeOrmCursorEnd: end };
|
|
2276
|
+
const requestForSub = { ...rest, $context: contextForSub, content: contentForSub };
|
|
2277
|
+
const result = await sets.perform(requestForSub);
|
|
2278
|
+
const { content } = result;
|
|
2279
|
+
resultContent = content;
|
|
2280
|
+
}
|
|
2281
|
+
cursorRound = cursorRound + 1;
|
|
2282
|
+
if (resultContent == null || resultContent == n1.PIPELINE_STEP_RETURN_NULL) {
|
|
2283
|
+
}
|
|
2284
|
+
else if (Array.isArray(resultContent)) {
|
|
2285
|
+
results.push(...resultContent);
|
|
2286
|
+
}
|
|
2287
|
+
else {
|
|
2288
|
+
results.push(resultContent);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
catch (e) {
|
|
2292
|
+
reject(e);
|
|
2293
|
+
}
|
|
2294
|
+
if (end) {
|
|
2295
|
+
resolve(results);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
const close = async (readable) => {
|
|
2299
|
+
try {
|
|
2300
|
+
readable?.destroy();
|
|
2301
|
+
}
|
|
2302
|
+
catch (e) {
|
|
2303
|
+
this.getLogger().error(e);
|
|
2304
|
+
}
|
|
2305
|
+
};
|
|
2306
|
+
const read = async ({ resolve, reject }) => {
|
|
2307
|
+
const readable = await runner.stream(sql, params, async () => {
|
|
2308
|
+
await close(readable);
|
|
2309
|
+
await pipe({ resolve, reject, end: true });
|
|
2310
|
+
}, async (e) => {
|
|
2311
|
+
await close(readable);
|
|
2312
|
+
reject(e);
|
|
2313
|
+
});
|
|
2314
|
+
readable.on('data', async (data) => {
|
|
2315
|
+
readable.pause();
|
|
2316
|
+
rows.push(data);
|
|
2317
|
+
await pipe({
|
|
2318
|
+
resolve, reject: async (e) => {
|
|
2319
|
+
await close(readable);
|
|
2320
|
+
reject(e);
|
|
2321
|
+
}, end: false
|
|
2322
|
+
});
|
|
2323
|
+
readable.resume();
|
|
2324
|
+
});
|
|
2325
|
+
};
|
|
2326
|
+
return new Promise((resolve, reject) => read({ resolve, reject }));
|
|
2327
|
+
}, request);
|
|
2328
|
+
}
|
|
2329
|
+
getFetchDataVariableName() {
|
|
2330
|
+
return '$factor';
|
|
2331
|
+
}
|
|
2332
|
+
getRequestVariableName() {
|
|
2333
|
+
return '$request';
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2219
2337
|
class TypeOrmSaveBySQLPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2220
2338
|
async doPerform(basis, request) {
|
|
2221
2339
|
const { sql, params } = this.getSql(basis, basis?.values);
|
|
@@ -2355,6 +2473,7 @@ exports.ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND = ERR_TYPEORM_DATASOURCE_TYPE_NOT_
|
|
|
2355
2473
|
exports.ERR_TYPEORM_ENTITY_NOT_FOUND = ERR_TYPEORM_ENTITY_NOT_FOUND;
|
|
2356
2474
|
exports.ERR_TYPEORM_SQL_NOT_EMPTY = ERR_TYPEORM_SQL_NOT_EMPTY;
|
|
2357
2475
|
exports.ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY = ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY;
|
|
2476
|
+
exports.ERR_TYPEORM_STREAM = ERR_TYPEORM_STREAM;
|
|
2358
2477
|
exports.ERR_TYPEORM_TRANSACTION_NOT_FOUND = ERR_TYPEORM_TRANSACTION_NOT_FOUND;
|
|
2359
2478
|
exports.EachPipelineStepSets = EachPipelineStepSets;
|
|
2360
2479
|
exports.FetchPipelineStep = FetchPipelineStep;
|
|
@@ -2377,6 +2496,7 @@ exports.TypeOrmBySnippetPipelineStep = TypeOrmBySnippetPipelineStep;
|
|
|
2377
2496
|
exports.TypeOrmDataSourceHelper = TypeOrmDataSourceHelper;
|
|
2378
2497
|
exports.TypeOrmDataSourceManager = TypeOrmDataSourceManager;
|
|
2379
2498
|
exports.TypeOrmLoadManyBySQLPipelineStep = TypeOrmLoadManyBySQLPipelineStep;
|
|
2499
|
+
exports.TypeOrmLoadManyBySQLUseCursorPipelineStep = TypeOrmLoadManyBySQLUseCursorPipelineStep;
|
|
2380
2500
|
exports.TypeOrmLoadOneBySQLPipelineStep = TypeOrmLoadOneBySQLPipelineStep;
|
|
2381
2501
|
exports.TypeOrmParsedSQLCache = TypeOrmParsedSQLCache;
|
|
2382
2502
|
exports.TypeOrmSaveBySQLPipelineStep = TypeOrmSaveBySQLPipelineStep;
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorCodes, UncatchableError, AbstractPipelineStep, CatchableError, ExposedUncatchableError, ERR_UNKNOWN, PipelineRepository, StepHelpersUtils } from '@rainbow-o23/n1';
|
|
1
|
+
import { ErrorCodes, UncatchableError, AbstractPipelineStep, CatchableError, ExposedUncatchableError, ERR_UNKNOWN, PipelineRepository, StepHelpersUtils, PIPELINE_STEP_RETURN_NULL } from '@rainbow-o23/n1';
|
|
2
2
|
import { DataSource } from 'typeorm';
|
|
3
3
|
import ts, { ScriptTarget, JsxEmit, ModuleKind, ModuleResolutionKind } from 'typescript';
|
|
4
4
|
import fetch from 'node-fetch';
|
|
@@ -23,6 +23,7 @@ const ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL = 'O03-00017';
|
|
|
23
23
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS = 'O03-00018';
|
|
24
24
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL = 'O03-00019';
|
|
25
25
|
const ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION = 'O03-00020';
|
|
26
|
+
const ERR_TYPEORM_STREAM = 'O03-00021';
|
|
26
27
|
ErrorCodes.ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY;
|
|
27
28
|
ErrorCodes.ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY;
|
|
28
29
|
ErrorCodes.ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND = ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND;
|
|
@@ -43,6 +44,7 @@ ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL = ERR_PIPELINE_SNIPPET_CANNOT_
|
|
|
43
44
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS = ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS;
|
|
44
45
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL = ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL;
|
|
45
46
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION = ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION;
|
|
47
|
+
ErrorCodes.ERR_TYPEORM_STREAM = ERR_TYPEORM_STREAM;
|
|
46
48
|
|
|
47
49
|
class AbstractTypeOrmDataSource {
|
|
48
50
|
_name;
|
|
@@ -390,7 +392,7 @@ class Utils {
|
|
|
390
392
|
suppressOutputPathCheck: false,
|
|
391
393
|
skipLibCheck: true,
|
|
392
394
|
skipDefaultLibCheck: true,
|
|
393
|
-
moduleResolution: ModuleResolutionKind.
|
|
395
|
+
moduleResolution: ModuleResolutionKind.NodeNext
|
|
394
396
|
}
|
|
395
397
|
});
|
|
396
398
|
snippet = transpiled.outputText;
|
|
@@ -2214,6 +2216,122 @@ class TypeOrmLoadManyBySQLPipelineStep extends AbstractTypeOrmLoadBySQLPipelineS
|
|
|
2214
2216
|
}
|
|
2215
2217
|
}
|
|
2216
2218
|
|
|
2219
|
+
class TypeOrmLoadManyBySQLUseCursorPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2220
|
+
_fetchSize;
|
|
2221
|
+
_streamToSnippet;
|
|
2222
|
+
_streamToFunc;
|
|
2223
|
+
_stepBuilders;
|
|
2224
|
+
constructor(options) {
|
|
2225
|
+
super(options);
|
|
2226
|
+
const config = this.getConfig();
|
|
2227
|
+
this._fetchSize = options.fetchSize ?? config.getNumber(`typeorm.${this.getDataSourceName()}.fetch.size`, 20);
|
|
2228
|
+
this._streamToSnippet = options.streamTo;
|
|
2229
|
+
this._streamToFunc = Utils.createAsyncFunction(this.getStreamToSnippet(), {
|
|
2230
|
+
createDefault: () => (async ($factor) => $factor),
|
|
2231
|
+
getVariableNames: () => this.generateVariableNames(),
|
|
2232
|
+
error: (e) => {
|
|
2233
|
+
this.getLogger().error(`Failed on create function for snippet[${this.getStreamToSnippet()}].`);
|
|
2234
|
+
throw e;
|
|
2235
|
+
}
|
|
2236
|
+
});
|
|
2237
|
+
this._stepBuilders = options.steps;
|
|
2238
|
+
}
|
|
2239
|
+
getFetchSize() {
|
|
2240
|
+
return this._fetchSize;
|
|
2241
|
+
}
|
|
2242
|
+
getStreamToSnippet() {
|
|
2243
|
+
return this._streamToSnippet;
|
|
2244
|
+
}
|
|
2245
|
+
generateVariableNames() {
|
|
2246
|
+
return [this.getFetchDataVariableName(), this.getRequestVariableName(), ...this.getHelpersVariableNames()];
|
|
2247
|
+
}
|
|
2248
|
+
getStepBuilders() {
|
|
2249
|
+
return this._stepBuilders ?? [];
|
|
2250
|
+
}
|
|
2251
|
+
async doPerform(basis, request) {
|
|
2252
|
+
const { sql, params } = this.getSql(basis, basis?.params);
|
|
2253
|
+
return await this.autoTrans(async (runner) => {
|
|
2254
|
+
const results = [];
|
|
2255
|
+
const rows = [];
|
|
2256
|
+
let cursorRound = 0;
|
|
2257
|
+
const pipe = async ({ resolve, reject, end }) => {
|
|
2258
|
+
if (!end && rows.length < this.getFetchSize()) {
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
try {
|
|
2262
|
+
const contentForSub = await this._streamToFunc([...rows], request, this.getHelpers(), this.getHelpers());
|
|
2263
|
+
rows.length = 0;
|
|
2264
|
+
let resultContent;
|
|
2265
|
+
if (this.getStepBuilders().length === 0) {
|
|
2266
|
+
resultContent = contentForSub;
|
|
2267
|
+
}
|
|
2268
|
+
else {
|
|
2269
|
+
const sets = new PipelineStepSets({
|
|
2270
|
+
...this.buildStepOptions(), name: this.getName(), steps: this.getStepBuilders()
|
|
2271
|
+
});
|
|
2272
|
+
const { content: _, $context, ...rest } = request;
|
|
2273
|
+
const contextForSub = { ...$context, $typeOrmCursorRound: cursorRound, $typeOrmCursorEnd: end };
|
|
2274
|
+
const requestForSub = { ...rest, $context: contextForSub, content: contentForSub };
|
|
2275
|
+
const result = await sets.perform(requestForSub);
|
|
2276
|
+
const { content } = result;
|
|
2277
|
+
resultContent = content;
|
|
2278
|
+
}
|
|
2279
|
+
cursorRound = cursorRound + 1;
|
|
2280
|
+
if (resultContent == null || resultContent == PIPELINE_STEP_RETURN_NULL) {
|
|
2281
|
+
}
|
|
2282
|
+
else if (Array.isArray(resultContent)) {
|
|
2283
|
+
results.push(...resultContent);
|
|
2284
|
+
}
|
|
2285
|
+
else {
|
|
2286
|
+
results.push(resultContent);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
catch (e) {
|
|
2290
|
+
reject(e);
|
|
2291
|
+
}
|
|
2292
|
+
if (end) {
|
|
2293
|
+
resolve(results);
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
const close = async (readable) => {
|
|
2297
|
+
try {
|
|
2298
|
+
readable?.destroy();
|
|
2299
|
+
}
|
|
2300
|
+
catch (e) {
|
|
2301
|
+
this.getLogger().error(e);
|
|
2302
|
+
}
|
|
2303
|
+
};
|
|
2304
|
+
const read = async ({ resolve, reject }) => {
|
|
2305
|
+
const readable = await runner.stream(sql, params, async () => {
|
|
2306
|
+
await close(readable);
|
|
2307
|
+
await pipe({ resolve, reject, end: true });
|
|
2308
|
+
}, async (e) => {
|
|
2309
|
+
await close(readable);
|
|
2310
|
+
reject(e);
|
|
2311
|
+
});
|
|
2312
|
+
readable.on('data', async (data) => {
|
|
2313
|
+
readable.pause();
|
|
2314
|
+
rows.push(data);
|
|
2315
|
+
await pipe({
|
|
2316
|
+
resolve, reject: async (e) => {
|
|
2317
|
+
await close(readable);
|
|
2318
|
+
reject(e);
|
|
2319
|
+
}, end: false
|
|
2320
|
+
});
|
|
2321
|
+
readable.resume();
|
|
2322
|
+
});
|
|
2323
|
+
};
|
|
2324
|
+
return new Promise((resolve, reject) => read({ resolve, reject }));
|
|
2325
|
+
}, request);
|
|
2326
|
+
}
|
|
2327
|
+
getFetchDataVariableName() {
|
|
2328
|
+
return '$factor';
|
|
2329
|
+
}
|
|
2330
|
+
getRequestVariableName() {
|
|
2331
|
+
return '$request';
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2217
2335
|
class TypeOrmSaveBySQLPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2218
2336
|
async doPerform(basis, request) {
|
|
2219
2337
|
const { sql, params } = this.getSql(basis, basis?.values);
|
|
@@ -2324,4 +2442,4 @@ class TypeOrmTransactionalPipelineStepSets extends PipelineStepSets {
|
|
|
2324
2442
|
}
|
|
2325
2443
|
}
|
|
2326
2444
|
|
|
2327
|
-
export { AbstractFragmentaryPipelineStep, AbstractTypeOrmBySQLPipelineStep, AbstractTypeOrmDataSource, AbstractTypeOrmLoadBySQLPipelineStep, AbstractTypeOrmPipelineStep, AsyncPipelineStepSets, BetterSqlite3TypeOrmDatasource, ConditionalPipelineStepSets, DEFAULT_TRANSACTION_NAME, DeletePropertyPipelineStep, ERR_EACH_FRAGMENT_NOT_ANY_ARRAY, ERR_FETCH_ERROR, ERR_PIPELINE_REF_NOT_EMPTY, ERR_PIPELINE_REF_NOT_FOUND, ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL, ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION, ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL, ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS, ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY, ERR_PIPELINE_STEP_METHOD_NOT_SUPPORTED, ERR_PIPELINE_STEP_REF_NOT_EMPTY, ERR_PIPELINE_STEP_REF_NOT_FOUND, ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY, ERR_TYPEORM_DATASOURCE_CREATOR_NOT_FOUND, ERR_TYPEORM_DATASOURCE_NOT_FOUND, ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND, ERR_TYPEORM_ENTITY_NOT_FOUND, ERR_TYPEORM_SQL_NOT_EMPTY, ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY, ERR_TYPEORM_TRANSACTION_NOT_FOUND, EachPipelineStepSets, FetchPipelineStep, GetPropertyPipelineStep, HttpAbortErrorCode, HttpUnknownErrorCode, MssqlTypeOrmDatasource, MysqlTypeOrmDatasource, OracleTypeOrmDatasource, ParallelPipelineStepSets, ParsedSqlSegmentType, PgsqlTypeOrmDatasource, PipelineStepSets, RefPipelinePipelineStep, RefStepPipelineStep, RoutesPipelineStepSets, SnippetPipelineStep, SnowflakePipelineStep, SupportedDataSourceTypes, TypeOrmBulkSaveBySQLPipelineStep, TypeOrmBySnippetPipelineStep, TypeOrmDataSourceHelper, TypeOrmDataSourceManager, TypeOrmLoadManyBySQLPipelineStep, TypeOrmLoadOneBySQLPipelineStep, TypeOrmParsedSQLCache, TypeOrmSaveBySQLPipelineStep, TypeOrmTransactionalPipelineStepSets, Utils };
|
|
2445
|
+
export { AbstractFragmentaryPipelineStep, AbstractTypeOrmBySQLPipelineStep, AbstractTypeOrmDataSource, AbstractTypeOrmLoadBySQLPipelineStep, AbstractTypeOrmPipelineStep, AsyncPipelineStepSets, BetterSqlite3TypeOrmDatasource, ConditionalPipelineStepSets, DEFAULT_TRANSACTION_NAME, DeletePropertyPipelineStep, ERR_EACH_FRAGMENT_NOT_ANY_ARRAY, ERR_FETCH_ERROR, ERR_PIPELINE_REF_NOT_EMPTY, ERR_PIPELINE_REF_NOT_FOUND, ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL, ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION, ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL, ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS, ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY, ERR_PIPELINE_STEP_METHOD_NOT_SUPPORTED, ERR_PIPELINE_STEP_REF_NOT_EMPTY, ERR_PIPELINE_STEP_REF_NOT_FOUND, ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY, ERR_TYPEORM_DATASOURCE_CREATOR_NOT_FOUND, ERR_TYPEORM_DATASOURCE_NOT_FOUND, ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND, ERR_TYPEORM_ENTITY_NOT_FOUND, ERR_TYPEORM_SQL_NOT_EMPTY, ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY, ERR_TYPEORM_STREAM, ERR_TYPEORM_TRANSACTION_NOT_FOUND, EachPipelineStepSets, FetchPipelineStep, GetPropertyPipelineStep, HttpAbortErrorCode, HttpUnknownErrorCode, MssqlTypeOrmDatasource, MysqlTypeOrmDatasource, OracleTypeOrmDatasource, ParallelPipelineStepSets, ParsedSqlSegmentType, PgsqlTypeOrmDatasource, PipelineStepSets, RefPipelinePipelineStep, RefStepPipelineStep, RoutesPipelineStepSets, SnippetPipelineStep, SnowflakePipelineStep, SupportedDataSourceTypes, TypeOrmBulkSaveBySQLPipelineStep, TypeOrmBySnippetPipelineStep, TypeOrmDataSourceHelper, TypeOrmDataSourceManager, TypeOrmLoadManyBySQLPipelineStep, TypeOrmLoadManyBySQLUseCursorPipelineStep, TypeOrmLoadOneBySQLPipelineStep, TypeOrmParsedSQLCache, TypeOrmSaveBySQLPipelineStep, TypeOrmTransactionalPipelineStepSets, Utils };
|
package/lib/error-codes.d.ts
CHANGED
|
@@ -19,3 +19,4 @@ export declare const ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL: O23ReservedErrorCod
|
|
|
19
19
|
export declare const ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS: O23ReservedErrorCode;
|
|
20
20
|
export declare const ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL: O23ReservedErrorCode;
|
|
21
21
|
export declare const ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION: O23ReservedErrorCode;
|
|
22
|
+
export declare const ERR_TYPEORM_STREAM: O23ReservedErrorCode;
|
|
@@ -5,6 +5,7 @@ export * from './abstract-typeorm-by-sql-step';
|
|
|
5
5
|
export * from './abstract-typeorm-load-by-sql-step';
|
|
6
6
|
export * from './typeorm-load-one-by-sql-step';
|
|
7
7
|
export * from './typeorm-load-many-by-sql-step';
|
|
8
|
+
export * from './typeorm-load-many-by-sql-use-cursor-step';
|
|
8
9
|
export * from './typeorm-save-by-sql-step';
|
|
9
10
|
export * from './typeorm-bulk-save-by-sql-step';
|
|
10
11
|
export * from './typeorm-transactional-step-sets';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PipelineStepBuilder, PipelineStepData, PipelineStepHelpers, PipelineStepPayload, Undefinable } from '@rainbow-o23/n1';
|
|
2
|
+
import { ScriptFuncOrBody } from '../step';
|
|
3
|
+
import { AbstractTypeOrmBySQLPipelineStep, TypeOrmBySQLPipelineStepOptions } from './abstract-typeorm-by-sql-step';
|
|
4
|
+
import { TypeOrmLoadBasis } from './abstract-typeorm-load-by-sql-step';
|
|
5
|
+
export type StreamToSubSteps = any;
|
|
6
|
+
export type StreamToFunc<In> = ($factor: Array<any>, $request: PipelineStepData<In>, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => Promise<StreamToSubSteps>;
|
|
7
|
+
export interface TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = TypeOrmLoadBasis, OutFragment = Out> extends TypeOrmBySQLPipelineStepOptions<In, Out, InFragment, OutFragment> {
|
|
8
|
+
fetchSize?: number;
|
|
9
|
+
streamTo?: ScriptFuncOrBody<StreamToFunc<In>>;
|
|
10
|
+
steps?: Array<PipelineStepBuilder>;
|
|
11
|
+
}
|
|
12
|
+
export declare class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = Undefinable<TypeOrmLoadBasis>, OutFragment = Out> extends AbstractTypeOrmBySQLPipelineStep<In, Out, Undefinable<TypeOrmLoadBasis>, OutFragment> {
|
|
13
|
+
private readonly _fetchSize;
|
|
14
|
+
private readonly _streamToSnippet;
|
|
15
|
+
private readonly _streamToFunc;
|
|
16
|
+
private readonly _stepBuilders;
|
|
17
|
+
constructor(options: TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In, Out, InFragment, OutFragment>);
|
|
18
|
+
protected getFetchSize(): number;
|
|
19
|
+
getStreamToSnippet(): ScriptFuncOrBody<StreamToFunc<In>>;
|
|
20
|
+
protected generateVariableNames(): Array<string>;
|
|
21
|
+
protected getStepBuilders(): Array<PipelineStepBuilder>;
|
|
22
|
+
protected doPerform(basis: Undefinable<TypeOrmLoadBasis>, request: PipelineStepData<In>): Promise<Undefinable<OutFragment>>;
|
|
23
|
+
protected getFetchDataVariableName(): string;
|
|
24
|
+
protected getRequestVariableName(): string;
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainbow-o23/n3",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.49",
|
|
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.49",
|
|
25
25
|
"node-fetch": "2.6.7",
|
|
26
26
|
"typeorm": "^0.3.20",
|
|
27
27
|
"typescript": "5.5.4"
|
package/src/lib/error-codes.ts
CHANGED
|
@@ -21,6 +21,7 @@ export const ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL: O23ReservedErrorCode = 'O03
|
|
|
21
21
|
export const ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS: O23ReservedErrorCode = 'O03-00018';
|
|
22
22
|
export const ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL: O23ReservedErrorCode = 'O03-00019';
|
|
23
23
|
export const ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION: O23ReservedErrorCode = 'O03-00020';
|
|
24
|
+
export const ERR_TYPEORM_STREAM: O23ReservedErrorCode = 'O03-00021';
|
|
24
25
|
|
|
25
26
|
ErrorCodes.ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY;
|
|
26
27
|
ErrorCodes.ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY = ERR_PIPELINE_STEP_CONDITIONAL_SNIPPET_NOT_EMPTY;
|
|
@@ -43,3 +44,4 @@ ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_GLOBAL = ERR_PIPELINE_SNIPPET_CANNOT_
|
|
|
43
44
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS = ERR_PIPELINE_SNIPPET_CANNOT_USE_PROCESS;
|
|
44
45
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL = ERR_PIPELINE_SNIPPET_CANNOT_USE_EVAL;
|
|
45
46
|
ErrorCodes.ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION = ERR_PIPELINE_SNIPPET_CANNOT_USE_FUNCTION;
|
|
47
|
+
ErrorCodes.ERR_TYPEORM_STREAM = ERR_TYPEORM_STREAM;
|
package/src/lib/step/utils.ts
CHANGED
|
@@ -70,7 +70,7 @@ export class Utils {
|
|
|
70
70
|
suppressOutputPathCheck: false,
|
|
71
71
|
skipLibCheck: true,
|
|
72
72
|
skipDefaultLibCheck: true,
|
|
73
|
-
moduleResolution: ModuleResolutionKind.
|
|
73
|
+
moduleResolution: ModuleResolutionKind.NodeNext // default use node next
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
snippet = transpiled.outputText;
|
|
@@ -8,6 +8,7 @@ export * from './abstract-typeorm-by-sql-step';
|
|
|
8
8
|
export * from './abstract-typeorm-load-by-sql-step';
|
|
9
9
|
export * from './typeorm-load-one-by-sql-step';
|
|
10
10
|
export * from './typeorm-load-many-by-sql-step';
|
|
11
|
+
export * from './typeorm-load-many-by-sql-use-cursor-step';
|
|
11
12
|
|
|
12
13
|
export * from './typeorm-save-by-sql-step';
|
|
13
14
|
export * from './typeorm-bulk-save-by-sql-step';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PIPELINE_STEP_RETURN_NULL,
|
|
3
|
+
PipelineStepBuilder,
|
|
4
|
+
PipelineStepData,
|
|
5
|
+
PipelineStepHelpers,
|
|
6
|
+
PipelineStepPayload,
|
|
7
|
+
Undefinable
|
|
8
|
+
} from '@rainbow-o23/n1';
|
|
9
|
+
import {ReadStream} from 'fs';
|
|
10
|
+
import {PipelineStepSets, ScriptFuncOrBody, Utils} from '../step';
|
|
11
|
+
import {AbstractTypeOrmBySQLPipelineStep, TypeOrmBySQLPipelineStepOptions} from './abstract-typeorm-by-sql-step';
|
|
12
|
+
import {TypeOrmLoadBasis} from './abstract-typeorm-load-by-sql-step';
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
export type StreamToSubSteps = any;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
export type StreamToFunc<In> = ($factor: Array<any>, $request: PipelineStepData<In>, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => Promise<StreamToSubSteps>;
|
|
18
|
+
|
|
19
|
+
export interface TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = TypeOrmLoadBasis, OutFragment = Out>
|
|
20
|
+
extends TypeOrmBySQLPipelineStepOptions<In, Out, InFragment, OutFragment> {
|
|
21
|
+
fetchSize?: number;
|
|
22
|
+
streamTo?: ScriptFuncOrBody<StreamToFunc<In>>;
|
|
23
|
+
steps?: Array<PipelineStepBuilder>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = Undefinable<TypeOrmLoadBasis>, OutFragment = Out>
|
|
27
|
+
extends AbstractTypeOrmBySQLPipelineStep<In, Out, Undefinable<TypeOrmLoadBasis>, OutFragment> {
|
|
28
|
+
private readonly _fetchSize: number;
|
|
29
|
+
private readonly _streamToSnippet: ScriptFuncOrBody<StreamToFunc<In>>;
|
|
30
|
+
private readonly _streamToFunc: StreamToFunc<In>;
|
|
31
|
+
private readonly _stepBuilders: Array<PipelineStepBuilder>;
|
|
32
|
+
|
|
33
|
+
public constructor(options: TypeOrmLoadManyBySQLUseCursorPipelineStepOptions<In, Out, InFragment, OutFragment>) {
|
|
34
|
+
super(options);
|
|
35
|
+
const config = this.getConfig();
|
|
36
|
+
this._fetchSize = options.fetchSize ?? config.getNumber(`typeorm.${this.getDataSourceName()}.fetch.size`, 20);
|
|
37
|
+
this._streamToSnippet = options.streamTo;
|
|
38
|
+
this._streamToFunc = Utils.createAsyncFunction(this.getStreamToSnippet(), {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
createDefault: () => (async ($factor: Array<any>) => $factor),
|
|
41
|
+
getVariableNames: () => this.generateVariableNames(),
|
|
42
|
+
error: (e: Error) => {
|
|
43
|
+
this.getLogger().error(`Failed on create function for snippet[${this.getStreamToSnippet()}].`);
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this._stepBuilders = options.steps;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected getFetchSize(): number {
|
|
51
|
+
return this._fetchSize;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public getStreamToSnippet(): ScriptFuncOrBody<StreamToFunc<In>> {
|
|
55
|
+
return this._streamToSnippet;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected generateVariableNames(): Array<string> {
|
|
59
|
+
return [this.getFetchDataVariableName(), this.getRequestVariableName(), ...this.getHelpersVariableNames()];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected getStepBuilders(): Array<PipelineStepBuilder> {
|
|
63
|
+
return this._stepBuilders ?? [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected async doPerform(basis: Undefinable<TypeOrmLoadBasis>, request: PipelineStepData<In>): Promise<Undefinable<OutFragment>> {
|
|
67
|
+
const {sql, params} = this.getSql(basis, basis?.params);
|
|
68
|
+
return await this.autoTrans<OutFragment>(async (runner) => {
|
|
69
|
+
const results = [];
|
|
70
|
+
const rows = [];
|
|
71
|
+
let cursorRound = 0;
|
|
72
|
+
const pipe = async ({resolve, reject, end}) => {
|
|
73
|
+
if (!end && rows.length < this.getFetchSize()) {
|
|
74
|
+
// not end, and size not meet the fresh required
|
|
75
|
+
// do nothing, wait for next
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
// get data from cache
|
|
80
|
+
const contentForSub = await this._streamToFunc([...rows], request, this.getHelpers(), this.getHelpers());
|
|
81
|
+
// clear cache
|
|
82
|
+
rows.length = 0;
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
let resultContent: any;
|
|
85
|
+
if (this.getStepBuilders().length === 0) {
|
|
86
|
+
// no sub step, use content as result
|
|
87
|
+
resultContent = contentForSub;
|
|
88
|
+
} else {
|
|
89
|
+
// create a step sets to run
|
|
90
|
+
const sets = new PipelineStepSets({
|
|
91
|
+
...this.buildStepOptions(), name: this.getName(), steps: this.getStepBuilders()
|
|
92
|
+
});
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
94
|
+
const {content: _, $context, ...rest} = request;
|
|
95
|
+
// pass a cursor end indicator to sub steps
|
|
96
|
+
const contextForSub = {...$context, $typeOrmCursorRound: cursorRound, $typeOrmCursorEnd: end};
|
|
97
|
+
const requestForSub = {...rest, $context: contextForSub, content: contentForSub};
|
|
98
|
+
const result = await sets.perform(requestForSub);
|
|
99
|
+
const {content} = result;
|
|
100
|
+
resultContent = content;
|
|
101
|
+
}
|
|
102
|
+
cursorRound = cursorRound + 1;
|
|
103
|
+
if (resultContent == null || resultContent == PIPELINE_STEP_RETURN_NULL) {
|
|
104
|
+
// ignore
|
|
105
|
+
} else if (Array.isArray(resultContent)) {
|
|
106
|
+
results.push(...resultContent);
|
|
107
|
+
} else {
|
|
108
|
+
results.push(resultContent);
|
|
109
|
+
}
|
|
110
|
+
} catch (e) {
|
|
111
|
+
reject(e);
|
|
112
|
+
}
|
|
113
|
+
if (end) {
|
|
114
|
+
resolve(results);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const close = async (readable: ReadStream) => {
|
|
118
|
+
// never throw exception from this function
|
|
119
|
+
try {
|
|
120
|
+
readable?.destroy();
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// ignore this error
|
|
123
|
+
this.getLogger().error(e);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const read = async ({resolve, reject}) => {
|
|
127
|
+
const readable = await runner.stream(sql, params, async () => {
|
|
128
|
+
// on end
|
|
129
|
+
await close(readable);
|
|
130
|
+
await pipe({resolve, reject, end: true});
|
|
131
|
+
}, async (e: Error) => {
|
|
132
|
+
// on error
|
|
133
|
+
await close(readable);
|
|
134
|
+
reject(e);
|
|
135
|
+
});
|
|
136
|
+
readable.on('data', async (data) => {
|
|
137
|
+
readable.pause();
|
|
138
|
+
rows.push(data);
|
|
139
|
+
await pipe({
|
|
140
|
+
resolve, reject: async (e: Error) => {
|
|
141
|
+
await close(readable);
|
|
142
|
+
reject(e);
|
|
143
|
+
}, end: false
|
|
144
|
+
});
|
|
145
|
+
readable.resume();
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
return new Promise<OutFragment>((resolve, reject) => read({resolve, reject}));
|
|
149
|
+
}, request);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* override this method when want to use another variable name rather than "$factor"
|
|
154
|
+
*/
|
|
155
|
+
protected getFetchDataVariableName(): string {
|
|
156
|
+
return '$factor';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected getRequestVariableName(): string {
|
|
160
|
+
return '$request';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {createConfig, createLogger} from '@rainbow-o23/n1';
|
|
2
|
+
import {TypeOrmDataSourceHelper, TypeOrmLoadManyBySQLUseCursorPipelineStep} from '../../src';
|
|
3
|
+
|
|
4
|
+
const logger = createLogger();
|
|
5
|
+
const config = createConfig(logger);
|
|
6
|
+
|
|
7
|
+
describe('TypeORM Cursor Suite', () => {
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
process.env.CFG_TYPEORM_TEST_HOST = 'localhost';
|
|
10
|
+
process.env.CFG_TYPEORM_TEST_USERNAME = 'o23';
|
|
11
|
+
process.env.CFG_TYPEORM_TEST_PASSWORD = 'o23';
|
|
12
|
+
const type = 'pg' + 'sql';
|
|
13
|
+
if (type === 'mysql') {
|
|
14
|
+
process.env.CFG_TYPEORM_TEST_TYPE = 'mysql';
|
|
15
|
+
process.env.CFG_TYPEORM_TEST_PORT = '3306';
|
|
16
|
+
process.env.CFG_TYPEORM_TEST_DATABASE = 'o23';
|
|
17
|
+
} else if (type === 'pgsql') {
|
|
18
|
+
process.env.CFG_TYPEORM_TEST_TYPE = 'pgsql';
|
|
19
|
+
process.env.CFG_TYPEORM_TEST_PORT = '5432';
|
|
20
|
+
process.env.CFG_TYPEORM_TEST_DATABASE = 'postgres';
|
|
21
|
+
process.env.CFG_TYPEORM_TEST_SCHEMA = 'o23';
|
|
22
|
+
} else if (type === 'mssql') {
|
|
23
|
+
process.env.CFG_TYPEORM_TEST_TYPE = 'mssql';
|
|
24
|
+
process.env.CFG_TYPEORM_TEST_PORT = '1433';
|
|
25
|
+
process.env.CFG_TYPEORM_TEST_PASSWORD = 'o23O23o23!';
|
|
26
|
+
process.env.CFG_TYPEORM_TEST_DATABASE = 'o23';
|
|
27
|
+
process.env.CFG_TYPEORM_TEST_SCHEMA = 'dbo';
|
|
28
|
+
process.env.CFG_TYPEORM_TEST_TRUST_SERVER_CERTIFICATE = 'true';
|
|
29
|
+
} else {
|
|
30
|
+
process.env.CFG_TYPEORM_TEST_TYPE = 'oracle';
|
|
31
|
+
process.env.CFG_TYPEORM_TEST_HOST = '127.0.0.1';
|
|
32
|
+
process.env.CFG_TYPEORM_TEST_PORT = '1521';
|
|
33
|
+
process.env.CFG_TYPEORM_TEST_SERVICE_NAME = 'orcl';
|
|
34
|
+
}
|
|
35
|
+
// process.env.CFG_TYPEORM_TEST_LOGGING = 'true';
|
|
36
|
+
await new TypeOrmDataSourceHelper(config).create({});
|
|
37
|
+
// const repo = (await TypeOrmDataSourceManager.findDataSource('TEST', config)).getDataSource().getRepository(TestTable);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('TypeORM load many using cursor #1', async () => {
|
|
41
|
+
// noinspection SqlResolve
|
|
42
|
+
const step = new TypeOrmLoadManyBySQLUseCursorPipelineStep<any, any>({
|
|
43
|
+
config, logger, dataSourceName: 'TEST', sql: 'SELECT * FROM T_O23_DB_CHANGE_LOG',
|
|
44
|
+
autonomous: true
|
|
45
|
+
});
|
|
46
|
+
const request = {content: (void 0)};
|
|
47
|
+
const response = await step.perform(request);
|
|
48
|
+
expect(response.content).not.toBeNull();
|
|
49
|
+
console.log(response.content);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterAll((done) => {
|
|
53
|
+
done();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -70,10 +70,10 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
70
70
|
logger,
|
|
71
71
|
dataSourceName: 'TEST',
|
|
72
72
|
sql: 'SELECT ID id, CONTENT content FROM T_TEST_TABLE WHERE ID = ?',
|
|
73
|
-
fromRequest: ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
73
|
+
fromRequest: async ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
74
74
|
return {params: [$factor.idToLoad]} as TypeOrmBasis;
|
|
75
75
|
},
|
|
76
|
-
toResponse: ($result: TestTable, _$request: PipelineStepData) => {
|
|
76
|
+
toResponse: async ($result: TestTable, _$request: PipelineStepData) => {
|
|
77
77
|
expect($result.id).toBe(1);
|
|
78
78
|
expect($result.content).toBe('hello world!');
|
|
79
79
|
return {loadedById: $result};
|
|
@@ -87,10 +87,10 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
87
87
|
logger,
|
|
88
88
|
dataSourceName: 'TEST',
|
|
89
89
|
sql: 'INSERT INTO T_TEST_TABLE(ID, CONTENT) VALUES (?, ?)',
|
|
90
|
-
fromRequest: ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
90
|
+
fromRequest: async ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
91
91
|
return {values: [$factor.item3.id, $factor.item3.content]};
|
|
92
92
|
},
|
|
93
|
-
toResponse: ($result: TypeOrmIdOfInserted, _$request: PipelineStepData) => {
|
|
93
|
+
toResponse: async ($result: TypeOrmIdOfInserted, _$request: PipelineStepData) => {
|
|
94
94
|
expect($result).toBe(3);
|
|
95
95
|
return {insertedId: $result};
|
|
96
96
|
},
|
|
@@ -104,10 +104,10 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
104
104
|
dataSourceName: 'TEST',
|
|
105
105
|
autonomous: true,
|
|
106
106
|
sql: 'SELECT ID id, CONTENT content FROM T_TEST_TABLE WHERE ID = ?',
|
|
107
|
-
fromRequest: ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
107
|
+
fromRequest: async ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
108
108
|
return {params: [$factor.item3.id]};
|
|
109
109
|
},
|
|
110
|
-
toResponse: ($result: Undefinable<TestTable>, $request: PipelineStepData) => {
|
|
110
|
+
toResponse: async ($result: Undefinable<TestTable>, $request: PipelineStepData) => {
|
|
111
111
|
expect($result).toBeUndefined();
|
|
112
112
|
return $request.content;
|
|
113
113
|
}
|
|
@@ -116,10 +116,10 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
116
116
|
{
|
|
117
117
|
create: async ({config, logger}) => new TypeOrmSaveBySQLPipelineStep({
|
|
118
118
|
config, logger, dataSourceName: 'TEST', sql: 'UPDATE T_TEST_TABLE SET CONTENT = ? WHERE ID = ?',
|
|
119
|
-
fromRequest: ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
119
|
+
fromRequest: async ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
120
120
|
return {values: [$factor.item3ChangeTo.content, $factor.item3ChangeTo.id]};
|
|
121
121
|
},
|
|
122
|
-
toResponse: ($result: TypeOrmCountOfAffected, _$request: PipelineStepData) => {
|
|
122
|
+
toResponse: async ($result: TypeOrmCountOfAffected, _$request: PipelineStepData) => {
|
|
123
123
|
// DON'T KNOW WHY THIS IS 3, SEEMS SHOULD BE 1 ACCORDING TO BETTER-SQLITE3 DOCUMENT
|
|
124
124
|
// BUT CURRENTLY IT RETURNS COUNT OF THIS TABLE, NOT IMPACTED ROW COUNT
|
|
125
125
|
expect($result).toBe(3);
|
|
@@ -134,14 +134,14 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
134
134
|
logger,
|
|
135
135
|
dataSourceName: 'TEST',
|
|
136
136
|
sql: 'INSERT INTO T_TEST_TABLE(ID, CONTENT) VALUES (?, ?)',
|
|
137
|
-
fromRequest: ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
137
|
+
fromRequest: async ($factor: TransactionalRequest, _$request: PipelineStepData<TransactionalRequest>) => {
|
|
138
138
|
return {
|
|
139
139
|
items: [
|
|
140
140
|
[$factor.item4.id, $factor.item4.content], [$factor.item5.id, $factor.item5.content]
|
|
141
141
|
]
|
|
142
142
|
};
|
|
143
143
|
},
|
|
144
|
-
toResponse: ($result: TypeOrmIdsOfInserted, _$request: PipelineStepData) => {
|
|
144
|
+
toResponse: async ($result: TypeOrmIdsOfInserted, _$request: PipelineStepData) => {
|
|
145
145
|
return {insertedIds: $result};
|
|
146
146
|
},
|
|
147
147
|
mergeRequest: true
|
|
@@ -150,7 +150,7 @@ class TransactionalTestStepSets extends TypeOrmTransactionalPipelineStepSets {
|
|
|
150
150
|
{
|
|
151
151
|
create: async ({config, logger}) => new TypeOrmLoadManyBySQLPipelineStep({
|
|
152
152
|
config, logger, dataSourceName: 'TEST', sql: 'SELECT ID id, CONTENT content FROM T_TEST_TABLE',
|
|
153
|
-
toResponse: ($result: Array<TestTable>, _$request: PipelineStepData) => {
|
|
153
|
+
toResponse: async ($result: Array<TestTable>, _$request: PipelineStepData) => {
|
|
154
154
|
return {all: $result};
|
|
155
155
|
},
|
|
156
156
|
mergeRequest: true
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import {createConfig, createLogger} from '@rainbow-o23/n1';
|
|
2
|
-
import {Column, Entity, PrimaryColumn} from 'typeorm';
|
|
3
|
-
import {TypeOrmDataSourceHelper, TypeOrmDataSourceManager, TypeOrmLoadEntityByIdPipelineStep} from '../../src';
|
|
4
|
-
|
|
5
|
-
const logger = createLogger();
|
|
6
|
-
const config = createConfig(logger);
|
|
7
|
-
|
|
8
|
-
// decorators are leading syntax errors in ide, since the test folder is not included in tsconfig.json
|
|
9
|
-
// but the tricky thing is, once test folder included into tsconfig.json, d.ts files will be created in src folder
|
|
10
|
-
// which cause currently use ts-ignore to avoid this syntax errors
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
@Entity({name: 'T_TEST_TABLE'})
|
|
13
|
-
export class TestTable {
|
|
14
|
-
// @ts-ignore
|
|
15
|
-
@PrimaryColumn('bigint', {name: 'ID'})
|
|
16
|
-
id: number;
|
|
17
|
-
// @ts-ignore
|
|
18
|
-
@Column('varchar', {name: 'CONTENT'})
|
|
19
|
-
content: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('TypeORM Entity Suite', () => {
|
|
23
|
-
beforeAll(async () => {
|
|
24
|
-
process.env.CFG_TYPEORM_TEST_TYPE = 'better-sqlite3';
|
|
25
|
-
process.env.CFG_TYPEORM_TEST_SYNCHRONIZE = 'true';
|
|
26
|
-
// in memory, kept in global cache, to make sure always use same one.
|
|
27
|
-
process.env.CFG_TYPEORM_TEST_KEPT_ON_GLOBAL = 'true';
|
|
28
|
-
await new TypeOrmDataSourceHelper(config).create({
|
|
29
|
-
'TEST': [TestTable]
|
|
30
|
-
});
|
|
31
|
-
const repo = (await TypeOrmDataSourceManager.findDataSource('TEST', config)).getDataSource().getRepository(TestTable);
|
|
32
|
-
await repo.insert({id: 1, content: 'hello world!'});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('TypeORM load entity by id Pipeline Step Test #1', async () => {
|
|
36
|
-
const step = new TypeOrmLoadEntityByIdPipelineStep({
|
|
37
|
-
config, logger, dataSourceName: 'TEST', entityName: 'TestTable',
|
|
38
|
-
autonomous: true
|
|
39
|
-
});
|
|
40
|
-
const request = {content: 1};
|
|
41
|
-
const response = await step.perform(request);
|
|
42
|
-
expect(response.content).not.toBeNull();
|
|
43
|
-
expect(response.content.id).toBe(1);
|
|
44
|
-
expect(response.content.content).toBe('hello world!');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterAll((done) => {
|
|
48
|
-
done();
|
|
49
|
-
});
|
|
50
|
-
});
|