@rainbow-o23/n3 1.0.46 → 1.0.48
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 +22 -0
- package/index.cjs +123 -1
- package/index.js +123 -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 +13 -14
- package/src/lib/error-codes.ts +2 -0
- package/src/lib/step/abstract-fragmentary-pipeline-step.ts +0 -1
- package/src/lib/step/step-sets.ts +1 -0
- package/src/lib/step/types.ts +1 -1
- package/src/lib/step/utils.ts +4 -4
- package/src/lib/typeorm-step/index.ts +1 -0
- package/src/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.ts +165 -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
|
@@ -526,6 +526,28 @@ export interface TypeOrmLoadBasis extends TypeOrmBasis {
|
|
|
526
526
|
Array<TypeOrmEntityToLoad>;
|
|
527
527
|
```
|
|
528
528
|
|
|
529
|
+
#### Load Many by SQL, Use Cursor
|
|
530
|
+
|
|
531
|
+
##### Request and Response
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// request
|
|
535
|
+
export interface TypeOrmLoadBasis extends TypeOrmBasis {
|
|
536
|
+
params?: Array<TypeOrmEntityValue> | TypeOrmEntityToSave;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// response
|
|
540
|
+
Array<any>;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
By specifying `fetchSize`, each batch of data retrieved will execute sub-steps. Before executing the sub-steps, the data to be passed to it
|
|
544
|
+
will be calculated using the `streamTo` function. If `streamTo` is not specified, the batch of data retrieved itself will be passed to the
|
|
545
|
+
sub-steps. If the sub-steps is not specified, all retrieved data will be merged and returned.
|
|
546
|
+
|
|
547
|
+
Therefore, the number of times the sub-step is executed is related to the quantity of data and the `fetchSize`. Meanwhile, each time the
|
|
548
|
+
sub-step is invoked, the context will include a `$$typeOrmCursorRound` variable indicating the current batch (starting from 0), and a
|
|
549
|
+
`$typeOrmCursorEnd` variable indicating whether it is the last batch.
|
|
550
|
+
|
|
529
551
|
#### Save by SQL
|
|
530
552
|
|
|
531
553
|
##### 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,124 @@ 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?.close((e) => {
|
|
2301
|
+
this.getLogger().error(e);
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
catch (e) {
|
|
2305
|
+
this.getLogger().error(e);
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
const read = async ({ resolve, reject }) => {
|
|
2309
|
+
const readable = await runner.stream(sql, params, async () => {
|
|
2310
|
+
await close(readable);
|
|
2311
|
+
await pipe({ resolve, reject, end: true });
|
|
2312
|
+
}, async (e) => {
|
|
2313
|
+
await close(readable);
|
|
2314
|
+
reject(e);
|
|
2315
|
+
});
|
|
2316
|
+
readable.on('data', async (data) => {
|
|
2317
|
+
rows.push(data);
|
|
2318
|
+
await pipe({
|
|
2319
|
+
resolve, reject: async (e) => {
|
|
2320
|
+
await close(readable);
|
|
2321
|
+
reject(e);
|
|
2322
|
+
}, end: false
|
|
2323
|
+
});
|
|
2324
|
+
readable.read();
|
|
2325
|
+
});
|
|
2326
|
+
readable.read();
|
|
2327
|
+
};
|
|
2328
|
+
return new Promise((resolve, reject) => read({ resolve, reject }));
|
|
2329
|
+
}, request);
|
|
2330
|
+
}
|
|
2331
|
+
getFetchDataVariableName() {
|
|
2332
|
+
return '$factor';
|
|
2333
|
+
}
|
|
2334
|
+
getRequestVariableName() {
|
|
2335
|
+
return '$request';
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2219
2339
|
class TypeOrmSaveBySQLPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2220
2340
|
async doPerform(basis, request) {
|
|
2221
2341
|
const { sql, params } = this.getSql(basis, basis?.values);
|
|
@@ -2355,6 +2475,7 @@ exports.ERR_TYPEORM_DATASOURCE_TYPE_NOT_FOUND = ERR_TYPEORM_DATASOURCE_TYPE_NOT_
|
|
|
2355
2475
|
exports.ERR_TYPEORM_ENTITY_NOT_FOUND = ERR_TYPEORM_ENTITY_NOT_FOUND;
|
|
2356
2476
|
exports.ERR_TYPEORM_SQL_NOT_EMPTY = ERR_TYPEORM_SQL_NOT_EMPTY;
|
|
2357
2477
|
exports.ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY = ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY;
|
|
2478
|
+
exports.ERR_TYPEORM_STREAM = ERR_TYPEORM_STREAM;
|
|
2358
2479
|
exports.ERR_TYPEORM_TRANSACTION_NOT_FOUND = ERR_TYPEORM_TRANSACTION_NOT_FOUND;
|
|
2359
2480
|
exports.EachPipelineStepSets = EachPipelineStepSets;
|
|
2360
2481
|
exports.FetchPipelineStep = FetchPipelineStep;
|
|
@@ -2377,6 +2498,7 @@ exports.TypeOrmBySnippetPipelineStep = TypeOrmBySnippetPipelineStep;
|
|
|
2377
2498
|
exports.TypeOrmDataSourceHelper = TypeOrmDataSourceHelper;
|
|
2378
2499
|
exports.TypeOrmDataSourceManager = TypeOrmDataSourceManager;
|
|
2379
2500
|
exports.TypeOrmLoadManyBySQLPipelineStep = TypeOrmLoadManyBySQLPipelineStep;
|
|
2501
|
+
exports.TypeOrmLoadManyBySQLUseCursorPipelineStep = TypeOrmLoadManyBySQLUseCursorPipelineStep;
|
|
2380
2502
|
exports.TypeOrmLoadOneBySQLPipelineStep = TypeOrmLoadOneBySQLPipelineStep;
|
|
2381
2503
|
exports.TypeOrmParsedSQLCache = TypeOrmParsedSQLCache;
|
|
2382
2504
|
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,124 @@ 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?.close((e) => {
|
|
2299
|
+
this.getLogger().error(e);
|
|
2300
|
+
});
|
|
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
|
+
rows.push(data);
|
|
2316
|
+
await pipe({
|
|
2317
|
+
resolve, reject: async (e) => {
|
|
2318
|
+
await close(readable);
|
|
2319
|
+
reject(e);
|
|
2320
|
+
}, end: false
|
|
2321
|
+
});
|
|
2322
|
+
readable.read();
|
|
2323
|
+
});
|
|
2324
|
+
readable.read();
|
|
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
|
+
|
|
2217
2337
|
class TypeOrmSaveBySQLPipelineStep extends AbstractTypeOrmBySQLPipelineStep {
|
|
2218
2338
|
async doPerform(basis, request) {
|
|
2219
2339
|
const { sql, params } = this.getSql(basis, basis?.values);
|
|
@@ -2324,4 +2444,4 @@ class TypeOrmTransactionalPipelineStepSets extends PipelineStepSets {
|
|
|
2324
2444
|
}
|
|
2325
2445
|
}
|
|
2326
2446
|
|
|
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 };
|
|
2447
|
+
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.48",
|
|
4
4
|
"description": "o23 pipelines",
|
|
5
5
|
"main": "index.cjs",
|
|
6
6
|
"module": "index.js",
|
|
@@ -21,9 +21,9 @@
|
|
|
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.48",
|
|
25
25
|
"node-fetch": "2.6.7",
|
|
26
|
-
"typeorm": "^0.3.
|
|
26
|
+
"typeorm": "^0.3.20",
|
|
27
27
|
"typescript": "5.5.4"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
"@types/events": "^3.0.1",
|
|
37
37
|
"@types/node": "18.16.12",
|
|
38
38
|
"@types/node-fetch": "2.6.4",
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
40
|
-
"@typescript-eslint/parser": "^
|
|
41
|
-
"better-sqlite3": "^
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^8.13.0",
|
|
40
|
+
"@typescript-eslint/parser": "^8.13.0",
|
|
41
|
+
"better-sqlite3": "^11.5.0",
|
|
42
42
|
"eslint": "^9.8.0",
|
|
43
|
-
"mssql": "^
|
|
44
|
-
"mysql2": "^3.
|
|
45
|
-
"oracledb": "^6.
|
|
46
|
-
"pg": "^8.
|
|
47
|
-
"pg-query-stream": "^4.
|
|
43
|
+
"mssql": "^11.0.1",
|
|
44
|
+
"mysql2": "^3.11.4",
|
|
45
|
+
"oracledb": "^6.6.0",
|
|
46
|
+
"pg": "^8.13.1",
|
|
47
|
+
"pg-query-stream": "^4.7.1",
|
|
48
48
|
"reflect-metadata": "^0.2.2",
|
|
49
|
-
"rollup": "^3.
|
|
49
|
+
"rollup": "^3.29.5",
|
|
50
50
|
"rollup-plugin-tslint": "^0.2.2",
|
|
51
51
|
"rollup-plugin-typescript2": "^0.34.1",
|
|
52
52
|
"tslib": "^2.4.1"
|
|
@@ -74,7 +74,6 @@
|
|
|
74
74
|
"testEnvironment": "node"
|
|
75
75
|
},
|
|
76
76
|
"volta": {
|
|
77
|
-
"
|
|
78
|
-
"yarn": "1.22.21"
|
|
77
|
+
"extends": "../package.json"
|
|
79
78
|
}
|
|
80
79
|
}
|
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;
|
|
@@ -213,7 +213,6 @@ export abstract class AbstractFragmentaryPipelineStep<In = PipelineStepPayload,
|
|
|
213
213
|
};
|
|
214
214
|
}
|
|
215
215
|
} else if (typeof funcOrSnippet === 'string') {
|
|
216
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
217
216
|
const func = Utils.createAsyncFunction(funcOrSnippet, {
|
|
218
217
|
createDefault: async () => {
|
|
219
218
|
throw new UncatchableError(ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY, 'Cannot create perform func on empty snippet.');
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import {AbstractFragmentaryPipelineStep, FragmentaryPipelineStepOptions} from './abstract-fragmentary-pipeline-step';
|
|
9
9
|
import {Utils} from './utils';
|
|
10
10
|
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
11
12
|
export interface PipelineStepSetsContext {
|
|
12
13
|
}
|
|
13
14
|
|
package/src/lib/step/types.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '@rainbow-o23/n1';
|
|
8
8
|
|
|
9
9
|
export type ScriptFunctionBody = string;
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
11
11
|
export type ScriptFuncOrBody<F = Function> = F | ScriptFunctionBody;
|
|
12
12
|
|
|
13
13
|
export interface ErrorHandleOptions<In, InFragment, E extends Error = Error> {
|
package/src/lib/step/utils.ts
CHANGED
|
@@ -43,7 +43,7 @@ const AvoidProxyObjects = [
|
|
|
43
43
|
];
|
|
44
44
|
|
|
45
45
|
export class Utils {
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
47
47
|
public static createFunction<F = Function>(snippet: ScriptFuncOrBody<F>, creators: {
|
|
48
48
|
createDefault: () => Undefinable<F> | never;
|
|
49
49
|
getVariableNames: () => Array<string>;
|
|
@@ -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;
|
|
@@ -105,7 +105,7 @@ export class Utils {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
109
109
|
public static createSyncFunction<F = Function>(snippet: ScriptFuncOrBody<F>, creators: {
|
|
110
110
|
createDefault: () => Undefinable<F> | never;
|
|
111
111
|
getVariableNames: () => Array<string>;
|
|
@@ -114,7 +114,7 @@ export class Utils {
|
|
|
114
114
|
return Utils.createFunction(snippet, {...creators});
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
118
118
|
public static createAsyncFunction<F = Function>(snippet: ScriptFuncOrBody<F>, creators: {
|
|
119
119
|
createDefault: () => Undefinable<F> | never;
|
|
120
120
|
getVariableNames: () => Array<string>;
|
|
@@ -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,165 @@
|
|
|
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?.close((e) => {
|
|
121
|
+
// ignore this error
|
|
122
|
+
this.getLogger().error(e);
|
|
123
|
+
});
|
|
124
|
+
} catch (e) {
|
|
125
|
+
// ignore this error
|
|
126
|
+
this.getLogger().error(e);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const read = async ({resolve, reject}) => {
|
|
130
|
+
const readable = await runner.stream(sql, params, async () => {
|
|
131
|
+
// on end
|
|
132
|
+
await close(readable);
|
|
133
|
+
await pipe({resolve, reject, end: true});
|
|
134
|
+
}, async (e: Error) => {
|
|
135
|
+
// on error
|
|
136
|
+
await close(readable);
|
|
137
|
+
reject(e);
|
|
138
|
+
});
|
|
139
|
+
readable.on('data', async (data) => {
|
|
140
|
+
rows.push(data);
|
|
141
|
+
await pipe({
|
|
142
|
+
resolve, reject: async (e: Error) => {
|
|
143
|
+
await close(readable);
|
|
144
|
+
reject(e);
|
|
145
|
+
}, end: false
|
|
146
|
+
});
|
|
147
|
+
readable.read();
|
|
148
|
+
});
|
|
149
|
+
readable.read();
|
|
150
|
+
};
|
|
151
|
+
return new Promise<OutFragment>((resolve, reject) => read({resolve, reject}));
|
|
152
|
+
}, request);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* override this method when want to use another variable name rather than "$factor"
|
|
157
|
+
*/
|
|
158
|
+
protected getFetchDataVariableName(): string {
|
|
159
|
+
return '$factor';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected getRequestVariableName(): string {
|
|
163
|
+
return '$request';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -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
|
-
});
|