@rainbow-o23/n3 0.1.21 → 1.0.23

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 CHANGED
@@ -171,6 +171,29 @@ const RequestContent = {$content: content, $item: element, $semaphore};
171
171
  If you need to terminate the loop prematurely, you just need to return the `$semaphore` signal from the request data. The loop will
172
172
  automatically end, and the collected processing results will be gathered and returned as an array.
173
173
 
174
+ ### Parallel
175
+
176
+ #### Constructor Parameters
177
+
178
+ | Name | Type | Default Value | Comments |
179
+ |-----------|-------------------------------------------------------------------|---------------|-----------------------------------------------|
180
+ | cloneData | ScriptFuncOrBody\<CloneDataFunc\<In, InFragment, EachInFragment>> | | Clone request data for each step. |
181
+ | race | boolean | false | Returns first settled result if race is true. |
182
+
183
+ The specified set of pipeline steps will be executed parallel, and
184
+
185
+ - All the execution results will be gathered into an array and returned when `race` is false,
186
+ - Returns the first settled result when `race` is true,
187
+
188
+ It is important to note that
189
+
190
+ - Each sub pipeline step will use the same request data, they share the same content memory address. So please be very
191
+ careful <span style='color: red;'>**NOT**</span> to attempt to modify the request data in the sub pipeline steps. Alternatively, you can
192
+ use `cloneData` to create a copy of the request data for each sub pipeline steps, so that request data operations can be modified in
193
+ certain sub pipeline steps without affecting other sub pipeline steps.
194
+ - The error handles of the `For Each` pipeline step will be used for each iteration of executing individual elements, rather than being
195
+ applied to the execution of the array as a whole.
196
+
174
197
  ### Trigger Pipeline
175
198
 
176
199
  #### Constructor Parameters
