@rainbow-o23/n3 0.1.1

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.
Files changed (97) hide show
  1. package/.babelrc +11 -0
  2. package/.eslintrc +23 -0
  3. package/README.md +584 -0
  4. package/index.cjs +2283 -0
  5. package/index.d.ts +5 -0
  6. package/index.js +2229 -0
  7. package/lib/error-codes.d.ts +17 -0
  8. package/lib/http/fetch-step.d.ts +53 -0
  9. package/lib/http/index.d.ts +2 -0
  10. package/lib/http/types.d.ts +21 -0
  11. package/lib/step/abstract-fragmentary-pipeline-step.d.ts +44 -0
  12. package/lib/step/async-step-sets.d.ts +5 -0
  13. package/lib/step/conditional-step-sets.d.ts +23 -0
  14. package/lib/step/delete-property-step.d.ts +11 -0
  15. package/lib/step/each-step-sets.d.ts +14 -0
  16. package/lib/step/get-property-step.d.ts +11 -0
  17. package/lib/step/index.d.ts +14 -0
  18. package/lib/step/ref-pipeline-step.d.ts +15 -0
  19. package/lib/step/ref-step-step.d.ts +14 -0
  20. package/lib/step/routes-step-sets.d.ts +30 -0
  21. package/lib/step/snippet-step.d.ts +19 -0
  22. package/lib/step/snowflake-step.d.ts +6 -0
  23. package/lib/step/step-sets.d.ts +19 -0
  24. package/lib/step/types.d.ts +13 -0
  25. package/lib/step/utils.d.ts +23 -0
  26. package/lib/typeorm/abstract-datasource.d.ts +22 -0
  27. package/lib/typeorm/better-sqlite3-datasource.d.ts +7 -0
  28. package/lib/typeorm/datasource-manager.d.ts +25 -0
  29. package/lib/typeorm/index.d.ts +7 -0
  30. package/lib/typeorm/mssql-datasource.d.ts +6 -0
  31. package/lib/typeorm/mysql-datasource.d.ts +6 -0
  32. package/lib/typeorm/oracle-datasource.d.ts +6 -0
  33. package/lib/typeorm/pgsql-datasource.d.ts +6 -0
  34. package/lib/typeorm-step/abstract-typeorm-by-sql-step.d.ts +62 -0
  35. package/lib/typeorm-step/abstract-typeorm-load-by-sql-step.d.ts +10 -0
  36. package/lib/typeorm-step/abstract-typeorm-step.d.ts +30 -0
  37. package/lib/typeorm-step/index.d.ts +12 -0
  38. package/lib/typeorm-step/type-orm-transactional-step-sets.d.ts +22 -0
  39. package/lib/typeorm-step/typeorm-bulk-save-by-sql-step.d.ts +9 -0
  40. package/lib/typeorm-step/typeorm-by-snippet-step.d.ts +21 -0
  41. package/lib/typeorm-step/typeorm-load-entity-by-id-step.d.ts +12 -0
  42. package/lib/typeorm-step/typeorm-load-many-by-sql-step.d.ts +6 -0
  43. package/lib/typeorm-step/typeorm-load-one-by-sql-step.d.ts +6 -0
  44. package/lib/typeorm-step/typeorm-save-by-sql-step.d.ts +9 -0
  45. package/lib/typeorm-step/typeorm-save-entity-step.d.ts +32 -0
  46. package/lib/typeorm-step/types.d.ts +22 -0
  47. package/package.json +74 -0
  48. package/rollup.config.base.js +33 -0
  49. package/rollup.config.ci.js +3 -0
  50. package/rollup.config.js +3 -0
  51. package/src/index.ts +7 -0
  52. package/src/lib/error-codes.ts +18 -0
  53. package/src/lib/http/fetch-step.ts +290 -0
  54. package/src/lib/http/index.ts +3 -0
  55. package/src/lib/http/types.ts +33 -0
  56. package/src/lib/step/abstract-fragmentary-pipeline-step.ts +367 -0
  57. package/src/lib/step/async-step-sets.ts +14 -0
  58. package/src/lib/step/conditional-step-sets.ts +115 -0
  59. package/src/lib/step/delete-property-step.ts +33 -0
  60. package/src/lib/step/each-step-sets.ts +80 -0
  61. package/src/lib/step/get-property-step.ts +34 -0
  62. package/src/lib/step/index.ts +18 -0
  63. package/src/lib/step/ref-pipeline-step.ts +65 -0
  64. package/src/lib/step/ref-step-step.ts +59 -0
  65. package/src/lib/step/routes-step-sets.ts +147 -0
  66. package/src/lib/step/snippet-step.ts +80 -0
  67. package/src/lib/step/snowflake-step.ts +16 -0
  68. package/src/lib/step/step-sets.ts +99 -0
  69. package/src/lib/step/types.ts +23 -0
  70. package/src/lib/step/utils.ts +109 -0
  71. package/src/lib/typeorm/abstract-datasource.ts +58 -0
  72. package/src/lib/typeorm/better-sqlite3-datasource.ts +29 -0
  73. package/src/lib/typeorm/datasource-manager.ts +104 -0
  74. package/src/lib/typeorm/index.ts +9 -0
  75. package/src/lib/typeorm/mssql-datasource.ts +131 -0
  76. package/src/lib/typeorm/mysql-datasource.ts +35 -0
  77. package/src/lib/typeorm/oracle-datasource.ts +27 -0
  78. package/src/lib/typeorm/pgsql-datasource.ts +25 -0
  79. package/src/lib/typeorm-step/abstract-typeorm-by-sql-step.ts +556 -0
  80. package/src/lib/typeorm-step/abstract-typeorm-load-by-sql-step.ts +31 -0
  81. package/src/lib/typeorm-step/abstract-typeorm-step.ts +241 -0
  82. package/src/lib/typeorm-step/index.ts +17 -0
  83. package/src/lib/typeorm-step/type-orm-transactional-step-sets.ts +129 -0
  84. package/src/lib/typeorm-step/typeorm-bulk-save-by-sql-step.ts +29 -0
  85. package/src/lib/typeorm-step/typeorm-by-snippet-step.ts +83 -0
  86. package/src/lib/typeorm-step/typeorm-load-entity-by-id-step.ts +35 -0
  87. package/src/lib/typeorm-step/typeorm-load-many-by-sql-step.ts +13 -0
  88. package/src/lib/typeorm-step/typeorm-load-one-by-sql-step.ts +13 -0
  89. package/src/lib/typeorm-step/typeorm-save-by-sql-step.ts +24 -0
  90. package/src/lib/typeorm-step/typeorm-save-entity-step.ts +109 -0
  91. package/src/lib/typeorm-step/types.ts +25 -0
  92. package/test/step/snippet-step.test.ts +21 -0
  93. package/test/step/snowflake-step.test.ts +22 -0
  94. package/test/step/typeorm-by-sql-autonomous.test.ts +117 -0
  95. package/test/step/typeorm-by-sql-transactional.test.ts +215 -0
  96. package/test/step/typeorm-entity.test.ts +50 -0
  97. package/tsconfig.json +36 -0
