@izara_project/izara-core-library-asynchronous-flow 1.0.42 → 1.0.44
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/package.json +5 -4
- package/src/asyncFlowSharedLib.js +102 -75
- package/src/awaitingMultipleSteps.js +113 -73
- package/src/awaitingStep.js +12 -10
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"author": "Sven Mason <thebarbariansven@gmail.com>",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"homepage": "https://bitbucket.org/izara-core-libraries/izara-core-library-asynchronous-flow#readme",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.44",
|
|
7
7
|
"description": "Shared asynchronous flow logic",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "index.js",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@izara_project/izara-core-library-core": "^1.0.32",
|
|
19
|
-
"@izara_project/izara-core-library-dynamodb": "^1.0.
|
|
19
|
+
"@izara_project/izara-core-library-dynamodb": "^1.0.21",
|
|
20
20
|
"@izara_project/izara-core-library-external-request": "^1.0.30",
|
|
21
|
-
"@izara_project/izara-core-library-logger": "^1.0.
|
|
21
|
+
"@izara_project/izara-core-library-logger": "^1.0.11",
|
|
22
22
|
"@izara_project/izara-core-library-sns": "^1.0.8",
|
|
23
23
|
"@izara_project/izara-core-library-sqs": "^1.0.7",
|
|
24
24
|
"@izara_project/izara-shared-core": "^1.0.13"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@izara_project/izara-core-library-logger": "^1.0.9",
|
|
31
31
|
"@izara_project/izara-core-library-sns": "^1.0.8",
|
|
32
32
|
"@izara_project/izara-core-library-sqs": "^1.0.7",
|
|
33
|
-
"@izara_project/izara-shared-core": "^1.0.12"
|
|
33
|
+
"@izara_project/izara-shared-core": "^1.0.12",
|
|
34
|
+
"ws": "^8.16.0"
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -36,20 +36,25 @@ import { identifierUuid } from '@izara_project/izara-shared-core';
|
|
|
36
36
|
* @returns {string}
|
|
37
37
|
*/
|
|
38
38
|
function _joinPrefixAndId(id, prefix = '') {
|
|
39
|
-
if (!prefix)
|
|
39
|
+
if (!prefix) {
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let cleanPrefix = prefix;
|
|
44
|
+
if (prefix.endsWith('_')) {
|
|
45
|
+
cleanPrefix = prefix.slice(0, -1);
|
|
46
|
+
}
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
let cleanId = id;
|
|
49
|
+
if (id.startsWith('_')) {
|
|
50
|
+
cleanId = id.slice(1);
|
|
51
|
+
}
|
|
43
52
|
|
|
44
53
|
return `${cleanPrefix}_${cleanId}`;
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
57
|
* Create awaitingStepId by concatenating prefix and partitionKey.
|
|
49
|
-
* If prefix ends with an underscore, it's removed before concatenation.
|
|
50
|
-
* If partitionKey starts with an underscore, it's removed before concatenation.
|
|
51
|
-
* This ensures a clean "PREFIX_PARTITIONKEY" format without double underscores.
|
|
52
|
-
*
|
|
53
58
|
* @param {string} partitionKey
|
|
54
59
|
* @param {string} [prefix='']
|
|
55
60
|
* @returns {string}
|
|
@@ -60,10 +65,6 @@ function createAwaitingStepId(partitionKey, prefix = '') {
|
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Create pendingStepId by concatenating prefix and identifierId.
|
|
63
|
-
* If prefix ends with an underscore, it's removed before concatenation.
|
|
64
|
-
* If identifierId starts with an underscore, it's removed before concatenation.
|
|
65
|
-
* This ensures a clean "PREFIX_IDENTIFIER" format without double underscores.
|
|
66
|
-
*
|
|
67
68
|
* @param {string} identifierId
|
|
68
69
|
* @param {string} [prefix='']
|
|
69
70
|
* @returns {string}
|
|
@@ -73,7 +74,7 @@ function createPendingStepId(identifierId, prefix = '') {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Create
|
|
77
|
+
* Create field name for storing unique-request id, with optional prefix.
|
|
77
78
|
* @param {string} prefix
|
|
78
79
|
* @param {string} [uniqueRequestIdFieldName='UniqueRequestId']
|
|
79
80
|
* @returns {string}
|
|
@@ -117,26 +118,29 @@ function createParentFlowId(prefix = '') {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
|
-
* Remove
|
|
121
|
-
* If prefix is empty, the original ID is returned.
|
|
121
|
+
* Remove known prefix from pendingStepId and validate not equal to prefix.
|
|
122
122
|
* @param {string} pendingStepId
|
|
123
123
|
* @param {string} [prefix=""]
|
|
124
124
|
* @returns {string}
|
|
125
|
-
* @throws {NoRetryError} If
|
|
125
|
+
* @throws {NoRetryError} If stripped value equals prefix (invalid)
|
|
126
126
|
*/
|
|
127
127
|
function explodePendingStepId(pendingStepId, prefix = '') {
|
|
128
|
-
if (!pendingStepId)
|
|
129
|
-
|
|
128
|
+
if (!pendingStepId) {
|
|
129
|
+
throw new NoRetryError('pendingStepId is required');
|
|
130
|
+
}
|
|
131
|
+
if (!prefix) {
|
|
132
|
+
return pendingStepId;
|
|
133
|
+
}
|
|
130
134
|
|
|
131
|
-
const identifierId = pendingStepId.slice(prefix.length);
|
|
132
|
-
if (identifierId === prefix)
|
|
135
|
+
const identifierId = pendingStepId.slice(prefix.length + 1);
|
|
136
|
+
if (identifierId === prefix) {
|
|
133
137
|
throw new NoRetryError('IdentifierId should not be like prefix.');
|
|
138
|
+
}
|
|
134
139
|
|
|
135
|
-
Logger.debug('return explode:', identifierId);
|
|
140
|
+
Logger.debug('return explode:', { identifierId });
|
|
136
141
|
return identifierId;
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
// copy from izara-core-library-trigger-cache
|
|
140
144
|
/**
|
|
141
145
|
* Checks if record has uniqueRequestId set, if not set to this requests uniqueRequestId and continue to process
|
|
142
146
|
* if already set check if matches this request uniqueRequestId, if yes continue to process, if not then stop processing
|
|
@@ -161,11 +165,11 @@ function explodePendingStepId(pendingStepId, prefix = '') {
|
|
|
161
165
|
*
|
|
162
166
|
* @async
|
|
163
167
|
* @param {IzContext} _izContext
|
|
164
|
-
* @param {string} tableName - Logical table name
|
|
165
|
-
* @param {DynamoKey} primaryKey - Primary key for
|
|
168
|
+
* @param {string} tableName - Logical table name
|
|
169
|
+
* @param {DynamoKey} primaryKey - Primary key for target record
|
|
166
170
|
* @param {string} [prefix=''] - Optional field-name prefix
|
|
167
|
-
* @param {string|null} [overwriteUniqueRequestId=null] -
|
|
168
|
-
* @param {string} [uniqueRequestIdFieldName="UniqueRequestId"] - Base
|
|
171
|
+
* @param {string|null} [overwriteUniqueRequestId=null] - Overwrite ID
|
|
172
|
+
* @param {string} [uniqueRequestIdFieldName="UniqueRequestId"] - Base name
|
|
169
173
|
* @returns {Promise<[UniqueRequestStatus, Attrs]>}
|
|
170
174
|
* @throws {NoRetryError} If unable to set uniqueRequestId after 2 tries
|
|
171
175
|
*/
|
|
@@ -178,7 +182,7 @@ async function checkUniqueRequestProcessing(
|
|
|
178
182
|
uniqueRequestIdFieldName = 'UniqueRequestId' // if set will check/add this fieldname with _izContext.uniqueRequestId value
|
|
179
183
|
) {
|
|
180
184
|
_izContext.logger.debug(
|
|
181
|
-
'[Lib:AsyncFlow:checkUniqueRequestProcessing] Input:
|
|
185
|
+
'[Lib:AsyncFlow:checkUniqueRequestProcessing] Input:',
|
|
182
186
|
{
|
|
183
187
|
_izContext,
|
|
184
188
|
tableName,
|
|
@@ -194,13 +198,15 @@ async function checkUniqueRequestProcessing(
|
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
try {
|
|
197
|
-
_izContext.logger.debug(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
+
_izContext.logger.debug('[Lib:checkUniqueRequestProcessing] reqId', {
|
|
202
|
+
uniqueRequestId: _izContext.uniqueRequestId
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
let uniqueRequestId = overwriteUniqueRequestId;
|
|
206
|
+
if (!uniqueRequestId) {
|
|
207
|
+
uniqueRequestId = _izContext.uniqueRequestId;
|
|
208
|
+
}
|
|
201
209
|
|
|
202
|
-
const uniqueRequestId =
|
|
203
|
-
overwriteUniqueRequestId || _izContext.uniqueRequestId;
|
|
204
210
|
uniqueRequestIdFieldName = createFieldNameUniqueRequestId(
|
|
205
211
|
prefix,
|
|
206
212
|
uniqueRequestIdFieldName
|
|
@@ -213,11 +219,17 @@ async function checkUniqueRequestProcessing(
|
|
|
213
219
|
primaryKey
|
|
214
220
|
);
|
|
215
221
|
|
|
216
|
-
if (!existingRecord)
|
|
222
|
+
if (!existingRecord) {
|
|
223
|
+
return ['recordNotFound', {}];
|
|
224
|
+
}
|
|
217
225
|
|
|
218
226
|
const currentReqId = existingRecord[uniqueRequestIdFieldName];
|
|
219
|
-
if (currentReqId === uniqueRequestId)
|
|
220
|
-
|
|
227
|
+
if (currentReqId === uniqueRequestId) {
|
|
228
|
+
return ['process', existingRecord];
|
|
229
|
+
}
|
|
230
|
+
if (currentReqId) {
|
|
231
|
+
return ['stop', existingRecord];
|
|
232
|
+
}
|
|
221
233
|
|
|
222
234
|
try {
|
|
223
235
|
const updateRecord = await dynamodbSharedLib.updateItem(
|
|
@@ -245,17 +257,22 @@ async function checkUniqueRequestProcessing(
|
|
|
245
257
|
);
|
|
246
258
|
return ['process', updateRecord];
|
|
247
259
|
} catch (err) {
|
|
248
|
-
if (err.name === 'ConditionalCheckFailedException')
|
|
260
|
+
if (err.name === 'ConditionalCheckFailedException') {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
249
263
|
throw err;
|
|
250
264
|
}
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
throw new NoRetryError(
|
|
254
|
-
`unable to set uniqueRequestId in table ${tableName}
|
|
255
|
-
primaryKey
|
|
268
|
+
`unable to set uniqueRequestId in table ${tableName}`,
|
|
269
|
+
{ primaryKey }
|
|
256
270
|
);
|
|
257
271
|
} catch (err) {
|
|
258
|
-
_izContext.logger.error(
|
|
272
|
+
_izContext.logger.error(
|
|
273
|
+
'[Lib:checkUniqueRequestProcessing] Error',
|
|
274
|
+
{ err }
|
|
275
|
+
);
|
|
259
276
|
throw err;
|
|
260
277
|
}
|
|
261
278
|
}
|
|
@@ -266,9 +283,9 @@ async function checkUniqueRequestProcessing(
|
|
|
266
283
|
* Returns [isSameOrUnset, uniqueRequestIdWhenComplete].
|
|
267
284
|
* @async
|
|
268
285
|
* @param {IzContext} _izContext
|
|
269
|
-
* @param {string} fullMainTableName - Fully resolved table name
|
|
286
|
+
* @param {string} fullMainTableName - Fully resolved table name
|
|
270
287
|
* @param {DynamoKey} keyValues
|
|
271
|
-
* @param {number|string} timeCacheComplete -
|
|
288
|
+
* @param {number|string} timeCacheComplete - Expected cache mark
|
|
272
289
|
* @param {string} prefix - Field prefix used in cache fields
|
|
273
290
|
* @returns {Promise<[boolean, string|undefined]>}
|
|
274
291
|
*/
|
|
@@ -279,7 +296,7 @@ async function checkTimeCacheComplete(
|
|
|
279
296
|
timeCacheComplete,
|
|
280
297
|
prefix
|
|
281
298
|
) {
|
|
282
|
-
_izContext.logger.debug('[Lib:AsyncFlow:checkTimeCacheComplete] Input:
|
|
299
|
+
_izContext.logger.debug('[Lib:AsyncFlow:checkTimeCacheComplete] Input:', {
|
|
283
300
|
_izContext,
|
|
284
301
|
fullMainTableName,
|
|
285
302
|
keyValues,
|
|
@@ -289,11 +306,12 @@ async function checkTimeCacheComplete(
|
|
|
289
306
|
|
|
290
307
|
if (!fullMainTableName || !keyValues || !timeCacheComplete || !prefix) {
|
|
291
308
|
throw new NoRetryError(
|
|
292
|
-
'fullMainTableName, keyValues, timeCacheComplete
|
|
309
|
+
'fullMainTableName, keyValues, timeCacheComplete' +
|
|
310
|
+
' and prefix are required'
|
|
293
311
|
);
|
|
294
312
|
}
|
|
295
313
|
|
|
296
|
-
|
|
314
|
+
const [returnValue, , uniqueRequestId] =
|
|
297
315
|
await checkAndGetTimeCacheComplete(
|
|
298
316
|
_izContext,
|
|
299
317
|
fullMainTableName,
|
|
@@ -310,7 +328,7 @@ async function checkTimeCacheComplete(
|
|
|
310
328
|
* Otherwise returns [true, storedValue, uniqueRequestId].
|
|
311
329
|
* @async
|
|
312
330
|
* @param {IzContext} _izContext
|
|
313
|
-
* @param {string} fullMainTableName - Fully resolved table name
|
|
331
|
+
* @param {string} fullMainTableName - Fully resolved table name
|
|
314
332
|
* @param {DynamoKey} keyValues
|
|
315
333
|
* @param {number|string} timeCacheComplete
|
|
316
334
|
* @param {string} prefix
|
|
@@ -325,27 +343,26 @@ async function checkAndGetTimeCacheComplete(
|
|
|
325
343
|
prefix
|
|
326
344
|
) {
|
|
327
345
|
_izContext.logger.debug(
|
|
328
|
-
'[Lib:AsyncFlow:checkAndGetTimeCacheComplete] Input:
|
|
346
|
+
'[Lib:AsyncFlow:checkAndGetTimeCacheComplete] Input:',
|
|
329
347
|
{
|
|
330
|
-
fullMainTableName
|
|
331
|
-
keyValues
|
|
332
|
-
timeCacheComplete
|
|
333
|
-
prefix
|
|
348
|
+
fullMainTableName,
|
|
349
|
+
keyValues,
|
|
350
|
+
timeCacheComplete,
|
|
351
|
+
prefix
|
|
334
352
|
}
|
|
335
353
|
);
|
|
336
354
|
|
|
337
355
|
const uniqueRequestIdFieldName = createFieldNameUniqueRequestId('cache');
|
|
338
356
|
const cacheCompleteFieldName = prefix + 'CacheComplete';
|
|
339
|
-
const statusFieldName = prefix + 'Status';
|
|
340
357
|
|
|
341
|
-
|
|
358
|
+
const getTimeCacheComplete = await dynamodbSharedLib.getItem(
|
|
342
359
|
_izContext,
|
|
343
360
|
fullMainTableName,
|
|
344
361
|
keyValues
|
|
345
362
|
);
|
|
346
363
|
_izContext.logger.debug(
|
|
347
|
-
`
|
|
348
|
-
getTimeCacheComplete
|
|
364
|
+
`[Lib:checkAndGetTimeCacheComplete] ${fullMainTableName}`,
|
|
365
|
+
{ getTimeCacheComplete }
|
|
349
366
|
);
|
|
350
367
|
|
|
351
368
|
if (!getTimeCacheComplete) {
|
|
@@ -371,13 +388,13 @@ async function checkAndGetTimeCacheComplete(
|
|
|
371
388
|
getTimeCacheComplete[cacheCompleteFieldName] !== timeCacheComplete
|
|
372
389
|
) {
|
|
373
390
|
return [false, getTimeCacheComplete[cacheCompleteFieldName]];
|
|
374
|
-
} else {
|
|
375
|
-
return [
|
|
376
|
-
true,
|
|
377
|
-
getTimeCacheComplete[cacheCompleteFieldName],
|
|
378
|
-
getTimeCacheComplete[uniqueRequestIdFieldName]
|
|
379
|
-
];
|
|
380
391
|
}
|
|
392
|
+
|
|
393
|
+
return [
|
|
394
|
+
true,
|
|
395
|
+
getTimeCacheComplete[cacheCompleteFieldName],
|
|
396
|
+
getTimeCacheComplete[uniqueRequestIdFieldName]
|
|
397
|
+
];
|
|
381
398
|
}
|
|
382
399
|
|
|
383
400
|
// ----- shared both stored cache and triggered cache, check uniqueRequestId changed ---------
|
|
@@ -403,9 +420,13 @@ async function checkCacheUniqueRequestId(
|
|
|
403
420
|
fullMainTableName,
|
|
404
421
|
keyValues
|
|
405
422
|
);
|
|
406
|
-
_izContext.logger.debug('
|
|
423
|
+
_izContext.logger.debug('[Lib:checkCacheUniqueRequestId] Output', {
|
|
424
|
+
cacheObject
|
|
425
|
+
});
|
|
407
426
|
|
|
408
|
-
return
|
|
427
|
+
return (
|
|
428
|
+
cacheObject[uniqueRequestIdCompleteFieldName] === checkUniqueRequestId
|
|
429
|
+
);
|
|
409
430
|
}
|
|
410
431
|
|
|
411
432
|
//====================================== end Multiple Lambda Invocations (Logic pagination of handling results)
|
|
@@ -427,7 +448,7 @@ function validateStartKeyParam(
|
|
|
427
448
|
partitionKeyFieldName,
|
|
428
449
|
sortKeyFieldName
|
|
429
450
|
) {
|
|
430
|
-
_izContext.logger.debug('[Lib:AsyncFlow:validateStartKeyParam] Input:
|
|
451
|
+
_izContext.logger.debug('[Lib:AsyncFlow:validateStartKeyParam] Input:', {
|
|
431
452
|
startKey,
|
|
432
453
|
partitionKeyFieldName,
|
|
433
454
|
sortKeyFieldName
|
|
@@ -442,14 +463,16 @@ function validateStartKeyParam(
|
|
|
442
463
|
!stringNotEmptyRegex.test(sortKeyFieldName)
|
|
443
464
|
) {
|
|
444
465
|
throw new NoRetryError(
|
|
445
|
-
'validateStartKeyParam:
|
|
466
|
+
'validateStartKeyParam:' +
|
|
467
|
+
' Invalid partitionKeyFieldName or sortKeyFieldName'
|
|
446
468
|
);
|
|
447
469
|
}
|
|
448
470
|
|
|
449
471
|
if (startKey && Object.keys(startKey).length !== 0) {
|
|
450
472
|
if (!startKey[partitionKeyFieldName] || !startKey[sortKeyFieldName]) {
|
|
451
473
|
throw new NoRetryError(
|
|
452
|
-
'validateStartKeyParam: Invalid startKey,
|
|
474
|
+
'validateStartKeyParam: Invalid startKey,' +
|
|
475
|
+
' missing partitionKeyFieldName or sortKeyFieldName'
|
|
453
476
|
);
|
|
454
477
|
}
|
|
455
478
|
return startKey;
|
|
@@ -464,12 +487,12 @@ function validateStartKeyParam(
|
|
|
464
487
|
* and re-enqueues the message to the specified SQS queue.
|
|
465
488
|
* @async
|
|
466
489
|
* @param {IzContext} _izContext
|
|
467
|
-
* @param {Attrs} messageProperty -
|
|
490
|
+
* @param {Attrs} messageProperty - Message body object to re-dispatch
|
|
468
491
|
* @param {Attrs} [passOnStartKey={}] - Optional startKey to pass along
|
|
469
492
|
* @param {number} numberInvocation - Current invocation count
|
|
470
|
-
* @param {string} queueName - Logical queue name
|
|
493
|
+
* @param {string} queueName - Logical queue name
|
|
471
494
|
* @returns {Promise<void>}
|
|
472
|
-
* @throws {NoRetryError} If
|
|
495
|
+
* @throws {NoRetryError} If numberInvocation exceeds safety limit
|
|
473
496
|
*/
|
|
474
497
|
async function validateMultipleInvocations(
|
|
475
498
|
_izContext,
|
|
@@ -479,12 +502,12 @@ async function validateMultipleInvocations(
|
|
|
479
502
|
queueName
|
|
480
503
|
) {
|
|
481
504
|
_izContext.logger.debug(
|
|
482
|
-
'[Lib:AsyncFlow:validateMultipleInvocations] Input:
|
|
505
|
+
'[Lib:AsyncFlow:validateMultipleInvocations] Input:',
|
|
483
506
|
{
|
|
484
|
-
messageProperty
|
|
485
|
-
passOnStartKey
|
|
486
|
-
numberInvocation
|
|
487
|
-
queueName
|
|
507
|
+
messageProperty,
|
|
508
|
+
passOnStartKey,
|
|
509
|
+
numberInvocation,
|
|
510
|
+
queueName
|
|
488
511
|
}
|
|
489
512
|
);
|
|
490
513
|
|
|
@@ -509,11 +532,15 @@ async function validateMultipleInvocations(
|
|
|
509
532
|
QueueUrl: await sqsSharedLib.sqsQueueUrl(_izContext, queueName)
|
|
510
533
|
};
|
|
511
534
|
_izContext.logger.debug(
|
|
512
|
-
`Send message to Dsq:${queueName}
|
|
513
|
-
messageReInvokeFunction
|
|
535
|
+
`[Lib:validateMultipleInvocations] Send message to Dsq:${queueName}`,
|
|
536
|
+
{ messageReInvokeFunction }
|
|
514
537
|
);
|
|
515
538
|
await sqs.sendMessage(_izContext, messageReInvokeFunction);
|
|
516
539
|
} catch (err) {
|
|
540
|
+
_izContext.logger.error(
|
|
541
|
+
'[Lib:validateMultipleInvocations] Error',
|
|
542
|
+
{ err }
|
|
543
|
+
);
|
|
517
544
|
throw err;
|
|
518
545
|
}
|
|
519
546
|
}
|
|
@@ -21,7 +21,8 @@ import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
|
21
21
|
|
|
22
22
|
import {
|
|
23
23
|
createParentFlowId,
|
|
24
|
-
createPendingStepId
|
|
24
|
+
createPendingStepId,
|
|
25
|
+
createAwaitingStepId
|
|
25
26
|
} from './asyncFlowSharedLib.js';
|
|
26
27
|
|
|
27
28
|
function _assertPendingStepId(pendingStepId) {
|
|
@@ -49,13 +50,17 @@ async function _putAwaitingMultipleStepRecords(
|
|
|
49
50
|
records.flatMap(({ awaitingStepId, additionalAttributes }) => {
|
|
50
51
|
const keys = { awaitingStepId, pendingStepId };
|
|
51
52
|
|
|
53
|
+
const itemPending = {
|
|
54
|
+
...keys,
|
|
55
|
+
...sharedAttributes
|
|
56
|
+
};
|
|
57
|
+
if (additionalAttributes) {
|
|
58
|
+
itemPending.additionalAttributes = additionalAttributes;
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
return [
|
|
53
62
|
dynamodbSharedLib.putItem(_izContext, tableSteps, keys),
|
|
54
|
-
dynamodbSharedLib.putItem(_izContext, tablePending,
|
|
55
|
-
...keys,
|
|
56
|
-
...sharedAttributes,
|
|
57
|
-
...(additionalAttributes && { additionalAttributes })
|
|
58
|
-
})
|
|
63
|
+
dynamodbSharedLib.putItem(_izContext, tablePending, itemPending)
|
|
59
64
|
];
|
|
60
65
|
})
|
|
61
66
|
);
|
|
@@ -96,23 +101,24 @@ async function _queryAwaitingStepsByPendingStepId(_izContext, pendingStepId) {
|
|
|
96
101
|
// One pendingStepId can have multiple awaitingStepIds awaiting it.
|
|
97
102
|
|
|
98
103
|
/**
|
|
99
|
-
* Create awaiting multiple step records to block a flow
|
|
104
|
+
* Create awaiting multiple step records to block a flow
|
|
105
|
+
* until multiple steps complete.
|
|
100
106
|
* @async
|
|
101
107
|
* @param {IzContext} _izContext - The Izara context.
|
|
102
|
-
* @param {string} pendingStepId - The ID of
|
|
108
|
+
* @param {string} pendingStepId - The ID of pending step.
|
|
103
109
|
* @param {Array<object>} [records=[]] - Array of step items.
|
|
104
|
-
* @param {string} [records[].awaitingStepId] -
|
|
105
|
-
* @param {object} [records[].message] -
|
|
106
|
-
* @param {object} [records[].flowType] -
|
|
107
|
-
* @param {string} records[].flowType.flowTag - Tag identifying
|
|
108
|
-
* @param {string} records[].flowType.serviceTag - Tag identifying
|
|
110
|
+
* @param {string} [records[].awaitingStepId] - Awaiting step ID.
|
|
111
|
+
* @param {object} [records[].message] - Message payload to publish.
|
|
112
|
+
* @param {object} [records[].flowType] - FlowType override.
|
|
113
|
+
* @param {string} records[].flowType.flowTag - Tag identifying flow.
|
|
114
|
+
* @param {string} records[].flowType.serviceTag - Tag identifying service.
|
|
109
115
|
* @param {object} [options={}] - Optional configurations.
|
|
110
|
-
* @param {string} [options.prefix=''] - Prefix
|
|
111
|
-
* @param {object} [options.additionalAttributes={}] - Default
|
|
112
|
-
* @param {object|null} [options.flowType=null] - Default flow schema
|
|
113
|
-
* @param {string} [options.flowType.flowTag] -
|
|
114
|
-
* @param {string} [options.flowType.serviceTag] -
|
|
115
|
-
* @returns {Promise<string|
|
|
116
|
+
* @param {string} [options.prefix=''] - Prefix for pendingStepId.
|
|
117
|
+
* @param {object} [options.additionalAttributes={}] - Default attributes.
|
|
118
|
+
* @param {object|null} [options.flowType=null] - Default flow schema.
|
|
119
|
+
* @param {string} [options.flowType.flowTag] - Default flow tag.
|
|
120
|
+
* @param {string} [options.flowType.serviceTag] - Default service tag.
|
|
121
|
+
* @returns {Promise<string|null>} The awaitingStepId of first record.
|
|
116
122
|
*/
|
|
117
123
|
export async function createAwaitingMultipleSteps(
|
|
118
124
|
_izContext,
|
|
@@ -157,7 +163,9 @@ export async function createAwaitingMultipleSteps(
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
const awaitingStepId =
|
|
160
|
-
record.awaitingStepId
|
|
166
|
+
createAwaitingStepId(record.awaitingStepId, prefix) ||
|
|
167
|
+
createParentFlowId(prefix);
|
|
168
|
+
|
|
161
169
|
return {
|
|
162
170
|
awaitingStepId,
|
|
163
171
|
additionalAttributes,
|
|
@@ -174,21 +182,27 @@ export async function createAwaitingMultipleSteps(
|
|
|
174
182
|
});
|
|
175
183
|
}
|
|
176
184
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
finalRecords.map(({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
185
|
+
const mappedStepRecords = finalRecords.map(
|
|
186
|
+
({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
180
187
|
const hasItemAttrs = itemAttrs && Object.keys(itemAttrs).length > 0;
|
|
181
188
|
const hasOptAttrs =
|
|
182
189
|
additionalAttributes && Object.keys(additionalAttributes).length > 0;
|
|
183
190
|
|
|
191
|
+
let mergedAttributes = null;
|
|
192
|
+
if (hasItemAttrs || hasOptAttrs) {
|
|
193
|
+
mergedAttributes = { ...additionalAttributes, ...itemAttrs };
|
|
194
|
+
}
|
|
195
|
+
|
|
184
196
|
return {
|
|
185
197
|
awaitingStepId,
|
|
186
|
-
additionalAttributes:
|
|
187
|
-
hasItemAttrs || hasOptAttrs
|
|
188
|
-
? { ...additionalAttributes, ...itemAttrs }
|
|
189
|
-
: undefined
|
|
198
|
+
additionalAttributes: mergedAttributes
|
|
190
199
|
};
|
|
191
|
-
}
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
await _putAwaitingMultipleStepRecords(
|
|
204
|
+
_izContext,
|
|
205
|
+
mappedStepRecords,
|
|
192
206
|
finalPendingStepId,
|
|
193
207
|
{
|
|
194
208
|
complete: false,
|
|
@@ -198,16 +212,19 @@ export async function createAwaitingMultipleSteps(
|
|
|
198
212
|
|
|
199
213
|
if (hasFlowPublish) {
|
|
200
214
|
await Promise.all(
|
|
201
|
-
finalRecords.map(({ message, topicArn }) =>
|
|
202
|
-
sns.publishAsync(_izContext, {
|
|
215
|
+
finalRecords.map(async ({ message, topicArn }) => {
|
|
216
|
+
await sns.publishAsync(_izContext, {
|
|
203
217
|
TopicArn: `${topicArn}_In`,
|
|
204
218
|
Message: JSON.stringify(message)
|
|
205
|
-
})
|
|
206
|
-
)
|
|
219
|
+
});
|
|
220
|
+
})
|
|
207
221
|
);
|
|
208
222
|
}
|
|
209
223
|
|
|
210
|
-
|
|
224
|
+
if (finalRecords[0] && finalRecords[0].awaitingStepId) {
|
|
225
|
+
return finalRecords[0].awaitingStepId;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
211
228
|
}
|
|
212
229
|
|
|
213
230
|
/**
|
|
@@ -246,11 +263,11 @@ export async function updateAwaitingMultipleStep(
|
|
|
246
263
|
}
|
|
247
264
|
|
|
248
265
|
/**
|
|
249
|
-
* Query and return the first pending step item that matches
|
|
266
|
+
* Query and return the first pending step item that matches awaitingStepId.
|
|
250
267
|
* @async
|
|
251
268
|
* @param {IzContext} _izContext - The Izara context.
|
|
252
269
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
253
|
-
* @returns {Promise<object|
|
|
270
|
+
* @returns {Promise<object|null>} The found item or null.
|
|
254
271
|
*/
|
|
255
272
|
export async function findPendingStepAwaitingMultipleSteps(
|
|
256
273
|
_izContext,
|
|
@@ -265,11 +282,14 @@ export async function findPendingStepAwaitingMultipleSteps(
|
|
|
265
282
|
awaitingStepId
|
|
266
283
|
);
|
|
267
284
|
|
|
268
|
-
|
|
285
|
+
if (items && items.length > 0) {
|
|
286
|
+
return items[0];
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
269
289
|
}
|
|
270
290
|
|
|
271
291
|
/**
|
|
272
|
-
* Query and return all pending step items matching
|
|
292
|
+
* Query and return all pending step items matching awaitingStepId.
|
|
273
293
|
* @async
|
|
274
294
|
* @param {IzContext} _izContext - The Izara context.
|
|
275
295
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
@@ -283,11 +303,11 @@ export async function findPendingStepsAwaitingMultipleSteps(
|
|
|
283
303
|
}
|
|
284
304
|
|
|
285
305
|
/**
|
|
286
|
-
* Retrieve
|
|
306
|
+
* Retrieve pendingStepId of first step matching awaitingStepId.
|
|
287
307
|
* @async
|
|
288
308
|
* @param {IzContext} _izContext - The Izara context.
|
|
289
309
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
290
|
-
* @returns {Promise<string|
|
|
310
|
+
* @returns {Promise<string|null>} pendingStepId of first item found or null.
|
|
291
311
|
*/
|
|
292
312
|
export async function findPendingStepIdAwaitingMultipleSteps(
|
|
293
313
|
_izContext,
|
|
@@ -297,11 +317,14 @@ export async function findPendingStepIdAwaitingMultipleSteps(
|
|
|
297
317
|
_izContext,
|
|
298
318
|
awaitingStepId
|
|
299
319
|
);
|
|
300
|
-
|
|
320
|
+
if (items && items[0] && items[0].pendingStepId) {
|
|
321
|
+
return items[0].pendingStepId;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
301
324
|
}
|
|
302
325
|
|
|
303
326
|
/**
|
|
304
|
-
* Query and return all awaiting steps matching
|
|
327
|
+
* Query and return all awaiting steps matching pendingStepId.
|
|
305
328
|
* @async
|
|
306
329
|
* @param {IzContext} _izContext - The Izara context.
|
|
307
330
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -315,22 +338,21 @@ export async function findAwaitingMultipleStepByPending(
|
|
|
315
338
|
}
|
|
316
339
|
|
|
317
340
|
/**
|
|
318
|
-
* Check if all awaiting steps for a pendingStepId are completed.
|
|
319
|
-
* is provided, it updates its status to complete and returns values/errors.
|
|
341
|
+
* Check if all awaiting steps for a pendingStepId are completed.
|
|
320
342
|
* @async
|
|
321
343
|
* @param {IzContext} _izContext - The Izara context.
|
|
322
344
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
323
|
-
* @param {string|null} [currentAwaitingStepId=null] -
|
|
324
|
-
* @param {Array<any>} [errorsFound=[]] - Errors collected from
|
|
325
|
-
* @param {object} [returnValues={}] - Values returned from
|
|
326
|
-
* @param {object} [settings={ checkIsAllError: false }] -
|
|
327
|
-
* @param {boolean} [settings.checkIsAllError=false] -
|
|
328
|
-
* @returns {Promise<object>} Status object containing
|
|
345
|
+
* @param {string|null} [currentAwaitingStepId=null] - Completed step ID.
|
|
346
|
+
* @param {Array<any>} [errorsFound=[]] - Errors collected from current step.
|
|
347
|
+
* @param {object} [returnValues={}] - Values returned from current step.
|
|
348
|
+
* @param {object} [settings={ checkIsAllError: false }] - Settings.
|
|
349
|
+
* @param {boolean} [settings.checkIsAllError=false] - Verify all failed.
|
|
350
|
+
* @returns {Promise<object>} Status object containing completion details.
|
|
329
351
|
*/
|
|
330
352
|
export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
331
353
|
_izContext,
|
|
332
354
|
pendingStepId,
|
|
333
|
-
currentAwaitingStepId
|
|
355
|
+
currentAwaitingStepId,
|
|
334
356
|
errorsFound = [],
|
|
335
357
|
returnValues = {},
|
|
336
358
|
settings = { checkIsAllError: false }
|
|
@@ -370,24 +392,29 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
370
392
|
|
|
371
393
|
_izContext.logger.debug(
|
|
372
394
|
'[Lib:checkAllAwaitingStepsFinishedWithReturnParams] awaitingStepItems',
|
|
373
|
-
awaitingStepItems
|
|
395
|
+
{ awaitingStepItems }
|
|
374
396
|
);
|
|
375
397
|
|
|
376
398
|
const hasIncomplete = awaitingStepItems.some(
|
|
377
399
|
item => item.awaitingStepId !== currentAwaitingStepId && !item.complete
|
|
378
400
|
);
|
|
379
401
|
|
|
402
|
+
let defaultAttrs = null;
|
|
403
|
+
if (awaitingStepItems[0] && awaitingStepItems[0].additionalAttributes) {
|
|
404
|
+
defaultAttrs = awaitingStepItems[0].additionalAttributes;
|
|
405
|
+
}
|
|
406
|
+
|
|
380
407
|
if (hasIncomplete) {
|
|
381
408
|
return {
|
|
382
409
|
isComplete: false,
|
|
383
|
-
|
|
410
|
+
collectedReturnValueByAwaitingStepId: null,
|
|
384
411
|
collectedErrors: [],
|
|
385
412
|
returnValues: null,
|
|
386
|
-
additionalAttributes:
|
|
413
|
+
additionalAttributes: defaultAttrs
|
|
387
414
|
};
|
|
388
415
|
}
|
|
389
416
|
|
|
390
|
-
const
|
|
417
|
+
const collectedReturnValueByAwaitingStepId = {};
|
|
391
418
|
let sharedReturnValues = null;
|
|
392
419
|
|
|
393
420
|
for (const item of awaitingStepItems) {
|
|
@@ -395,30 +422,42 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
395
422
|
continue;
|
|
396
423
|
}
|
|
397
424
|
|
|
398
|
-
|
|
399
|
-
|
|
425
|
+
collectedReturnValueByAwaitingStepId[item.awaitingStepId] =
|
|
426
|
+
item.returnValues;
|
|
427
|
+
if (sharedReturnValues === null) {
|
|
428
|
+
sharedReturnValues = item.returnValues;
|
|
429
|
+
}
|
|
400
430
|
}
|
|
401
431
|
|
|
402
|
-
const collectedErrors =
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
432
|
+
const collectedErrors = [];
|
|
433
|
+
for (const item of awaitingStepItems) {
|
|
434
|
+
if (item.errorsFound && item.errorsFound.length > 0) {
|
|
435
|
+
collectedErrors.push(
|
|
436
|
+
`Error found in step ${item.awaitingStepId}`,
|
|
437
|
+
...item.errorsFound
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
407
441
|
|
|
408
|
-
|
|
442
|
+
const result = {
|
|
409
443
|
isComplete: true,
|
|
410
|
-
|
|
444
|
+
collectedReturnValueByAwaitingStepId,
|
|
411
445
|
collectedErrors,
|
|
412
446
|
returnValues: sharedReturnValues,
|
|
413
|
-
additionalAttributes:
|
|
414
|
-
...(settings.checkIsAllError && {
|
|
415
|
-
isAllError: awaitingStepItems.every(item => item.errorsFound?.length > 0)
|
|
416
|
-
})
|
|
447
|
+
additionalAttributes: defaultAttrs
|
|
417
448
|
};
|
|
449
|
+
|
|
450
|
+
if (settings.checkIsAllError) {
|
|
451
|
+
result.isAllError = awaitingStepItems.every(
|
|
452
|
+
item => item.errorsFound && item.errorsFound.length > 0
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return result;
|
|
418
457
|
}
|
|
419
458
|
|
|
420
459
|
/**
|
|
421
|
-
* Clean up/delete all awaiting multiple steps records
|
|
460
|
+
* Clean up/delete all awaiting multiple steps records for pendingStepId.
|
|
422
461
|
* @async
|
|
423
462
|
* @param {IzContext} _izContext - The Izara context.
|
|
424
463
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -429,7 +468,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
429
468
|
pendingStepId
|
|
430
469
|
});
|
|
431
470
|
|
|
432
|
-
const
|
|
471
|
+
const awaitingStepItems = await _queryAwaitingStepsByPendingStepId(
|
|
433
472
|
_izContext,
|
|
434
473
|
pendingStepId
|
|
435
474
|
);
|
|
@@ -443,7 +482,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
443
482
|
);
|
|
444
483
|
|
|
445
484
|
await Promise.all(
|
|
446
|
-
|
|
485
|
+
awaitingStepItems.flatMap(
|
|
447
486
|
({ awaitingStepId, pendingStepId: itemPendingStepId }) => [
|
|
448
487
|
dynamodbSharedLib.deleteItem(_izContext, tableSteps, {
|
|
449
488
|
awaitingStepId,
|
|
@@ -461,12 +500,12 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
461
500
|
}
|
|
462
501
|
|
|
463
502
|
/**
|
|
464
|
-
* Delete a specific awaiting step from Steps table (and
|
|
503
|
+
* Delete a specific awaiting step from Steps table (and byPending table).
|
|
465
504
|
* @async
|
|
466
505
|
* @param {IzContext} _izContext - The Izara context.
|
|
467
506
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
468
507
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
469
|
-
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
508
|
+
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
470
509
|
* @returns {Promise<void>}
|
|
471
510
|
*/
|
|
472
511
|
export async function removeAwaitingMultipleStep(
|
|
@@ -483,7 +522,8 @@ export async function removeAwaitingMultipleStep(
|
|
|
483
522
|
|
|
484
523
|
if (!awaitingStepId || !pendingStepId) {
|
|
485
524
|
throw new NoRetryError(
|
|
486
|
-
'[Lib:removeAwaitingMultipleStep]
|
|
525
|
+
'[Lib:removeAwaitingMultipleStep]' +
|
|
526
|
+
' awaitingStepId or pendingStepId required'
|
|
487
527
|
);
|
|
488
528
|
}
|
|
489
529
|
|
package/src/awaitingStep.js
CHANGED
|
@@ -44,16 +44,18 @@ async function _findSinglePendingStep(_izContext, awaitingStepId) {
|
|
|
44
44
|
//======================================
|
|
45
45
|
// AwaitingStep
|
|
46
46
|
//======================================
|
|
47
|
-
// AwaitingStep flow awaiting one external flow, will continue its flow once
|
|
47
|
+
// AwaitingStep flow awaiting one external flow, will continue its flow once
|
|
48
|
+
// one external flow completes.
|
|
48
49
|
// One awaitingStepId can have multiple pendingStepIds awaiting it.
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
|
-
* Create a single awaiting step record to block a flow until a specific
|
|
52
|
+
* Create a single awaiting step record to block a flow until a specific
|
|
53
|
+
* external flow step completes.
|
|
52
54
|
* @async
|
|
53
55
|
* @param {IzContext} _izContext - The Izara context.
|
|
54
56
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
55
|
-
* @param {string} pendingStepId - The ID of
|
|
56
|
-
* @param {object} [additionalAttributes={}] -
|
|
57
|
+
* @param {string} pendingStepId - The ID of pending step that is waiting.
|
|
58
|
+
* @param {object} [additionalAttributes={}] - Attributes to save.
|
|
57
59
|
* @param {object} [callingFlowConfig={}] - Calling flow configuration.
|
|
58
60
|
* @returns {Promise<void>}
|
|
59
61
|
*/
|
|
@@ -91,15 +93,15 @@ export async function createAwaitingStep(
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
/**
|
|
94
|
-
* Query and return
|
|
96
|
+
* Query and return single pending step item matching given awaitingStepId.
|
|
95
97
|
* @async
|
|
96
98
|
* @param {IzContext} _izContext - The Izara context.
|
|
97
99
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
98
100
|
* @returns {Promise<object>} The found pending step record.
|
|
99
|
-
* @throws {NoRetryError} If
|
|
101
|
+
* @throws {NoRetryError} If missing or if not exactly one record found.
|
|
100
102
|
*/
|
|
101
103
|
export async function findPendingStep(_izContext, awaitingStepId) {
|
|
102
|
-
|
|
104
|
+
return _findSinglePendingStep(_izContext, awaitingStepId);
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
/**
|
|
@@ -122,7 +124,7 @@ export async function removeAwaitingStep(
|
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
if (!awaitingStepId || !pendingStepId) {
|
|
125
|
-
throw new NoRetryError('awaitingStepId pendingStepId required');
|
|
127
|
+
throw new NoRetryError('awaitingStepId, pendingStepId required');
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
await dynamodbSharedLib.deleteItem(
|
|
@@ -133,7 +135,7 @@ export async function removeAwaitingStep(
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
/**
|
|
136
|
-
* Delete
|
|
138
|
+
* Delete awaiting step record from AwaitingStep table by uniqueRequestId.
|
|
137
139
|
* @async
|
|
138
140
|
* @param {IzContext} _izContext - The Izara context.
|
|
139
141
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
@@ -157,7 +159,7 @@ export async function removeAwaitingStepWithCheckUniqueRequestId(
|
|
|
157
159
|
|
|
158
160
|
if (!awaitingStepId || !pendingStepId || !checkUniqueRequestId) {
|
|
159
161
|
throw new NoRetryError(
|
|
160
|
-
'awaitingStepId, pendingStepId checkUniqueRequestId required'
|
|
162
|
+
'awaitingStepId, pendingStepId, checkUniqueRequestId required'
|
|
161
163
|
);
|
|
162
164
|
}
|
|
163
165
|
|