package/index.cjs CHANGED
@@ -701,7 +701,7 @@ class PipelineStepSets extends AbstractFragmentaryPipelineStep {
701
701
  const request = await promise;
702
702
  return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
703
703
  .execute(async () => {
704
- this.traceStepOut(traceId, step, request);
704
+ this.traceStepIn(traceId, step, request);
705
705
  const response = await step.perform({ ...request, $context: { ...context, traceId } });
706
706
  this.traceStepOut(traceId, step, response);
707
707
  return this.returnOrContinueOrClear(request, response);
@@ -719,6 +719,67 @@ class AsyncPipelineStepSets extends PipelineStepSets {
719
719
  }
720
720
  }
721
721
 
722
+ class ParallelPipelineStepSets extends PipelineStepSets {
723
+ _cloneDataSnippet;
724
+ _cloneData;
725
+ _race;
726
+ constructor(options) {
727
+ super(options);
728
+ this._cloneDataSnippet = options.cloneData;
729
+ this._cloneData = Utils.createSyncFunction(this.getCloneDataSnippet(), {
730
+ createDefault: () => ($factor, _$request, _$helpers, _$) => {
731
+ return $factor;
732
+ },
733
+ getVariableNames: () => ['$factor', '$request', ...this.getHelpersVariableNames()],
734
+ error: (e) => {
735
+ this.getLogger().error(`Failed on create function for clone data, snippet is [${this.getCloneDataSnippet()}].`);
736
+ throw e;
737
+ }
738
+ });
739
+ this._race = options.race ?? false;
740
+ }
741
+ getCloneDataSnippet() {
742
+ return this._cloneDataSnippet;
743
+ }
744
+ raceOne() {
745
+ return this._race;
746
+ }
747
+ cloneDataForEach($factor, $request) {
748
+ if ($factor == null) {
749
+ return null;
750
+ }
751
+ const $helpers = this.getHelpers();
752
+ return this._cloneData($factor, $request, $helpers, $helpers);
753
+ }
754
+ async doPerform(data, request) {
755
+ return await this.performWithContext(request, async (request, context) => {
756
+ const { $context: { traceId } = {} } = request;
757
+ const steps = await this.createSteps();
758
+ const execute = () => {
759
+ return steps.map(async (step) => {
760
+ return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
761
+ .execute(async () => {
762
+ const eachData = this.cloneDataForEach(data, request);
763
+ const eachRequest = { content: eachData, $context: { ...context, traceId } };
764
+ this.traceStepIn(traceId, step, request);
765
+ const response = await step.perform(eachRequest);
766
+ this.traceStepOut(traceId, step, response);
767
+ return response;
768
+ });
769
+ });
770
+ };
771
+ if (this.raceOne()) {
772
+ const response = await Promise.race(execute());
773
+ return response.content;
774
+ }
775
+ else {
776
+ const responses = await Promise.all(execute());
777
+ return responses.map(response => response.content);
778
+ }
779
+ });
780
+ }
781
+ }
782
+
722
783
  class EachPipelineStepSets extends PipelineStepSets {
723
784
  _originalContentPropertyName;
724
785
  _itemPropertyName;
@@ -2234,6 +2295,7 @@ exports.HttpUnknownErrorCode = HttpUnknownErrorCode;
2234
2295
  exports.MssqlTypeOrmDatasource = MssqlTypeOrmDatasource;
2235
2296
  exports.MysqlTypeOrmDatasource = MysqlTypeOrmDatasource;
2236
2297
  exports.OracleTypeOrmDatasource = OracleTypeOrmDatasource;
2298
+ exports.ParallelPipelineStepSets = ParallelPipelineStepSets;
2237
2299
  exports.PgsqlTypeOrmDatasource = PgsqlTypeOrmDatasource;
2238
2300
  exports.PipelineStepSets = PipelineStepSets;
2239
2301
  exports.RefPipelinePipelineStep = RefPipelinePipelineStep;
package/index.js CHANGED
@@ -699,7 +699,7 @@ class PipelineStepSets extends AbstractFragmentaryPipelineStep {
699
699
  const request = await promise;
700
700
  return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
701
701
  .execute(async () => {
702
- this.traceStepOut(traceId, step, request);
702
+ this.traceStepIn(traceId, step, request);
703
703
  const response = await step.perform({ ...request, $context: { ...context, traceId } });
704
704
  this.traceStepOut(traceId, step, response);
705
705
  return this.returnOrContinueOrClear(request, response);
@@ -717,6 +717,67 @@ class AsyncPipelineStepSets extends PipelineStepSets {
717
717
  }
718
718
  }
719
719
 
720
+ class ParallelPipelineStepSets extends PipelineStepSets {
721
+ _cloneDataSnippet;
722
+ _cloneData;
723
+ _race;
724
+ constructor(options) {
725
+ super(options);
726
+ this._cloneDataSnippet = options.cloneData;
727
+ this._cloneData = Utils.createSyncFunction(this.getCloneDataSnippet(), {
728
+ createDefault: () => ($factor, _$request, _$helpers, _$) => {
729
+ return $factor;
730
+ },
731
+ getVariableNames: () => ['$factor', '$request', ...this.getHelpersVariableNames()],
732
+ error: (e) => {
733
+ this.getLogger().error(`Failed on create function for clone data, snippet is [${this.getCloneDataSnippet()}].`);
734
+ throw e;
735
+ }
736
+ });
737
+ this._race = options.race ?? false;
738
+ }
739
+ getCloneDataSnippet() {
740
+ return this._cloneDataSnippet;
741
+ }
742
+ raceOne() {
743
+ return this._race;
744
+ }
745
+ cloneDataForEach($factor, $request) {
746
+ if ($factor == null) {
747
+ return null;
748
+ }
749
+ const $helpers = this.getHelpers();
750
+ return this._cloneData($factor, $request, $helpers, $helpers);
751
+ }
752
+ async doPerform(data, request) {
753
+ return await this.performWithContext(request, async (request, context) => {
754
+ const { $context: { traceId } = {} } = request;
755
+ const steps = await this.createSteps();
756
+ const execute = () => {
757
+ return steps.map(async (step) => {
758
+ return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
759
+ .execute(async () => {
760
+ const eachData = this.cloneDataForEach(data, request);
761
+ const eachRequest = { content: eachData, $context: { ...context, traceId } };
762
+ this.traceStepIn(traceId, step, request);
763
+ const response = await step.perform(eachRequest);
764
+ this.traceStepOut(traceId, step, response);
765
+ return response;
766
+ });
767
+ });
768
+ };
769
+ if (this.raceOne()) {
770
+ const response = await Promise.race(execute());
771
+ return response.content;
772
+ }
773
+ else {
774
+ const responses = await Promise.all(execute());
775
+ return responses.map(response => response.content);
776
+ }
777
+ });
778
+ }
779
+ }
780
+
720
781
  class EachPipelineStepSets extends PipelineStepSets {
721
782
  _originalContentPropertyName;
722
783
  _itemPropertyName;
@@ -2198,4 +2259,4 @@ class TypeOrmTransactionalPipelineStepSets extends PipelineStepSets {
2198
2259
  }
2199
2260
  }
2200
2261
 
2201
- 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_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, ParsedSqlSegmentType, PgsqlTypeOrmDatasource, PipelineStepSets, RefPipelinePipelineStep, RefStepPipelineStep, RoutesPipelineStepSets, SnippetPipelineStep, SnowflakePipelineStep, SupportedDataSourceTypes, TypeOrmBulkSaveBySQLPipelineStep, TypeOrmBySnippetPipelineStep, TypeOrmDataSourceHelper, TypeOrmDataSourceManager, TypeOrmLoadEntityByIdPipelineStep, TypeOrmLoadManyBySQLPipelineStep, TypeOrmLoadOneBySQLPipelineStep, TypeOrmParsedSQLCache, TypeOrmSaveBySQLPipelineStep, TypeOrmSaveEntityPipelineStep, TypeOrmTransactionalPipelineStepSets, Utils };
2262
+ 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_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, TypeOrmLoadEntityByIdPipelineStep, TypeOrmLoadManyBySQLPipelineStep, TypeOrmLoadOneBySQLPipelineStep, TypeOrmParsedSQLCache, TypeOrmSaveBySQLPipelineStep, TypeOrmSaveEntityPipelineStep, TypeOrmTransactionalPipelineStepSets, Utils };
@@ -3,6 +3,7 @@ export * from './utils';
3
3
  export * from './abstract-fragmentary-pipeline-step';
4
4
  export * from './step-sets';
5
5
  export * from './async-step-sets';
6
+ export * from './parallel-step-sets';
6
7
  export * from './each-step-sets';
7
8
  export * from './conditional-step-sets';
8
9
  export * from './routes-step-sets';
@@ -0,0 +1,18 @@
1
+ import { PipelineStepData, PipelineStepHelpers, PipelineStepPayload } from '@rainbow-o23/n1';
2
+ import { PipelineStepSets, PipelineStepSetsOptions } from './step-sets';
3
+ import { ScriptFuncOrBody } from './types';
4
+ export type CloneDataFunc<In, InFragment, EachInFragment = InFragment> = ($factor: InFragment, $request: PipelineStepData<In>, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => EachInFragment;
5
+ export interface ParallelPipelineStepSetsOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out, EachInFragment = InFragment> extends PipelineStepSetsOptions<In, Out, InFragment, OutFragment> {
6
+ cloneData?: ScriptFuncOrBody<CloneDataFunc<In, InFragment, EachInFragment>>;
7
+ race?: boolean;
8
+ }
9
+ export declare class ParallelPipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out, EachInFragment = InFragment> extends PipelineStepSets<In, Out, InFragment, OutFragment> {
10
+ private readonly _cloneDataSnippet;
11
+ private readonly _cloneData;
12
+ private readonly _race;
13
+ constructor(options: ParallelPipelineStepSetsOptions<In, Out, InFragment, OutFragment, EachInFragment>);
14
+ getCloneDataSnippet(): ScriptFuncOrBody<CloneDataFunc<In, InFragment, EachInFragment>>;
15
+ raceOne(): boolean;
16
+ protected cloneDataForEach($factor: InFragment, $request: PipelineStepData<In>): EachInFragment;
17
+ protected doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment>;
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-o23/n3",
3
- "version": "0.1.21",
3
+ "version": "1.0.23",
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": "0.1.21",
24
+ "@rainbow-o23/n1": "1.0.23",
25
25
  "@theinternetfolks/snowflake": "^1.3.0",
26
26
  "node-fetch": "2.6.7",
27
27
  "typeorm": "^0.3.17"
@@ -5,6 +5,7 @@ export * from './abstract-fragmentary-pipeline-step';
5
5
 
6
6
  export * from './step-sets';
7
7
  export * from './async-step-sets';
8
+ export * from './parallel-step-sets';
8
9
  export * from './each-step-sets';
9
10
  export * from './conditional-step-sets';
10
11
  export * from './routes-step-sets';
@@ -0,0 +1,92 @@
1
+ import {PipelineStepData, PipelineStepHelpers, PipelineStepPayload} from '@rainbow-o23/n1';
2
+ import {PipelineStepSets, PipelineStepSetsContext, PipelineStepSetsOptions} from './step-sets';
3
+ import {ScriptFuncOrBody} from './types';
4
+ import {Utils} from './utils';
5
+
6
+ export type CloneDataFunc<In, InFragment, EachInFragment = InFragment> = ($factor: InFragment, $request: PipelineStepData<In>, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => EachInFragment;
7
+
8
+ export interface ParallelPipelineStepSetsOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out, EachInFragment = InFragment>
9
+ extends PipelineStepSetsOptions<In, Out, InFragment, OutFragment> {
10
+ cloneData?: ScriptFuncOrBody<CloneDataFunc<In, InFragment, EachInFragment>>;
11
+ race?: boolean;
12
+ }
13
+
14
+ /**
15
+ * pipeline steps to execute sets of steps parallel.
16
+ */
17
+ export class ParallelPipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out, EachInFragment = InFragment>
18
+ extends PipelineStepSets<In, Out, InFragment, OutFragment> {
19
+ private readonly _cloneDataSnippet: ScriptFuncOrBody<CloneDataFunc<In, InFragment, EachInFragment>>;
20
+ private readonly _cloneData: CloneDataFunc<In, InFragment, EachInFragment>;
21
+ private readonly _race: boolean;
22
+
23
+ public constructor(options: ParallelPipelineStepSetsOptions<In, Out, InFragment, OutFragment, EachInFragment>) {
24
+ super(options);
25
+ this._cloneDataSnippet = options.cloneData;
26
+ this._cloneData = Utils.createSyncFunction(this.getCloneDataSnippet(), {
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
+ createDefault: () => ($factor: InFragment, _$request: PipelineStepData<In>, _$helpers: PipelineStepHelpers, _$: PipelineStepHelpers): EachInFragment => {
29
+ return $factor as unknown as EachInFragment;
30
+ },
31
+ getVariableNames: () => ['$factor', '$request', ...this.getHelpersVariableNames()], //this.generateFromRequestVariableNames(),
32
+ error: (e: Error) => {
33
+ this.getLogger().error(`Failed on create function for clone data, snippet is [${this.getCloneDataSnippet()}].`);
34
+ throw e;
35
+ }
36
+ });
37
+ this._race = options.race ?? false;
38
+ }
39
+
40
+ public getCloneDataSnippet(): ScriptFuncOrBody<CloneDataFunc<In, InFragment, EachInFragment>> {
41
+ return this._cloneDataSnippet;
42
+ }
43
+
44
+ /**
45
+ * returns true when only one needs to be retrieved
46
+ */
47
+ public raceOne(): boolean {
48
+ return this._race;
49
+ }
50
+
51
+ /**
52
+ * default get request content
53
+ */
54
+ protected cloneDataForEach($factor: InFragment, $request: PipelineStepData<In>): EachInFragment {
55
+ if ($factor == null) {
56
+ return null;
57
+ }
58
+ const $helpers = this.getHelpers();
59
+ return this._cloneData($factor, $request, $helpers, $helpers);
60
+ }
61
+
62
+ protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
63
+ return await this.performWithContext(
64
+ request, async (request: PipelineStepData<In>, context: PipelineStepSetsContext): Promise<OutFragment> => {
65
+ const {$context: {traceId} = {}} = request;
66
+ const steps = await this.createSteps();
67
+ const execute = () => {
68
+ return steps.map(async step => {
69
+ return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
70
+ .execute(async () => {
71
+ const eachData = this.cloneDataForEach(data, request);
72
+ const eachRequest = {content: eachData, $context: {...context, traceId}};
73
+ this.traceStepIn(traceId, step, request);
74
+ const response = await step.perform(eachRequest);
75
+ this.traceStepOut(traceId, step, response);
76
+ // return
77
+ return response;
78
+ });
79
+ });
80
+ };
81
+ if (this.raceOne()) {
82
+ // race
83
+ const response = await Promise.race(execute());
84
+ return response.content as OutFragment;
85
+ } else {
86
+ // all
87
+ const responses = await Promise.all(execute());
88
+ return responses.map(response => response.content) as OutFragment;
89
+ }
90
+ });
91
+ }
92
+ }
@@ -85,7 +85,7 @@ export class PipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayloa
85
85
  const request = await promise;
86
86
  return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
87
87
  .execute(async () => {
88
- this.traceStepOut(traceId, step, request);
88
+ this.traceStepIn(traceId, step, request);
89
89
  const response = await step.perform({...request, $context: {...context, traceId}});
90
90
  this.traceStepOut(traceId, step, response);
91
91
  // if no response returned, keep using request for next
@@ -0,0 +1,25 @@
1
+ import {createConfig, createLogger, PipelineStep, PipelineStepBuilder, PipelineStepOptions} from '@rainbow-o23/n1';
2
+ import {ParallelPipelineStepSets, SnippetPipelineStep} from '../../src';
3
+
4
+ const logger = createLogger();
5
+ const config = createConfig(logger);
6
+
7
+ test('Parallel Pipeline Step Test #1, + 100', async () => {
8
+ const step = new ParallelPipelineStepSets({
9
+ config, logger, steps: [
10
+ new class implements PipelineStepBuilder {
11
+ async create(options?: PipelineStepOptions): Promise<PipelineStep> {
12
+ return new SnippetPipelineStep({config, logger, snippet: 'return $factor.base * 100;'});
13
+ }
14
+ },
15
+ new class implements PipelineStepBuilder {
16
+ async create(options?: PipelineStepOptions): Promise<PipelineStep> {
17
+ return new SnippetPipelineStep({config, logger, snippet: 'return $factor.base * 200;'});
18
+ }
19
+ }
20
+ ]
21
+ });
22
+ const request = {content: {base: 1}};
23
+ const response = await step.perform(request);
24
+ expect(response.content).toEqual([100, 200]);
25
+ });