@rainbow-o23/n3 1.0.58 → 1.0.60
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 +13 -5
- package/index.cjs +180 -100
- package/index.js +180 -100
- package/lib/http/fetch-step.d.ts +12 -1
- package/lib/step/abstract-fragmentary-pipeline-step.d.ts +2 -2
- package/lib/step/async-step-sets.d.ts +2 -1
- package/lib/step/step-sets.d.ts +6 -6
- package/lib/typeorm-step/typeorm-transactional-step-sets.d.ts +3 -3
- package/lib/typeorm-step/types.d.ts +2 -2
- package/package.json +4 -4
- package/src/lib/http/fetch-step.ts +183 -80
- package/src/lib/step/abstract-fragmentary-pipeline-step.ts +9 -12
- package/src/lib/step/async-step-sets.ts +10 -1
- package/src/lib/step/parallel-step-sets.ts +4 -4
- package/src/lib/step/ref-pipeline-step.ts +1 -2
- package/src/lib/step/step-sets.ts +10 -16
- package/src/lib/typeorm-step/typeorm-load-many-by-sql-use-cursor-step.ts +3 -1
- package/src/lib/typeorm-step/typeorm-transactional-step-sets.ts +3 -3
- package/src/lib/typeorm-step/types.ts +2 -2
- package/test/step/parallel-step.test.ts +9 -2
- package/test/step/snippet-step.test.ts +3 -3
- package/test/step/snowflake-step.test.ts +3 -3
- package/test/step/typeorm-by-cursor.test.ts +2 -2
- package/test/step/typeorm-by-sql-autonomous.test.ts +7 -6
- package/test/step/typeorm-by-sql-transactional.test.ts +3 -1
|
@@ -38,6 +38,7 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
38
38
|
extends AbstractFragmentaryPipelineStep<In, Out, InFragment, OutFragment> {
|
|
39
39
|
private readonly _endpointSystemCode: string;
|
|
40
40
|
private readonly _endpointName: string;
|
|
41
|
+
private readonly _endpointKey: string;
|
|
41
42
|
private readonly _endpointUrl: string;
|
|
42
43
|
private readonly _endpointMethod: string;
|
|
43
44
|
private readonly _endpointHeaders: Record<string, string>;
|
|
@@ -48,6 +49,8 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
48
49
|
private readonly _omittedTransparentHeaderNames: Array<string>;
|
|
49
50
|
private readonly _headersGenerateSnippet: ScriptFuncOrBody<HttpGenerateHeaders<In, InFragment>>;
|
|
50
51
|
private readonly _headersGenerateFunc: HttpGenerateHeaders<In, InFragment>;
|
|
52
|
+
private readonly _endpointTraceIdHeaderName?: string;
|
|
53
|
+
private readonly _endpointTraceIdScope?: 'system' | 'endpoint';
|
|
51
54
|
private readonly _bodyUsed: boolean;
|
|
52
55
|
private readonly _bodyGenerateSnippet: ScriptFuncOrBody<HttpGenerateBody<In, InFragment, BodyData>>;
|
|
53
56
|
private readonly _bodyGenerateFunc: HttpGenerateBody<In, InFragment, BodyData>;
|
|
@@ -60,16 +63,16 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
60
63
|
const config = this.getConfig();
|
|
61
64
|
this._endpointSystemCode = options.endpointSystemCode;
|
|
62
65
|
this._endpointName = options.endpointName;
|
|
63
|
-
|
|
64
|
-
this._endpointUrl = config.getString(`endpoints.${
|
|
66
|
+
this._endpointKey = `${this._endpointSystemCode}.${this._endpointName}`;
|
|
67
|
+
this._endpointUrl = config.getString(`endpoints.${this._endpointKey}.url`);
|
|
65
68
|
this._endpointMethod = (options.method ?? 'POST').trim().toLowerCase();
|
|
66
69
|
this._endpointHeaders = this.generateEndpointHeaders(
|
|
67
|
-
config.getString(`endpoints.${
|
|
68
|
-
this.generateEndpointHeaders(config.getString(`endpoints.${this.
|
|
70
|
+
config.getString(`endpoints.${this._endpointKey}.headers`),
|
|
71
|
+
this.generateEndpointHeaders(config.getString(`endpoints.${this._endpointSystemCode}.global.headers`)));
|
|
69
72
|
// in second
|
|
70
73
|
this._endpointTimeout = options.timeout
|
|
71
|
-
?? config.getNumber(`endpoints.${
|
|
72
|
-
?? config.getNumber(`endpoints.${this.
|
|
74
|
+
?? config.getNumber(`endpoints.${this._endpointKey}.timeout`)
|
|
75
|
+
?? config.getNumber(`endpoints.${this._endpointSystemCode}.global.timeout`, -1);
|
|
73
76
|
// to millisecond
|
|
74
77
|
this._endpointTimeout = this._endpointTimeout > 0 ? this._endpointTimeout * 1000 : -1;
|
|
75
78
|
this._urlGenerateSnippet = options.urlGenerate;
|
|
@@ -84,12 +87,12 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
84
87
|
});
|
|
85
88
|
this._transparentHeaderNames = options.transparentHeaderNames
|
|
86
89
|
?? this.generateTransparentHeaderNames(
|
|
87
|
-
config.getString(`endpoints.${
|
|
88
|
-
this.generateTransparentHeaderNames(config.getString(`endpoints.${this.
|
|
90
|
+
config.getString(`endpoints.${this._endpointKey}.headers.transparent`),
|
|
91
|
+
this.generateTransparentHeaderNames(config.getString(`endpoints.${this._endpointSystemCode}.global.headers.transparent`)));
|
|
89
92
|
this._omittedTransparentHeaderNames = options.omittedTransparentHeaderNames
|
|
90
93
|
?? this.generateTransparentHeaderNames(
|
|
91
|
-
config.getString(`endpoints.${
|
|
92
|
-
this.generateTransparentHeaderNames(config.getString(`endpoints.${this.
|
|
94
|
+
config.getString(`endpoints.${this._endpointKey}.headers.transparent.omitted`),
|
|
95
|
+
this.generateTransparentHeaderNames(config.getString(`endpoints.${this._endpointSystemCode}.global.headers.transparent.omitted`)));
|
|
93
96
|
this._headersGenerateSnippet = options.headersGenerate;
|
|
94
97
|
this._headersGenerateFunc = Utils.createAsyncFunction(this.getHeadersGenerateSnippet(), {
|
|
95
98
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -100,6 +103,20 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
100
103
|
throw e;
|
|
101
104
|
}
|
|
102
105
|
});
|
|
106
|
+
let traceIdHeaderName = config.getString(`endpoints.${this._endpointKey}.trace.header.name`)?.trim();
|
|
107
|
+
if (traceIdHeaderName != null) {
|
|
108
|
+
this._endpointTraceIdHeaderName = traceIdHeaderName.toLowerCase();
|
|
109
|
+
this._endpointTraceIdScope = 'endpoint';
|
|
110
|
+
} else {
|
|
111
|
+
traceIdHeaderName = config.getString(`endpoints.${this._endpointSystemCode}.global.trace.header.name`)?.trim();
|
|
112
|
+
if (traceIdHeaderName != null) {
|
|
113
|
+
this._endpointTraceIdHeaderName = traceIdHeaderName.toLowerCase();
|
|
114
|
+
this._endpointTraceIdScope = 'system';
|
|
115
|
+
} else {
|
|
116
|
+
this._endpointTraceIdHeaderName = (void 0);
|
|
117
|
+
this._endpointTraceIdScope = (void 0);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
103
120
|
this._bodyUsed = options.bodyUsed;
|
|
104
121
|
this._bodyGenerateSnippet = options.bodyGenerate;
|
|
105
122
|
this._bodyGenerateFunc = Utils.createAsyncFunction(this.getBodyGenerateSnippet(), {
|
|
@@ -182,7 +199,7 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
182
199
|
}
|
|
183
200
|
|
|
184
201
|
public getEndpointKey(): string {
|
|
185
|
-
return
|
|
202
|
+
return this._endpointKey;
|
|
186
203
|
}
|
|
187
204
|
|
|
188
205
|
public getEndpointUrl(): string {
|
|
@@ -257,6 +274,14 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
257
274
|
return ['$factor', '$request', ...this.getHelpersVariableNames()];
|
|
258
275
|
}
|
|
259
276
|
|
|
277
|
+
public getEndpointTraceIdHeaderName(): string | undefined {
|
|
278
|
+
return this._endpointTraceIdHeaderName;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public getEndpointTraceIdScope(): 'system' | 'endpoint' | undefined {
|
|
282
|
+
return this._endpointTraceIdScope;
|
|
283
|
+
}
|
|
284
|
+
|
|
260
285
|
protected isBodyUsed(): boolean | undefined {
|
|
261
286
|
return this._bodyUsed;
|
|
262
287
|
}
|
|
@@ -284,81 +309,159 @@ export class FetchPipelineStep<In = PipelineStepPayload, Out = PipelineStepPaylo
|
|
|
284
309
|
return ['$options', ...this.getHelpersVariableNames()];
|
|
285
310
|
}
|
|
286
311
|
|
|
287
|
-
protected
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
Object.keys(value).forEach(key => {
|
|
305
|
-
const headerValue = value[key];
|
|
306
|
-
if (headerValue != null) {
|
|
307
|
-
const s = `${headerValue}`;
|
|
308
|
-
if (s.length !== 0) {
|
|
309
|
-
headers[key] = s;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
} else {
|
|
314
|
-
const headerValue = `${value}`;
|
|
315
|
-
if (headerValue.length !== 0) {
|
|
316
|
-
headers[name] = headerValue;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return headers;
|
|
320
|
-
}, {});
|
|
321
|
-
(this.getOmittedTransparentHeaderNames() ?? []).forEach(name => delete transparentHeaders[name]);
|
|
322
|
-
|
|
323
|
-
const generatedHeaders = await this._headersGenerateFunc(data, request, $helpers, $helpers) ?? {};
|
|
324
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
-
let body: any;
|
|
326
|
-
const bodyUsed = this.isBodyUsed();
|
|
327
|
-
if (bodyUsed === true || (bodyUsed == null && method !== 'get')) {
|
|
328
|
-
body = await this._bodyGenerateFunc(data, request, $helpers, $helpers);
|
|
312
|
+
protected syncEndpointTraceId(headers: Record<string, string>, request: PipelineStepData<In>): string | undefined {
|
|
313
|
+
const endpointTraceIdHeaderName = this.getEndpointTraceIdHeaderName();
|
|
314
|
+
if (endpointTraceIdHeaderName == null) {
|
|
315
|
+
return (void 0);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const headerName = Object.keys(headers).find(name => name.toLowerCase() === endpointTraceIdHeaderName);
|
|
319
|
+
if (headerName != null) {
|
|
320
|
+
return (void 0);
|
|
321
|
+
}
|
|
322
|
+
let endpointTraceId = headers[headerName];
|
|
323
|
+
const endpointTraceIdScope = this.getEndpointTraceIdScope();
|
|
324
|
+
|
|
325
|
+
if (endpointTraceId != null) {
|
|
326
|
+
// trace id found in headers, put into context
|
|
327
|
+
if (endpointTraceIdScope === 'system') {
|
|
328
|
+
request.$context.setScopedTraceId(this.getEndpointSystemCode(), endpointTraceIdHeaderName, endpointTraceId);
|
|
329
329
|
} else {
|
|
330
|
-
|
|
330
|
+
request.$context.setScopedTraceId(this.getEndpointKey(), endpointTraceIdHeaderName, endpointTraceId);
|
|
331
331
|
}
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
} else {
|
|
333
|
+
// trace if not in headers, find in context, and put into header if found
|
|
334
|
+
if (endpointTraceIdScope === 'system') {
|
|
335
|
+
endpointTraceId = request.$context.findScopedTraceId(this.getEndpointSystemCode())?.[1];
|
|
336
|
+
} else {
|
|
337
|
+
endpointTraceId = request.$context.findScopedTraceId(this.getEndpointKey())?.[1];
|
|
334
338
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}).forEach(name => {
|
|
340
|
-
delete headers[name];
|
|
341
|
-
});
|
|
339
|
+
if (endpointTraceId != null) {
|
|
340
|
+
headers[endpointTraceIdHeaderName] = endpointTraceId;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
342
343
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
344
|
+
return endpointTraceId;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
protected async generateRequestHeaders(data: InFragment, request: PipelineStepData<In>, $helpers: PipelineStepHelpers): Promise<Record<string, string>> {
|
|
348
|
+
const staticHeaders = this.getEndpointHeaders() ?? {};
|
|
349
|
+
const transparentHeaders = (this.getTransparentHeaderNames() ?? []).reduce((headers, name) => {
|
|
350
|
+
const value = Utils.getValue(data, name);
|
|
351
|
+
if (value == null) {
|
|
352
|
+
// no value of given header name, ignored
|
|
353
|
+
} else if (Array.isArray(value)) {
|
|
354
|
+
const headerValue = value.filter(v => v != null && `${v}`.length !== 0).join(', ');
|
|
355
|
+
if (headerValue.length !== 0) {
|
|
356
|
+
headers[name] = headerValue;
|
|
357
|
+
}
|
|
358
|
+
} else if (typeof value === 'object') {
|
|
359
|
+
Object.keys(value).forEach(key => {
|
|
360
|
+
const headerValue = value[key];
|
|
361
|
+
if (headerValue != null) {
|
|
362
|
+
const s = `${headerValue}`;
|
|
363
|
+
if (s.length !== 0) {
|
|
364
|
+
headers[key] = s;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
359
368
|
} else {
|
|
360
|
-
|
|
369
|
+
const headerValue = `${value}`;
|
|
370
|
+
if (headerValue.length !== 0) {
|
|
371
|
+
headers[name] = headerValue;
|
|
372
|
+
}
|
|
361
373
|
}
|
|
374
|
+
return headers;
|
|
375
|
+
}, {});
|
|
376
|
+
(this.getOmittedTransparentHeaderNames() ?? []).forEach(name => delete transparentHeaders[name]);
|
|
377
|
+
|
|
378
|
+
const generatedHeaders = await this._headersGenerateFunc(data, request, $helpers, $helpers) ?? {};
|
|
379
|
+
const headers = {...staticHeaders, ...transparentHeaders, ...generatedHeaders};
|
|
380
|
+
// remove some headers, leave them to fetch to calculate automatically.
|
|
381
|
+
// and get trace id when existing
|
|
382
|
+
Object.keys(headers).filter(name => {
|
|
383
|
+
return ['content-encoding', 'content-length'].includes(name.toLowerCase());
|
|
384
|
+
}).forEach(name => {
|
|
385
|
+
delete headers[name];
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return headers;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
392
|
+
protected async generateRequestBody(method: string, data: InFragment, request: PipelineStepData<In>, $helpers: PipelineStepHelpers): Promise<any> {
|
|
393
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
394
|
+
let body: any;
|
|
395
|
+
const bodyUsed = this.isBodyUsed();
|
|
396
|
+
if (bodyUsed === true || (bodyUsed == null && method !== 'get')) {
|
|
397
|
+
body = await this._bodyGenerateFunc(data, request, $helpers, $helpers);
|
|
398
|
+
} else {
|
|
399
|
+
body = (void 0);
|
|
400
|
+
}
|
|
401
|
+
if (body != null && typeof body !== 'string') {
|
|
402
|
+
body = JSON.stringify(body);
|
|
403
|
+
}
|
|
404
|
+
return body;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
protected tryToRetrieveTraceIdFromResponse(request: PipelineStepData<In>, response: Response): string | undefined {
|
|
408
|
+
const endpointTraceIdHeaderName = this.getEndpointTraceIdHeaderName();
|
|
409
|
+
if (endpointTraceIdHeaderName == null) {
|
|
410
|
+
return (void 0);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const endpointTraceId = response.headers.get(endpointTraceIdHeaderName);
|
|
414
|
+
if (endpointTraceId == null || endpointTraceId.length === 0) {
|
|
415
|
+
return (void 0);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const endpointTraceIdScope = this.getEndpointTraceIdScope();
|
|
419
|
+
if (endpointTraceIdScope === 'system') {
|
|
420
|
+
request.$context.setScopedTraceId(this.getEndpointSystemCode(), endpointTraceIdHeaderName, endpointTraceId);
|
|
421
|
+
} else {
|
|
422
|
+
request.$context.setScopedTraceId(this.getEndpointKey(), endpointTraceIdHeaderName, endpointTraceId);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return endpointTraceId;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
429
|
+
protected async sendRequest(url: string, method: string, headers: Record<string, string>, body: any,
|
|
430
|
+
data: InFragment, request: PipelineStepData<In>,
|
|
431
|
+
$helpers: PipelineStepHelpers): Promise<OutFragment> {
|
|
432
|
+
const response = await fetch(url, {
|
|
433
|
+
method, headers, body,
|
|
434
|
+
signal: this.needTimeout() ? (() => {
|
|
435
|
+
const controller = new AbortController();
|
|
436
|
+
setTimeout(() => controller.abort(), this.getEndpointTimeout());
|
|
437
|
+
return controller.signal;
|
|
438
|
+
})() : (void 0)
|
|
439
|
+
});
|
|
440
|
+
// retrieve trace id from response anyway
|
|
441
|
+
this.tryToRetrieveTraceIdFromResponse(request, response);
|
|
442
|
+
const status = response.status;
|
|
443
|
+
if (status >= 400) {
|
|
444
|
+
return await this._responseErrorHandleFunc({
|
|
445
|
+
$url: url, $factor: data, $request: request, $response: response,
|
|
446
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
447
|
+
// @ts-ignore
|
|
448
|
+
$errorCode: `${status}`
|
|
449
|
+
}, $helpers, $helpers);
|
|
450
|
+
} else {
|
|
451
|
+
return await this._responseGenerateFunc(response, data, request, $helpers, $helpers);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
|
|
456
|
+
const $helpers = this.getHelpers();
|
|
457
|
+
let url = '';
|
|
458
|
+
try {
|
|
459
|
+
url = await this._urlGenerateFunc(this.getEndpointUrl(), data, request, $helpers, $helpers);
|
|
460
|
+
const method = this.getEndpointMethod();
|
|
461
|
+
const headers = await this.generateRequestHeaders(data, request, $helpers);
|
|
462
|
+
this.syncEndpointTraceId(headers, request);
|
|
463
|
+
const body = await this.generateRequestBody(method, data, request, $helpers);
|
|
464
|
+
return await this.sendRequest(url, method, headers, body, data, request, $helpers);
|
|
362
465
|
} catch (e) {
|
|
363
466
|
if (e instanceof DOMException || e.name === 'AbortError') {
|
|
364
467
|
return await this._responseErrorHandleFunc({
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
UncatchableError
|
|
12
12
|
} from '@rainbow-o23/n1';
|
|
13
13
|
import {ERR_PIPELINE_STEP_SNIPPET_NOT_EMPTY} from '../error-codes';
|
|
14
|
-
import {
|
|
14
|
+
import {PipelineStepSetsExecutionContext} from './step-sets';
|
|
15
15
|
import {
|
|
16
16
|
HandleAnyError,
|
|
17
17
|
HandleCatchableError,
|
|
@@ -264,7 +264,10 @@ export abstract class AbstractFragmentaryPipelineStep<In = PipelineStepPayload,
|
|
|
264
264
|
*/
|
|
265
265
|
protected async setToOutput($result: OutFragment, $request: PipelineStepData<In>): Promise<PipelineStepData<Out>> {
|
|
266
266
|
const $helpers = this.getHelpers();
|
|
267
|
-
return {
|
|
267
|
+
return {
|
|
268
|
+
content: await this._toResponseFunc($result, $request, $helpers, $helpers),
|
|
269
|
+
$context: $request.$context
|
|
270
|
+
};
|
|
268
271
|
}
|
|
269
272
|
|
|
270
273
|
/**
|
|
@@ -278,14 +281,8 @@ export abstract class AbstractFragmentaryPipelineStep<In = PipelineStepPayload,
|
|
|
278
281
|
return {config: this.getConfig(), logger: this.getLogger()};
|
|
279
282
|
}
|
|
280
283
|
|
|
281
|
-
protected createErrorHandleContext(request: PipelineStepData<In>):
|
|
282
|
-
|
|
283
|
-
if (context == null) {
|
|
284
|
-
return {};
|
|
285
|
-
} else {
|
|
286
|
-
const {authorization, traceId, ...rest} = context;
|
|
287
|
-
return {authorization, traceId, ...Utils.clone(rest)};
|
|
288
|
-
}
|
|
284
|
+
protected createErrorHandleContext(request: PipelineStepData<In>): PipelineStepSetsExecutionContext {
|
|
285
|
+
return request.$context;
|
|
289
286
|
}
|
|
290
287
|
|
|
291
288
|
/**
|
|
@@ -293,7 +290,7 @@ export abstract class AbstractFragmentaryPipelineStep<In = PipelineStepPayload,
|
|
|
293
290
|
*/
|
|
294
291
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
295
292
|
protected async handleErrorSteps(fragment: InFragment, errorCode: string, error: any, request: PipelineStepData<In>, builders: Array<PipelineStepBuilder>): Promise<OutFragment> {
|
|
296
|
-
const
|
|
293
|
+
const traceId = request.$context.traceId;
|
|
297
294
|
const errorContext = this.createErrorHandleContext(request);
|
|
298
295
|
const options = this.buildStepOptions();
|
|
299
296
|
const steps = await Promise.all(builders.map(async builder => await builder.create(options)));
|
|
@@ -303,7 +300,7 @@ export abstract class AbstractFragmentaryPipelineStep<In = PipelineStepPayload,
|
|
|
303
300
|
.execute(async () => {
|
|
304
301
|
this.traceStepIn(traceId, step, request);
|
|
305
302
|
const response = await step.perform({
|
|
306
|
-
...request, $context:
|
|
303
|
+
...request, $context: errorContext
|
|
307
304
|
});
|
|
308
305
|
this.traceStepOut(traceId, step, response);
|
|
309
306
|
// if no response returned, keep using request for next
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import {PipelineStepData, PipelineStepPayload} from '@rainbow-o23/n1';
|
|
2
|
-
import {PipelineStepSets} from './step-sets';
|
|
2
|
+
import {PipelineStepSets, PipelineStepSetsExecutionContext} from './step-sets';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* pipeline steps to execute sets of steps asynchronous.
|
|
6
|
+
*
|
|
7
|
+
* Execution context will be copied and will not be retrieved back to the main thread.
|
|
8
|
+
* The transaction context will not be passed to asynchronous sub - steps.
|
|
6
9
|
*/
|
|
7
10
|
export class AsyncPipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In>
|
|
8
11
|
extends PipelineStepSets<In, Out, InFragment, void> {
|
|
12
|
+
|
|
13
|
+
protected inheritContext(request: PipelineStepData<In>): PipelineStepSetsExecutionContext {
|
|
14
|
+
// $trans does not inherit by sub step
|
|
15
|
+
return request.$context.clone('$trans');
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<void> {
|
|
10
19
|
// noinspection ES6MissingAwait
|
|
11
20
|
super.doPerform(data, request);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PipelineStepData, PipelineStepHelpers, PipelineStepPayload} from '@rainbow-o23/n1';
|
|
2
|
-
import {PipelineStepSets,
|
|
2
|
+
import {PipelineStepSets, PipelineStepSetsExecutionContext, PipelineStepSetsOptions} from './step-sets';
|
|
3
3
|
import {ScriptFuncOrBody} from './types';
|
|
4
4
|
import {Utils} from './utils';
|
|
5
5
|
|
|
@@ -61,15 +61,15 @@ export class ParallelPipelineStepSets<In = PipelineStepPayload, Out = PipelineSt
|
|
|
61
61
|
|
|
62
62
|
protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
|
|
63
63
|
return await this.performWithContext(
|
|
64
|
-
request, async (request: PipelineStepData<In>, context:
|
|
65
|
-
const
|
|
64
|
+
request, async (request: PipelineStepData<In>, context: PipelineStepSetsExecutionContext): Promise<OutFragment> => {
|
|
65
|
+
const traceId = context.traceId;
|
|
66
66
|
const steps = await this.createSteps();
|
|
67
67
|
const execute = () => {
|
|
68
68
|
return steps.map(async step => {
|
|
69
69
|
return await this.measurePerformance(traceId, 'STEP', step.constructor.name)
|
|
70
70
|
.execute(async () => {
|
|
71
71
|
const eachData = await this.cloneDataForEach(data, request);
|
|
72
|
-
const eachRequest = {content: eachData, $context:
|
|
72
|
+
const eachRequest = {content: eachData, $context: context};
|
|
73
73
|
this.traceStepIn(traceId, step, request);
|
|
74
74
|
const response = await step.perform(eachRequest);
|
|
75
75
|
this.traceStepOut(traceId, step, response);
|
|
@@ -56,9 +56,8 @@ export class RefPipelinePipelineStep<In = PipelineStepPayload, Out = PipelineSte
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
|
|
59
|
-
const {$context: {authorization, traceId} = {}} = request;
|
|
60
59
|
const pipeline = await this.getPipelineBuilder().create(this.buildPipelineOptions());
|
|
61
|
-
const result = await pipeline.perform({payload: data,
|
|
60
|
+
const result = await pipeline.perform({payload: data, $context: request.$context});
|
|
62
61
|
return result.payload;
|
|
63
62
|
}
|
|
64
63
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
PipelineExecutionContext,
|
|
2
3
|
PipelineStep,
|
|
3
4
|
PipelineStepBuilder,
|
|
4
5
|
PipelineStepData,
|
|
@@ -6,10 +7,9 @@ import {
|
|
|
6
7
|
PipelineStepPayload
|
|
7
8
|
} from '@rainbow-o23/n1';
|
|
8
9
|
import {AbstractFragmentaryPipelineStep, FragmentaryPipelineStepOptions} from './abstract-fragmentary-pipeline-step';
|
|
9
|
-
import {Utils} from './utils';
|
|
10
10
|
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
12
|
-
export interface
|
|
12
|
+
export interface PipelineStepSetsExecutionContext extends PipelineExecutionContext {
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface PipelineStepSetsOptions<In = PipelineStepPayload, Out = PipelineStepPayload, InFragment = In, OutFragment = Out>
|
|
@@ -46,25 +46,19 @@ export class PipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayloa
|
|
|
46
46
|
return await Promise.all(this.getStepBuilders().map(async builder => await builder.create(options)));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
protected inheritContext(request: PipelineStepData<In>):
|
|
50
|
-
|
|
51
|
-
if (context == null) {
|
|
52
|
-
return {};
|
|
53
|
-
} else {
|
|
54
|
-
const {authorization, traceId, ...rest} = context;
|
|
55
|
-
return {authorization, traceId, ...Utils.clone(rest)};
|
|
56
|
-
}
|
|
49
|
+
protected inheritContext(request: PipelineStepData<In>): PipelineStepSetsExecutionContext {
|
|
50
|
+
return request.$context;
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
/**
|
|
60
54
|
* default do nothing, return given inherited context directly
|
|
61
55
|
*/
|
|
62
56
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
|
-
protected async attachMineToInternalContext(inheritedContext:
|
|
57
|
+
protected async attachMineToInternalContext(inheritedContext: PipelineStepSetsExecutionContext, _request: PipelineStepData<In>): Promise<PipelineStepSetsExecutionContext> {
|
|
64
58
|
return inheritedContext;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
protected async createInternalContext<Ctx extends
|
|
61
|
+
protected async createInternalContext<Ctx extends PipelineStepSetsExecutionContext>(request: PipelineStepData<In>): Promise<Ctx> {
|
|
68
62
|
return await this.attachMineToInternalContext(this.inheritContext(request), request) as Ctx;
|
|
69
63
|
}
|
|
70
64
|
|
|
@@ -73,15 +67,15 @@ export class PipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayloa
|
|
|
73
67
|
*/
|
|
74
68
|
protected async performWithContext(
|
|
75
69
|
request: PipelineStepData<In>,
|
|
76
|
-
run: (request: PipelineStepData<In>, context:
|
|
70
|
+
run: (request: PipelineStepData<In>, context: PipelineStepSetsExecutionContext) => Promise<OutFragment>): Promise<OutFragment> {
|
|
77
71
|
const context = await this.createInternalContext(request);
|
|
78
72
|
return await run(request, context);
|
|
79
73
|
}
|
|
80
74
|
|
|
81
75
|
protected async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
|
|
82
76
|
return await this.performWithContext(
|
|
83
|
-
request, async (request: PipelineStepData<In>, context:
|
|
84
|
-
const
|
|
77
|
+
request, async (request: PipelineStepData<In>, context: PipelineStepSetsExecutionContext): Promise<OutFragment> => {
|
|
78
|
+
const traceId = context.traceId;
|
|
85
79
|
const steps = await this.createSteps();
|
|
86
80
|
const response = await steps.reduce(async (promise, step) => {
|
|
87
81
|
const request = await promise;
|
|
@@ -89,7 +83,7 @@ export class PipelineStepSets<In = PipelineStepPayload, Out = PipelineStepPayloa
|
|
|
89
83
|
.execute(async () => {
|
|
90
84
|
this.traceStepIn(traceId, step, request);
|
|
91
85
|
const response = await step.perform({
|
|
92
|
-
...request, $context:
|
|
86
|
+
...request, $context: context
|
|
93
87
|
});
|
|
94
88
|
this.traceStepOut(traceId, step, response);
|
|
95
89
|
// if no response returned, keep using request for next
|
|
@@ -105,7 +105,9 @@ export class TypeOrmLoadManyBySQLUseCursorPipelineStep<In = PipelineStepPayload,
|
|
|
105
105
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
106
106
|
const {content: _, $context, ...rest} = request;
|
|
107
107
|
// pass a cursor end indicator to sub steps
|
|
108
|
-
const contextForSub = {
|
|
108
|
+
const contextForSub = $context.temporaryWith({
|
|
109
|
+
$typeOrmCursorRound: cursorRound, $typeOrmCursorEnd: end
|
|
110
|
+
});
|
|
109
111
|
const requestForSub = {...rest, $context: contextForSub, content: contentForSub};
|
|
110
112
|
const result = await sets.perform(requestForSub);
|
|
111
113
|
const {content} = result;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {PipelineStepData, PipelineStepPayload, UncatchableError, Undefinable} from '@rainbow-o23/n1';
|
|
2
2
|
import {QueryRunner} from 'typeorm';
|
|
3
3
|
import {ERR_TYPEORM_DATASOURCE_NOT_FOUND} from '../error-codes';
|
|
4
|
-
import {PipelineStepSets,
|
|
4
|
+
import {PipelineStepSets, PipelineStepSetsExecutionContext, PipelineStepSetsOptions} from '../step';
|
|
5
5
|
import {TypeOrmDataSource, TypeOrmDataSourceManager} from '../typeorm';
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_TRANSACTION_NAME,
|
|
@@ -69,7 +69,7 @@ export class TypeOrmTransactionalPipelineStepSets<In = PipelineStepPayload, Out
|
|
|
69
69
|
* inherit transaction (use runner in context) if exists, otherwise create new runner
|
|
70
70
|
*/
|
|
71
71
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
72
|
-
protected async attachMineToInternalContext(inheritedContext:
|
|
72
|
+
protected async attachMineToInternalContext(inheritedContext: PipelineStepSetsExecutionContext, _request: PipelineStepData<In>): Promise<TypeOrmTransactionalContext> {
|
|
73
73
|
const context = inheritedContext as TypeOrmTransactionalContext;
|
|
74
74
|
if (context.$trans != null) {
|
|
75
75
|
if (context.$trans[this.getTransactionKey()] != null) {
|
|
@@ -96,7 +96,7 @@ export class TypeOrmTransactionalPipelineStepSets<In = PipelineStepPayload, Out
|
|
|
96
96
|
*/
|
|
97
97
|
protected async performWithContext(
|
|
98
98
|
request: PipelineStepData<In>,
|
|
99
|
-
run: (request: PipelineStepData<In>, context:
|
|
99
|
+
run: (request: PipelineStepData<In>, context: PipelineStepSetsExecutionContext) => Promise<OutFragment>): Promise<OutFragment> {
|
|
100
100
|
let runner: QueryRunner = null;
|
|
101
101
|
try {
|
|
102
102
|
const context: TypeOrmTransactionalContext = await this.createInternalContext(request);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {DeepPartial, ObjectLiteral, QueryRunner} from 'typeorm';
|
|
2
|
-
import {
|
|
2
|
+
import {PipelineStepSetsExecutionContext} from '../step';
|
|
3
3
|
import {TypeOrmDataSource} from '../typeorm';
|
|
4
4
|
|
|
5
5
|
export type TypeOrmIdType = string | number | bigint;
|
|
@@ -19,6 +19,6 @@ export type TypeOrmBulkWrittenResult = TypeOrmIdsOfInserted | TypeOrmCountsOfAff
|
|
|
19
19
|
|
|
20
20
|
export const DEFAULT_TRANSACTION_NAME: TypeOrmTransactionName = '$default-transaction';
|
|
21
21
|
|
|
22
|
-
export interface TypeOrmTransactionalContext extends
|
|
22
|
+
export interface TypeOrmTransactionalContext extends PipelineStepSetsExecutionContext {
|
|
23
23
|
$trans: Record<TypeOrmTransactionKey, [TypeOrmDataSource, QueryRunner]>;
|
|
24
24
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createConfig,
|
|
3
|
+
createLogger,
|
|
4
|
+
PipelineExecutionContext,
|
|
5
|
+
PipelineStep,
|
|
6
|
+
PipelineStepBuilder,
|
|
7
|
+
PipelineStepOptions
|
|
8
|
+
} from '@rainbow-o23/n1';
|
|
2
9
|
import {ParallelPipelineStepSets, SnippetPipelineStep} from '../../src';
|
|
3
10
|
|
|
4
11
|
const logger = createLogger();
|
|
@@ -19,7 +26,7 @@ test('Parallel Pipeline Step Test #1, + 100', async () => {
|
|
|
19
26
|
}
|
|
20
27
|
]
|
|
21
28
|
});
|
|
22
|
-
const request = {content: {base: 1}};
|
|
29
|
+
const request = {content: {base: 1}, $context: new PipelineExecutionContext()};
|
|
23
30
|
const response = await step.perform(request);
|
|
24
31
|
expect(response.content).toEqual([100, 200]);
|
|
25
32
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {createConfig, createLogger} from '@rainbow-o23/n1';
|
|
1
|
+
import {createConfig, createLogger, PipelineExecutionContext} from '@rainbow-o23/n1';
|
|
2
2
|
import {SnippetPipelineStep} from '../../src';
|
|
3
3
|
|
|
4
4
|
const logger = createLogger();
|
|
@@ -7,7 +7,7 @@ const config = createConfig(logger);
|
|
|
7
7
|
test('Snippet Pipeline Step Test #1, + 100', async () => {
|
|
8
8
|
const snippet = 'return $factor.base + 100;';
|
|
9
9
|
const step = new SnippetPipelineStep({config, logger, snippet});
|
|
10
|
-
const request = {content: {base: 1}};
|
|
10
|
+
const request = {content: {base: 1}, $context: new PipelineExecutionContext()};
|
|
11
11
|
const response = await step.perform(request);
|
|
12
12
|
expect(response.content).toEqual(101);
|
|
13
13
|
});
|
|
@@ -15,7 +15,7 @@ test('Snippet Pipeline Step Test #1, + 100', async () => {
|
|
|
15
15
|
test('Snippet Pipeline Step Test #2, async + 100', async () => {
|
|
16
16
|
const snippet = 'return await new Promise(resolve => resolve($factor.base + 100));';
|
|
17
17
|
const step = new SnippetPipelineStep({config, logger, snippet});
|
|
18
|
-
const request = {content: {base: 1}};
|
|
18
|
+
const request = {content: {base: 1}, $context: new PipelineExecutionContext()};
|
|
19
19
|
const response = await step.perform(request);
|
|
20
20
|
expect(response.content).toEqual(101);
|
|
21
21
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {createConfig, createLogger} from '@rainbow-o23/n1';
|
|
1
|
+
import {createConfig, createLogger, PipelineExecutionContext} from '@rainbow-o23/n1';
|
|
2
2
|
import {SnowflakePipelineStep} from '../../src';
|
|
3
3
|
|
|
4
4
|
const logger = createLogger();
|
|
@@ -6,7 +6,7 @@ const config = createConfig(logger);
|
|
|
6
6
|
|
|
7
7
|
test('Snowflake Pipeline Step Test #1, replace content', async () => {
|
|
8
8
|
const step = new SnowflakePipelineStep({config, logger, mergeRequest: '$id'});
|
|
9
|
-
const request = {content: (void 0)};
|
|
9
|
+
const request = {content: (void 0), $context: new PipelineExecutionContext()};
|
|
10
10
|
const response = await step.perform(request);
|
|
11
11
|
expect(response.content).not.toBeNull();
|
|
12
12
|
expect(response.content.$id).not.toBeNull();
|
|
@@ -14,7 +14,7 @@ test('Snowflake Pipeline Step Test #1, replace content', async () => {
|
|
|
14
14
|
|
|
15
15
|
test('Snowflake Pipeline Step Test #2, merge content', async () => {
|
|
16
16
|
const step = new SnowflakePipelineStep({config, logger, mergeRequest: '$id'});
|
|
17
|
-
const request = {content: {base: 1}};
|
|
17
|
+
const request = {content: {base: 1}, $context: new PipelineExecutionContext()};
|
|
18
18
|
const response = await step.perform(request);
|
|
19
19
|
expect(response.content).not.toBeNull();
|
|
20
20
|
expect(response.content.base).toEqual(1);
|