@@ -0,0 +1,241 @@
1
+ import {PipelineStepData, PipelineStepPayload, UncatchableError, Undefinable} from '@rainbow-o23/n1';
2
+ import {DataSource, EntityMetadata, ObjectLiteral, QueryRunner, Repository} from 'typeorm';
3
+ import {ColumnMetadata} from 'typeorm/metadata/ColumnMetadata.js';
4
+ import {
5
+ ERR_TYPEORM_DATASOURCE_NOT_FOUND,
6
+ ERR_TYPEORM_ENTITY_NOT_FOUND,
7
+ ERR_TYPEORM_TRANSACTION_NOT_FOUND
8
+ } from '../error-codes';
9
+ import {AbstractFragmentaryPipelineStep, FragmentaryPipelineStepOptions} from '../step';
10
+ import {DataSourceType, TypeOrmDataSource, TypeOrmDataSourceManager} from '../typeorm';
11
+ import {
12
+ DEFAULT_TRANSACTION_NAME,
13
+ TypeOrmDataSourceName,
14
+ TypeOrmEntityName,
15
+ TypeOrmTransactionalContext,
16
+ TypeOrmTransactionKey,
17
+ TypeOrmTransactionName
18
+ } from './types';
19
+
20
+ export interface TypeOrmPipelineStepOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
21
+ extends FragmentaryPipelineStepOptions<In, Out, InFragment, OutFragment> {
22
+ dataSourceName: TypeOrmDataSourceName;
23
+ /**
24
+ * to identify the transaction should be used, or ignore it to use the default transaction.
25
+ * transaction must be created by {@link TypeOrmTransactionalPipelineStepSets},
26
+ * otherwise raise exception when autonomous is false.
27
+ */
28
+ transactionName?: TypeOrmTransactionName;
29
+ /** when autonomous is true, transaction name should be ignored no matter what given */
30
+ autonomous?: boolean;
31
+ }
32
+
33
+ // noinspection JSUnusedGlobalSymbols
34
+ export abstract class AbstractTypeOrmPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
35
+ extends AbstractFragmentaryPipelineStep<In, Out, InFragment, OutFragment> {
36
+ private readonly _dataSourceName: TypeOrmDataSourceName;
37
+ private readonly _transactionName: TypeOrmTransactionName;
38
+ private readonly _autonomous: boolean;
39
+
40
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
41
+ public constructor(options: TypeOrmPipelineStepOptions<In, Out, InFragment, OutFragment>) {
42
+ super(options);
43
+ this._dataSourceName = options.dataSourceName;
44
+ this._transactionName = options.transactionName || DEFAULT_TRANSACTION_NAME;
45
+ this._autonomous = options.autonomous ?? false;
46
+ }
47
+
48
+ public getDataSourceName(): TypeOrmDataSourceName {
49
+ return this._dataSourceName;
50
+ }
51
+
52
+ public getTransactionName(): TypeOrmTransactionName {
53
+ return this._transactionName;
54
+ }
55
+
56
+ public getTransactionKey(): TypeOrmTransactionKey {
57
+ return `${this.getDataSourceName()}.${this.getTransactionName()}`;
58
+ }
59
+
60
+ public isAutonomous(): boolean {
61
+ return this._autonomous;
62
+ }
63
+
64
+ protected isTransactional(request: PipelineStepData<In>): boolean {
65
+ if (this.isAutonomous()) {
66
+ // autonomous transaction
67
+ return false;
68
+ } else {
69
+ const {$context} = request as PipelineStepData<In, TypeOrmTransactionalContext>;
70
+ const transactionKey = this.getTransactionKey();
71
+ if ($context == null || $context.$trans == null || $context.$trans[transactionKey] == null) {
72
+ throw new UncatchableError(ERR_TYPEORM_TRANSACTION_NOT_FOUND, `Transaction[${transactionKey}] not found.`);
73
+ } else {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+
79
+ protected findTransactionalContext(request: PipelineStepData<In>): Undefinable<[TypeOrmDataSource, QueryRunner]> {
80
+ const {$context} = request as PipelineStepData<In, TypeOrmTransactionalContext>;
81
+ return $context?.$trans?.[this.getTransactionKey()];
82
+ }
83
+
84
+ protected findDataSourceType(): DataSourceType {
85
+ return TypeOrmDataSourceManager.findDataSourceType(this.getDataSourceName(), this.getConfig());
86
+ }
87
+
88
+ /**
89
+ * returns undefined when data source not found
90
+ */
91
+ protected async findDataSource(request: PipelineStepData<In>): Promise<Undefinable<TypeOrmDataSource>> {
92
+ if (this.isTransactional(request)) {
93
+ return this.findTransactionalContext(request)?.[0];
94
+ } else {
95
+ const typeOrmDataSource = await TypeOrmDataSourceManager.findDataSource(this.getDataSourceName(), this.getConfig());
96
+ if (typeOrmDataSource == null) {
97
+ return (void 0);
98
+ } else {
99
+ return typeOrmDataSource;
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * throw error when data source not found
106
+ */
107
+ protected async findDataSourceOrThrow(request: PipelineStepData<In>): Promise<Undefinable<TypeOrmDataSource>> {
108
+ let typeOrmDataSource: Undefinable<TypeOrmDataSource>;
109
+ if (this.isTransactional(request)) {
110
+ typeOrmDataSource = this.findTransactionalContext(request)?.[0];
111
+ } else {
112
+ typeOrmDataSource = await TypeOrmDataSourceManager.findDataSource(this.getDataSourceName(), this.getConfig());
113
+ }
114
+ if (typeOrmDataSource == null) {
115
+ throw new UncatchableError(ERR_TYPEORM_DATASOURCE_NOT_FOUND, `Data source[${this.getDataSourceName()}] not found.`);
116
+ } else {
117
+ return typeOrmDataSource;
118
+ }
119
+ }
120
+
121
+ protected async createRunner(request: PipelineStepData<In>): Promise<QueryRunner> {
122
+ if (this.isTransactional(request)) {
123
+ // use this to guard not found
124
+ await this.findDataSourceOrThrow(request);
125
+ return this.findTransactionalContext(request)?.[1];
126
+ } else {
127
+ const typeOrmDataSource = await this.findDataSourceOrThrow(request);
128
+ const dataSource = typeOrmDataSource.getDataSource();
129
+ const runner = dataSource.createQueryRunner();
130
+ await runner.connect();
131
+ return runner;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * 1. create runner,
137
+ * 2. start transaction,
138
+ * 3. run given function by created runner,
139
+ * 4. commit or rollback transaction,
140
+ * 4.1 handle rollback error when rolled back
141
+ * 4.1.1 throw error if anything(should be an error) returned,
142
+ * 4.1.2 do nothing if nothing returned,
143
+ * 5. release runner.
144
+ */
145
+ protected async trans<R>(
146
+ run: (runner: QueryRunner) => Promise<R>, handleRollbackError: (err: Error) => Undefinable<Error>,
147
+ request: PipelineStepData<In>): Promise<R> {
148
+ if (this.isTransactional(request)) {
149
+ // use this to guard not found
150
+ const runner = await this.createRunner(request);
151
+ try {
152
+ return await run(runner);
153
+ } catch (e) {
154
+ handleRollbackError(e);
155
+ // throw anyway
156
+ throw e;
157
+ }
158
+ } else {
159
+ let runner: QueryRunner = null;
160
+ try {
161
+ runner = await this.createRunner(request);
162
+ await runner.startTransaction();
163
+ const r = await run(runner);
164
+ await runner.commitTransaction();
165
+ return r;
166
+ } catch (err) {
167
+ // noinspection PointlessBooleanExpressionJS
168
+ if (runner != null) {
169
+ try {
170
+ // noinspection JSObjectNullOrUndefined
171
+ await runner.rollbackTransaction();
172
+ } catch (e) {
173
+ this.getLogger().error(`Failed to rollback typeorm transaction of data source[${this.getDataSourceName()}].`, e, this.constructor.name);
174
+ }
175
+ }
176
+ const error = handleRollbackError(err);
177
+ if (error != null) {
178
+ throw error;
179
+ }
180
+ } finally {
181
+ if (runner != null) {
182
+ try {
183
+ await runner.release();
184
+ } catch (e) {
185
+ this.getLogger().error(`Failed to release typeorm query runner of data source[${this.getDataSourceName()}].`, e, this.constructor.name);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * only when step is a sub step of {@link TypeOrmTransactionalPipelineStepSets}, and use the transaction which created by step sets,
194
+ * it is transactional, otherwise it is not.
195
+ *
196
+ * No transaction control simply means that there is no explicit transaction control internally,
197
+ * but it does not mean that write operations cannot be performed.
198
+ * If a write operation is performed, the data will be immediately committed to the database.
199
+ *
200
+ * 1. create runner,
201
+ * 2. run given function by created runner,
202
+ * 3. release runner.
203
+ */
204
+ protected async autoTrans<R>(run: (runner: QueryRunner) => Promise<R>, request: PipelineStepData<In>): Promise<R> {
205
+ if (this.isTransactional(request)) {
206
+ const runner = await this.createRunner(request);
207
+ return await run(runner);
208
+ } else {
209
+ let runner: QueryRunner = null;
210
+ try {
211
+ runner = await this.createRunner(request);
212
+ return await run(runner);
213
+ } finally {
214
+ if (runner != null) {
215
+ try {
216
+ await runner.release();
217
+ } catch (e) {
218
+ this.getLogger().error(`Failed to release typeorm query runner of data source[${this.getDataSourceName()}].`, e, this.constructor.name);
219
+ // ignore
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * find metadata by given name. throw error when data source or metadata not found.
228
+ */
229
+ protected async findMetadata<E extends ObjectLiteral>(name: TypeOrmEntityName, request: PipelineStepData<In>): Promise<[EntityMetadata, ColumnMetadata, Repository<E>, DataSource]> {
230
+ const runner = await this.createRunner(request);
231
+ const dataSource = runner.connection;
232
+ const metadata = (dataSource.entityMetadatas || []).find(metadata => metadata.name == name);
233
+ if (metadata == null) {
234
+ throw new UncatchableError(ERR_TYPEORM_ENTITY_NOT_FOUND,
235
+ `Entity[${name}] in data source[${this.getDataSourceName()}] not found.`);
236
+ }
237
+ const column = metadata.columns.find(column => column.isPrimary);
238
+ const repository = runner.manager.getRepository(metadata.target);
239
+ return [metadata, column, repository as Repository<E>, dataSource];
240
+ }
241
+ }
@@ -0,0 +1,17 @@
1
+ export * from './types';
2
+
3
+ export * from './abstract-typeorm-step';
4
+ export * from './typeorm-load-entity-by-id-step';
5
+ export * from './typeorm-save-entity-step';
6
+
7
+ export * from './typeorm-by-snippet-step';
8
+ export * from './abstract-typeorm-by-sql-step';
9
+
10
+ export * from './abstract-typeorm-load-by-sql-step';
11
+ export * from './typeorm-load-one-by-sql-step';
12
+ export * from './typeorm-load-many-by-sql-step';
13
+
14
+ export * from './typeorm-save-by-sql-step';
15
+ export * from './typeorm-bulk-save-by-sql-step';
16
+
17
+ export * from './type-orm-transactional-step-sets';
@@ -0,0 +1,129 @@
1
+ import {PipelineStepData, PipelineStepPayload, UncatchableError, Undefinable} from '@rainbow-o23/n1';
2
+ import {QueryRunner} from 'typeorm';
3
+ import {ERR_TYPEORM_DATASOURCE_NOT_FOUND} from '../error-codes';
4
+ import {PipelineStepSets, PipelineStepSetsContext, PipelineStepSetsOptions} from '../step';
5
+ import {TypeOrmDataSource, TypeOrmDataSourceManager} from '../typeorm';
6
+ import {
7
+ DEFAULT_TRANSACTION_NAME,
8
+ TypeOrmDataSourceName,
9
+ TypeOrmTransactionalContext,
10
+ TypeOrmTransactionKey,
11
+ TypeOrmTransactionName
12
+ } from './types';
13
+
14
+ export interface TypeOrmTransactionalPipelineStepSetsOptions extends PipelineStepSetsOptions {
15
+ dataSourceName: TypeOrmDataSourceName;
16
+ transactionName?: TypeOrmTransactionName;
17
+ }
18
+
19
+ /**
20
+ * if given transaction is open (transaction key exists), will use it.
21
+ * so if there are multiple transactions need to be open, use different transaction name.
22
+ * for different data source,
23
+ */
24
+ export class TypeOrmTransactionalPipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
25
+ extends PipelineStepSets<In, Out, InFragment, OutFragment> {
26
+ private readonly _dataSourceName: TypeOrmDataSourceName;
27
+ private readonly _transactionName: TypeOrmTransactionName;
28
+
29
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
30
+ public constructor(options: TypeOrmTransactionalPipelineStepSetsOptions) {
31
+ super(options);
32
+ this._dataSourceName = options.dataSourceName;
33
+ this._transactionName = (options.transactionName ?? '').trim() || DEFAULT_TRANSACTION_NAME;
34
+ }
35
+
36
+ public getDataSourceName(): TypeOrmDataSourceName {
37
+ return this._dataSourceName;
38
+ }
39
+
40
+ public getTransactionName(): TypeOrmTransactionName {
41
+ return this._transactionName;
42
+ }
43
+
44
+ public getTransactionKey(): TypeOrmTransactionKey {
45
+ return `${this.getDataSourceName()}.${this.getTransactionName()}`;
46
+ }
47
+
48
+ /**
49
+ * throw error when data source not found
50
+ */
51
+ protected async findDataSourceOrThrow(): Promise<Undefinable<TypeOrmDataSource>> {
52
+ const typeOrmDataSource = await TypeOrmDataSourceManager.findDataSource(this.getDataSourceName(), this.getConfig());
53
+ if (typeOrmDataSource == null) {
54
+ throw new UncatchableError(ERR_TYPEORM_DATASOURCE_NOT_FOUND, `Data source[${this.getDataSourceName()}] not found.`);
55
+ } else {
56
+ return typeOrmDataSource;
57
+ }
58
+ }
59
+
60
+ protected async createRunner(): Promise<[TypeOrmDataSource, QueryRunner]> {
61
+ const typeOrmDataSource = await this.findDataSourceOrThrow();
62
+ const dataSource = typeOrmDataSource.getDataSource();
63
+ const runner = dataSource.createQueryRunner();
64
+ await runner.connect();
65
+ return [typeOrmDataSource, runner];
66
+ }
67
+
68
+ /**
69
+ * inherit transaction (use runner in context) if exists, otherwise create new runner
70
+ */
71
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
+ protected async attachMineToInternalContext(inheritedContext: PipelineStepSetsContext, _request: PipelineStepData<In>): Promise<TypeOrmTransactionalContext> {
73
+ const context = inheritedContext as TypeOrmTransactionalContext;
74
+ if (context.$trans != null) {
75
+ if (context.$trans[this.getTransactionKey()] != null) {
76
+ // exists, do nothing
77
+ } else {
78
+ context.$trans[this.getTransactionKey()] = await this.createRunner();
79
+ }
80
+ } else {
81
+ context.$trans = {[this.getTransactionKey()]: await this.createRunner()};
82
+ }
83
+ return context;
84
+ }
85
+
86
+ private getRunner(context: TypeOrmTransactionalContext): QueryRunner {
87
+ return context.$trans[this.getTransactionKey()][1];
88
+ }
89
+
90
+ /**
91
+ * 1. create runner, attach to internal context
92
+ * 2. start transaction
93
+ * 3. run steps
94
+ * 4. commit or rollback transaction
95
+ * 5. release runner
96
+ */
97
+ protected async performWithContext(
98
+ request: PipelineStepData<In>,
99
+ run: (request: PipelineStepData<In>, context: PipelineStepSetsContext) => Promise<OutFragment>): Promise<OutFragment> {
100
+ let runner: QueryRunner = null;
101
+ try {
102
+ const context: TypeOrmTransactionalContext = await this.createInternalContext(request);
103
+ runner = this.getRunner(context);
104
+ await runner.startTransaction();
105
+ const r = await run(request, context);
106
+ await runner.commitTransaction();
107
+ return r;
108
+ } catch (e) {
109
+ // noinspection PointlessBooleanExpressionJS
110
+ if (runner != null) {
111
+ try {
112
+ // noinspection JSObjectNullOrUndefined
113
+ await runner.rollbackTransaction();
114
+ } catch (e) {
115
+ this.getLogger().error(`Failed to rollback typeorm transaction[${this.getTransactionName()}] of data source[${this.getDataSourceName()}].`, e, this.constructor.name);
116
+ }
117
+ }
118
+ throw e;
119
+ } finally {
120
+ if (runner != null) {
121
+ try {
122
+ await runner.release();
123
+ } catch (e) {
124
+ this.getLogger().error(`Failed to release typeorm query runner for transaction[${this.getTransactionName()}] of data source[${this.getDataSourceName()}].`, e, this.constructor.name);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,29 @@
1
+ import {PipelineStepData, PipelineStepPayload, Undefinable} from '@rainbow-o23/n1';
2
+ import {AbstractTypeOrmBySQLPipelineStep, TypeOrmBasis} from './abstract-typeorm-by-sql-step';
3
+ import {TypeOrmBulkWrittenResult, TypeOrmEntityToSave, TypeOrmEntityValue} from './types';
4
+
5
+ export interface TypeOrmBulkSaveBasis extends TypeOrmBasis {
6
+ items?: Array<Array<TypeOrmEntityValue> | TypeOrmEntityToSave>;
7
+ }
8
+
9
+ /**
10
+ * ignore when values is not present or values is an empty array
11
+ */
12
+ export class TypeOrmBulkSaveBySQLPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, OutFragment extends TypeOrmBulkWrittenResult = TypeOrmBulkWrittenResult>
13
+ extends AbstractTypeOrmBySQLPipelineStep<In, Out, Undefinable<TypeOrmBulkSaveBasis>, OutFragment> {
14
+ protected async doPerform(basis: Undefinable<TypeOrmBulkSaveBasis>, request: PipelineStepData<In>): Promise<OutFragment> {
15
+ if (basis?.items == null || basis?.items.length === 0) {
16
+ return [] as OutFragment;
17
+ }
18
+
19
+ return await this.autoTrans<OutFragment>(async (runner) => {
20
+ const result: OutFragment = [] as OutFragment;
21
+ for (const item of basis.items) {
22
+ const {sql, params} = this.getSql(basis, item);
23
+ const ret = await runner.query(sql, params);
24
+ result.push(this.parseResult(ret));
25
+ }
26
+ return result;
27
+ }, request);
28
+ }
29
+ }
@@ -0,0 +1,83 @@
1
+ import {PipelineStepData, PipelineStepHelpers, PipelineStepPayload, UncatchableError} from '@rainbow-o23/n1';
2
+ import {QueryRunner} from 'typeorm';
3
+ import {ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY} from '../error-codes';
4
+ import {ScriptFuncOrBody, Utils} from '../step';
5
+ import {AbstractTypeOrmPipelineStep, TypeOrmPipelineStepOptions} from './abstract-typeorm-step';
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ export type TypeOrmPerformWithInFragment<InFragment, OutFragment> = ($runner: QueryRunner, $factor: InFragment, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => Promise<OutFragment>;
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ export type TypeOrmPerformWithoutInFragment<OutFragment> = ($runner: QueryRunner, $helpers: PipelineStepHelpers, $: PipelineStepHelpers) => Promise<OutFragment>;
11
+ export type TypeOrmPerformFunc<InFragment, OutFragment> =
12
+ TypeOrmPerformWithInFragment<InFragment, OutFragment> | TypeOrmPerformWithoutInFragment<OutFragment>;
13
+
14
+ export interface TypeOrmBySnippetPipelineStepOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
15
+ extends TypeOrmPipelineStepOptions<In, Out, InFragment, OutFragment> {
16
+ snippet: ScriptFuncOrBody<TypeOrmPerformFunc<InFragment, OutFragment>>;
17
+ }
18
+
19
+ /**
20
+ * ignore when values is not present or values is an empty array
21
+ */
22
+ export class TypeOrmBySnippetPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
23
+ extends AbstractTypeOrmPipelineStep<In, Out, InFragment, OutFragment> {
24
+ private readonly _snippet: ScriptFuncOrBody<TypeOrmPerformFunc<InFragment, OutFragment>>;
25
+ private readonly _func: TypeOrmPerformFunc<InFragment, OutFragment>;
26
+
27
+ public constructor(options: TypeOrmBySnippetPipelineStepOptions<In, Out, InFragment, OutFragment>) {
28
+ super(options);
29
+ this._snippet = options.snippet;
30
+ this._func = Utils.createAsyncFunction(this.getSnippet(), {
31
+ createDefault: () => {
32
+ throw new UncatchableError(ERR_TYPEORM_STEP_SNIPPET_NOT_EMPTY, 'Cannot create perform func on empty snippet.');
33
+ },
34
+ getVariableNames: () => this.generateVariableNames(),
35
+ error: (e: Error) => {
36
+ this.getLogger().error(`Failed on create function for typeorm snippet, snippet is [${this.getSnippet()}].`);
37
+ throw e;
38
+ }
39
+ });
40
+ }
41
+
42
+ public getSnippet(): ScriptFuncOrBody<TypeOrmPerformFunc<InFragment, OutFragment>> {
43
+ return this._snippet;
44
+ }
45
+
46
+ protected generateVariableNames(): Array<string> {
47
+ return [
48
+ this.getRunnerVariableName(),
49
+ this.isInFragmentIgnored() ? null : this.getInFragmentVariableName(),
50
+ ...this.getHelpersVariableNames()
51
+ ].filter(x => x != null);
52
+ }
53
+
54
+ protected async doPerform(basis: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
55
+ return await this.autoTrans<OutFragment>(async (runner) => {
56
+ const $helpers = this.getHelpers();
57
+ if (this.isInFragmentIgnored()) {
58
+ return await (this._func as TypeOrmPerformWithoutInFragment<OutFragment>)(runner, $helpers, $helpers);
59
+ } else {
60
+ return await (this._func as TypeOrmPerformWithInFragment<InFragment, OutFragment>)(runner, basis, $helpers, $helpers);
61
+ }
62
+ }, request);
63
+ }
64
+
65
+ /**
66
+ * is request step data ignored to snippet function.
67
+ * default returns false
68
+ */
69
+ protected isInFragmentIgnored(): boolean {
70
+ return false;
71
+ }
72
+
73
+ protected getRunnerVariableName(): string {
74
+ return '$runner';
75
+ }
76
+
77
+ /**
78
+ * override this method when want to use another variable name rather than "$factor"
79
+ */
80
+ protected getInFragmentVariableName(): string {
81
+ return '$factor';
82
+ }
83
+ }
@@ -0,0 +1,35 @@
1
+ import {PipelineStepData, PipelineStepPayload, Undefinable} from '@rainbow-o23/n1';
2
+ import {AbstractTypeOrmPipelineStep, TypeOrmPipelineStepOptions} from './abstract-typeorm-step';
3
+ import {TypeOrmEntityName, TypeOrmEntityToLoad, TypeOrmIdType} from './types';
4
+
5
+ export interface TypeOrmLoadEntityByIdPipelineStepOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = TypeOrmIdType, OutFragment = Out>
6
+ extends TypeOrmPipelineStepOptions<In, Out, InFragment, OutFragment> {
7
+ entityName: TypeOrmEntityName;
8
+ }
9
+
10
+ /**
11
+ * no transaction here
12
+ */
13
+ export class TypeOrmLoadEntityByIdPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, OutFragment = TypeOrmEntityToLoad>
14
+ extends AbstractTypeOrmPipelineStep<In, Out, TypeOrmIdType, OutFragment> {
15
+ private readonly _entityName: TypeOrmEntityName;
16
+
17
+ public constructor(options: TypeOrmLoadEntityByIdPipelineStepOptions<In, Out, TypeOrmIdType, OutFragment>) {
18
+ super(options);
19
+ this._entityName = options.entityName;
20
+ }
21
+
22
+ public getEntityName(): TypeOrmEntityName {
23
+ return this._entityName;
24
+ }
25
+
26
+ protected async doPerform(id: TypeOrmIdType, request: PipelineStepData<In>): Promise<Undefinable<OutFragment>> {
27
+ const [, column, repository] = await this.findMetadata(this.getEntityName(), request);
28
+ const loaded = await repository.findOneBy({[column.propertyName]: id}) as OutFragment;
29
+ if (loaded == null) {
30
+ return (void 0);
31
+ } else {
32
+ return loaded;
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ import {PipelineStepPayload} from '@rainbow-o23/n1';
2
+ import {AbstractTypeOrmLoadBySQLPipelineStep} from './abstract-typeorm-load-by-sql-step';
3
+ import {TypeOrmEntityToLoad} from './types';
4
+
5
+ /**
6
+ * load many object by sql
7
+ */
8
+ export class TypeOrmLoadManyBySQLPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, OutFragment = Array<TypeOrmEntityToLoad>>
9
+ extends AbstractTypeOrmLoadBySQLPipelineStep<In, Out, OutFragment> {
10
+ protected async getDataFromResultSet(rst: Array<TypeOrmEntityToLoad>): Promise<OutFragment> {
11
+ return rst as OutFragment;
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ import {PipelineStepPayload} from '@rainbow-o23/n1';
2
+ import {AbstractTypeOrmLoadBySQLPipelineStep} from './abstract-typeorm-load-by-sql-step';
3
+ import {TypeOrmEntityToLoad} from './types';
4
+
5
+ /**
6
+ * load one object by sql
7
+ */
8
+ export class TypeOrmLoadOneBySQLPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, OutFragment = TypeOrmEntityToLoad>
9
+ extends AbstractTypeOrmLoadBySQLPipelineStep<In, Out, OutFragment> {
10
+ protected async getDataFromResultSet(rst: Array<TypeOrmEntityToLoad>): Promise<OutFragment> {
11
+ return rst[0] as OutFragment;
12
+ }
13
+ }
@@ -0,0 +1,24 @@
1
+ import {PipelineStepData, PipelineStepPayload, Undefinable} from '@rainbow-o23/n1';
2
+ import {AbstractTypeOrmBySQLPipelineStep, TypeOrmBasis} from './abstract-typeorm-by-sql-step';
3
+ import {TypeOrmEntityToSave, TypeOrmEntityValue, TypeOrmWrittenResult} from './types';
4
+
5
+ export interface TypeOrmSaveBasis extends TypeOrmBasis {
6
+ values?: Array<TypeOrmEntityValue> | TypeOrmEntityToSave;
7
+ }
8
+
9
+ /**
10
+ * execute anyway, even values is not present.
11
+ *
12
+ * It is important to note that when executing the Update/Delete SQL through this step,
13
+ * the affected data rows may not be just one row. This depends on the given SQL and values.
14
+ */
15
+ export class TypeOrmSaveBySQLPipelineStep<In = PipelineStepPayload, Out = PipelineStepPayload, OutFragment = TypeOrmWrittenResult>
16
+ extends AbstractTypeOrmBySQLPipelineStep<In, Out, Undefinable<TypeOrmSaveBasis>, OutFragment> {
17
+ protected async doPerform(basis: Undefinable<TypeOrmSaveBasis>, request: PipelineStepData<In>): Promise<OutFragment> {
18
+ const {sql, params} = this.getSql(basis, basis?.values);
19
+ return await this.autoTrans<OutFragment>(async (runner) => {
20
+ const result = await runner.query(sql, params);
21
+ return this.parseResult(result);
22
+ }, request);
23
+ }
24
+ }