@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.
@@ -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
- const endpointKey = this.getEndpointKey();
64
- this._endpointUrl = config.getString(`endpoints.${endpointKey}.url`);
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.${endpointKey}.headers`),
68
- this.generateEndpointHeaders(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers`)));
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.${endpointKey}.timeout`)
72
- ?? config.getNumber(`endpoints.${this.getEndpointSystemCode()}.global.timeout`, -1);
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.${endpointKey}.headers.transparent`),
88
- this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent`)));
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.${endpointKey}.headers.transparent.omitted`),
92
- this.generateTransparentHeaderNames(config.getString(`endpoints.${this.getEndpointSystemCode()}.global.headers.transparent.omitted`)));
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 `${this.getEndpointSystemCode()}.${this.getEndpointName()}`;
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 async doPerform(data: InFragment, request: PipelineStepData<In>): Promise<OutFragment> {
288
- const $helpers = this.getHelpers();
289
- let url = '';
290
- try {
291
- url = await this._urlGenerateFunc(this.getEndpointUrl(), data, request, $helpers, $helpers);
292
- const method = this.getEndpointMethod();
293
- const staticHeaders = this.getEndpointHeaders() ?? {};
294
- const transparentHeaders = (this.getTransparentHeaderNames() ?? []).reduce((headers, name) => {
295
- const value = Utils.getValue(data, name);
296
- if (value == null) {
297
- // no value of given header name, ignored
298
- } else if (Array.isArray(value)) {
299
- const headerValue = value.filter(v => v != null && `${v}`.length !== 0).join(', ');
300
- if (headerValue.length !== 0) {
301
- headers[name] = headerValue;
302
- }
303
- } else if (typeof value === 'object') {
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
- body = (void 0);
330
+ request.$context.setScopedTraceId(this.getEndpointKey(), endpointTraceIdHeaderName, endpointTraceId);
331
331
  }
332
- if (body != null && typeof body !== 'string') {
333
- body = JSON.stringify(body);
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
- const headers = {...staticHeaders, ...transparentHeaders, ...generatedHeaders};
336
- // remove some headers, leave them to fetch to calculate automatically.
337
- Object.keys(headers).filter(name => {
338
- return ['content-encoding', 'content-length'].includes(name.toLowerCase());
339
- }).forEach(name => {
340
- delete headers[name];
341
- });
339
+ if (endpointTraceId != null) {
340
+ headers[endpointTraceIdHeaderName] = endpointTraceId;
341
+ }
342
+ }
342
343
 
343
- const response = await fetch(url, {
344
- method, headers, body,
345
- signal: this.needTimeout() ? (() => {
346
- const controller = new AbortController();
347
- setTimeout(() => controller.abort(), this.getEndpointTimeout());
348
- return controller.signal;
349
- })() : (void 0)
350
- });
351
- const status = response.status;
352
- if (status >= 400) {
353
- return await this._responseErrorHandleFunc({
354
- $url: url, $factor: data, $request: request, $response: response,
355
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
356
- // @ts-ignore
357
- $errorCode: `${status}`
358
- }, $helpers, $helpers);
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
- return await this._responseGenerateFunc(response, data, request, $helpers, $helpers);
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 {PipelineStepSetsContext} from './step-sets';
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 {content: await this._toResponseFunc($result, $request, $helpers, $helpers)};
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>): PipelineStepSetsContext {
282
- const context = request.$context;
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 {$context: {authorization, traceId} = {}} = request;
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: {...errorContext, authorization, traceId}
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, PipelineStepSetsContext, PipelineStepSetsOptions} from './step-sets';
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: PipelineStepSetsContext): Promise<OutFragment> => {
65
- const {$context: {authorization, traceId} = {}} = request;
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: {...context, authorization, traceId}};
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, authorization, traceId});
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 PipelineStepSetsContext {
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>): PipelineStepSetsContext {
50
- const context = request.$context;
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: PipelineStepSetsContext, _request: PipelineStepData<In>): Promise<PipelineStepSetsContext> {
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 PipelineStepSetsContext>(request: PipelineStepData<In>): Promise<Ctx> {
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: PipelineStepSetsContext) => Promise<OutFragment>): Promise<OutFragment> {
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: PipelineStepSetsContext): Promise<OutFragment> => {
84
- const {$context: {authorization, traceId} = {}} = request;
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: {...context, authorization, traceId}
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 = {...$context, $typeOrmCursorRound: cursorRound, $typeOrmCursorEnd: end};
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, PipelineStepSetsContext, PipelineStepSetsOptions} from '../step';
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: PipelineStepSetsContext, _request: PipelineStepData<In>): Promise<TypeOrmTransactionalContext> {
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: PipelineStepSetsContext) => Promise<OutFragment>): Promise<OutFragment> {
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 {PipelineStepSetsContext} from '../step';
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 PipelineStepSetsContext {
22
+ export interface TypeOrmTransactionalContext extends PipelineStepSetsExecutionContext {
23
23
  $trans: Record<TypeOrmTransactionKey, [TypeOrmDataSource, QueryRunner]>;
24
24
  }
@@ -1,4 +1,11 @@
1
- import {createConfig, createLogger, PipelineStep, PipelineStepBuilder, PipelineStepOptions} from '@rainbow-o23/n1';
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);