@izara_project/izara-core-library-asynchronous-flow 1.0.42 → 1.0.43
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 +1 -1
- package/src/asyncFlowSharedLib.js +101 -74
- package/src/awaitingMultipleSteps.js +107 -70
- 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.43",
|
|
7
7
|
"description": "Shared asynchronous flow logic",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "index.js",
|
|
@@ -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
135
|
const identifierId = pendingStepId.slice(prefix.length);
|
|
132
|
-
if (identifierId === prefix)
|
|
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
|
}
|
|
@@ -49,13 +49,17 @@ async function _putAwaitingMultipleStepRecords(
|
|
|
49
49
|
records.flatMap(({ awaitingStepId, additionalAttributes }) => {
|
|
50
50
|
const keys = { awaitingStepId, pendingStepId };
|
|
51
51
|
|
|
52
|
+
const itemPending = {
|
|
53
|
+
...keys,
|
|
54
|
+
...sharedAttributes
|
|
55
|
+
};
|
|
56
|
+
if (additionalAttributes) {
|
|
57
|
+
itemPending.additionalAttributes = additionalAttributes;
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
return [
|
|
53
61
|
dynamodbSharedLib.putItem(_izContext, tableSteps, keys),
|
|
54
|
-
dynamodbSharedLib.putItem(_izContext, tablePending,
|
|
55
|
-
...keys,
|
|
56
|
-
...sharedAttributes,
|
|
57
|
-
...(additionalAttributes && { additionalAttributes })
|
|
58
|
-
})
|
|
62
|
+
dynamodbSharedLib.putItem(_izContext, tablePending, itemPending)
|
|
59
63
|
];
|
|
60
64
|
})
|
|
61
65
|
);
|
|
@@ -96,23 +100,24 @@ async function _queryAwaitingStepsByPendingStepId(_izContext, pendingStepId) {
|
|
|
96
100
|
// One pendingStepId can have multiple awaitingStepIds awaiting it.
|
|
97
101
|
|
|
98
102
|
/**
|
|
99
|
-
* Create awaiting multiple step records to block a flow
|
|
103
|
+
* Create awaiting multiple step records to block a flow
|
|
104
|
+
* until multiple steps complete.
|
|
100
105
|
* @async
|
|
101
106
|
* @param {IzContext} _izContext - The Izara context.
|
|
102
|
-
* @param {string} pendingStepId - The ID of
|
|
107
|
+
* @param {string} pendingStepId - The ID of pending step.
|
|
103
108
|
* @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
|
|
109
|
+
* @param {string} [records[].awaitingStepId] - Awaiting step ID.
|
|
110
|
+
* @param {object} [records[].message] - Message payload to publish.
|
|
111
|
+
* @param {object} [records[].flowType] - FlowType override.
|
|
112
|
+
* @param {string} records[].flowType.flowTag - Tag identifying flow.
|
|
113
|
+
* @param {string} records[].flowType.serviceTag - Tag identifying service.
|
|
109
114
|
* @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|
|
|
115
|
+
* @param {string} [options.prefix=''] - Prefix for pendingStepId.
|
|
116
|
+
* @param {object} [options.additionalAttributes={}] - Default attributes.
|
|
117
|
+
* @param {object|null} [options.flowType=null] - Default flow schema.
|
|
118
|
+
* @param {string} [options.flowType.flowTag] - Default flow tag.
|
|
119
|
+
* @param {string} [options.flowType.serviceTag] - Default service tag.
|
|
120
|
+
* @returns {Promise<string|null>} The awaitingStepId of first record.
|
|
116
121
|
*/
|
|
117
122
|
export async function createAwaitingMultipleSteps(
|
|
118
123
|
_izContext,
|
|
@@ -174,21 +179,27 @@ export async function createAwaitingMultipleSteps(
|
|
|
174
179
|
});
|
|
175
180
|
}
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
finalRecords.map(({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
182
|
+
const mappedStepRecords = finalRecords.map(
|
|
183
|
+
({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
180
184
|
const hasItemAttrs = itemAttrs && Object.keys(itemAttrs).length > 0;
|
|
181
185
|
const hasOptAttrs =
|
|
182
186
|
additionalAttributes && Object.keys(additionalAttributes).length > 0;
|
|
183
187
|
|
|
188
|
+
let mergedAttributes = null;
|
|
189
|
+
if (hasItemAttrs || hasOptAttrs) {
|
|
190
|
+
mergedAttributes = { ...additionalAttributes, ...itemAttrs };
|
|
191
|
+
}
|
|
192
|
+
|
|
184
193
|
return {
|
|
185
194
|
awaitingStepId,
|
|
186
|
-
additionalAttributes:
|
|
187
|
-
hasItemAttrs || hasOptAttrs
|
|
188
|
-
? { ...additionalAttributes, ...itemAttrs }
|
|
189
|
-
: undefined
|
|
195
|
+
additionalAttributes: mergedAttributes
|
|
190
196
|
};
|
|
191
|
-
}
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
await _putAwaitingMultipleStepRecords(
|
|
201
|
+
_izContext,
|
|
202
|
+
mappedStepRecords,
|
|
192
203
|
finalPendingStepId,
|
|
193
204
|
{
|
|
194
205
|
complete: false,
|
|
@@ -198,16 +209,19 @@ export async function createAwaitingMultipleSteps(
|
|
|
198
209
|
|
|
199
210
|
if (hasFlowPublish) {
|
|
200
211
|
await Promise.all(
|
|
201
|
-
finalRecords.map(({ message, topicArn }) =>
|
|
202
|
-
sns.publishAsync(_izContext, {
|
|
212
|
+
finalRecords.map(async ({ message, topicArn }) => {
|
|
213
|
+
await sns.publishAsync(_izContext, {
|
|
203
214
|
TopicArn: `${topicArn}_In`,
|
|
204
215
|
Message: JSON.stringify(message)
|
|
205
|
-
})
|
|
206
|
-
)
|
|
216
|
+
});
|
|
217
|
+
})
|
|
207
218
|
);
|
|
208
219
|
}
|
|
209
220
|
|
|
210
|
-
|
|
221
|
+
if (finalRecords[0] && finalRecords[0].awaitingStepId) {
|
|
222
|
+
return finalRecords[0].awaitingStepId;
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
211
225
|
}
|
|
212
226
|
|
|
213
227
|
/**
|
|
@@ -246,11 +260,11 @@ export async function updateAwaitingMultipleStep(
|
|
|
246
260
|
}
|
|
247
261
|
|
|
248
262
|
/**
|
|
249
|
-
* Query and return the first pending step item that matches
|
|
263
|
+
* Query and return the first pending step item that matches awaitingStepId.
|
|
250
264
|
* @async
|
|
251
265
|
* @param {IzContext} _izContext - The Izara context.
|
|
252
266
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
253
|
-
* @returns {Promise<object|
|
|
267
|
+
* @returns {Promise<object|null>} The found item or null.
|
|
254
268
|
*/
|
|
255
269
|
export async function findPendingStepAwaitingMultipleSteps(
|
|
256
270
|
_izContext,
|
|
@@ -265,11 +279,14 @@ export async function findPendingStepAwaitingMultipleSteps(
|
|
|
265
279
|
awaitingStepId
|
|
266
280
|
);
|
|
267
281
|
|
|
268
|
-
|
|
282
|
+
if (items && items.length > 0) {
|
|
283
|
+
return items[0];
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
269
286
|
}
|
|
270
287
|
|
|
271
288
|
/**
|
|
272
|
-
* Query and return all pending step items matching
|
|
289
|
+
* Query and return all pending step items matching awaitingStepId.
|
|
273
290
|
* @async
|
|
274
291
|
* @param {IzContext} _izContext - The Izara context.
|
|
275
292
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
@@ -283,11 +300,11 @@ export async function findPendingStepsAwaitingMultipleSteps(
|
|
|
283
300
|
}
|
|
284
301
|
|
|
285
302
|
/**
|
|
286
|
-
* Retrieve
|
|
303
|
+
* Retrieve pendingStepId of first step matching awaitingStepId.
|
|
287
304
|
* @async
|
|
288
305
|
* @param {IzContext} _izContext - The Izara context.
|
|
289
306
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
290
|
-
* @returns {Promise<string|
|
|
307
|
+
* @returns {Promise<string|null>} pendingStepId of first item found or null.
|
|
291
308
|
*/
|
|
292
309
|
export async function findPendingStepIdAwaitingMultipleSteps(
|
|
293
310
|
_izContext,
|
|
@@ -297,11 +314,14 @@ export async function findPendingStepIdAwaitingMultipleSteps(
|
|
|
297
314
|
_izContext,
|
|
298
315
|
awaitingStepId
|
|
299
316
|
);
|
|
300
|
-
|
|
317
|
+
if (items && items[0] && items[0].pendingStepId) {
|
|
318
|
+
return items[0].pendingStepId;
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
301
321
|
}
|
|
302
322
|
|
|
303
323
|
/**
|
|
304
|
-
* Query and return all awaiting steps matching
|
|
324
|
+
* Query and return all awaiting steps matching pendingStepId.
|
|
305
325
|
* @async
|
|
306
326
|
* @param {IzContext} _izContext - The Izara context.
|
|
307
327
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -315,17 +335,16 @@ export async function findAwaitingMultipleStepByPending(
|
|
|
315
335
|
}
|
|
316
336
|
|
|
317
337
|
/**
|
|
318
|
-
* Check if all awaiting steps for a pendingStepId are completed.
|
|
319
|
-
* is provided, it updates its status to complete and returns values/errors.
|
|
338
|
+
* Check if all awaiting steps for a pendingStepId are completed.
|
|
320
339
|
* @async
|
|
321
340
|
* @param {IzContext} _izContext - The Izara context.
|
|
322
341
|
* @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
|
|
342
|
+
* @param {string|null} [currentAwaitingStepId=null] - Completed step ID.
|
|
343
|
+
* @param {Array<any>} [errorsFound=[]] - Errors collected from current step.
|
|
344
|
+
* @param {object} [returnValues={}] - Values returned from current step.
|
|
345
|
+
* @param {object} [settings={ checkIsAllError: false }] - Settings.
|
|
346
|
+
* @param {boolean} [settings.checkIsAllError=false] - Verify all failed.
|
|
347
|
+
* @returns {Promise<object>} Status object containing completion details.
|
|
329
348
|
*/
|
|
330
349
|
export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
331
350
|
_izContext,
|
|
@@ -370,24 +389,29 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
370
389
|
|
|
371
390
|
_izContext.logger.debug(
|
|
372
391
|
'[Lib:checkAllAwaitingStepsFinishedWithReturnParams] awaitingStepItems',
|
|
373
|
-
awaitingStepItems
|
|
392
|
+
{ awaitingStepItems }
|
|
374
393
|
);
|
|
375
394
|
|
|
376
395
|
const hasIncomplete = awaitingStepItems.some(
|
|
377
396
|
item => item.awaitingStepId !== currentAwaitingStepId && !item.complete
|
|
378
397
|
);
|
|
379
398
|
|
|
399
|
+
let defaultAttrs = null;
|
|
400
|
+
if (awaitingStepItems[0] && awaitingStepItems[0].additionalAttributes) {
|
|
401
|
+
defaultAttrs = awaitingStepItems[0].additionalAttributes;
|
|
402
|
+
}
|
|
403
|
+
|
|
380
404
|
if (hasIncomplete) {
|
|
381
405
|
return {
|
|
382
406
|
isComplete: false,
|
|
383
|
-
|
|
407
|
+
collectedReturnValueByAwaitingStepId: null,
|
|
384
408
|
collectedErrors: [],
|
|
385
409
|
returnValues: null,
|
|
386
|
-
additionalAttributes:
|
|
410
|
+
additionalAttributes: defaultAttrs
|
|
387
411
|
};
|
|
388
412
|
}
|
|
389
413
|
|
|
390
|
-
const
|
|
414
|
+
const collectedReturnValueByAwaitingStepId = {};
|
|
391
415
|
let sharedReturnValues = null;
|
|
392
416
|
|
|
393
417
|
for (const item of awaitingStepItems) {
|
|
@@ -395,30 +419,42 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
395
419
|
continue;
|
|
396
420
|
}
|
|
397
421
|
|
|
398
|
-
|
|
399
|
-
|
|
422
|
+
collectedReturnValueByAwaitingStepId[item.awaitingStepId] =
|
|
423
|
+
item.returnValues;
|
|
424
|
+
if (sharedReturnValues === null) {
|
|
425
|
+
sharedReturnValues = item.returnValues;
|
|
426
|
+
}
|
|
400
427
|
}
|
|
401
428
|
|
|
402
|
-
const collectedErrors =
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
429
|
+
const collectedErrors = [];
|
|
430
|
+
for (const item of awaitingStepItems) {
|
|
431
|
+
if (item.errorsFound && item.errorsFound.length > 0) {
|
|
432
|
+
collectedErrors.push(
|
|
433
|
+
`Error found in step ${item.awaitingStepId}`,
|
|
434
|
+
...item.errorsFound
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
407
438
|
|
|
408
|
-
|
|
439
|
+
const result = {
|
|
409
440
|
isComplete: true,
|
|
410
|
-
|
|
441
|
+
collectedReturnValueByAwaitingStepId,
|
|
411
442
|
collectedErrors,
|
|
412
443
|
returnValues: sharedReturnValues,
|
|
413
|
-
additionalAttributes:
|
|
414
|
-
...(settings.checkIsAllError && {
|
|
415
|
-
isAllError: awaitingStepItems.every(item => item.errorsFound?.length > 0)
|
|
416
|
-
})
|
|
444
|
+
additionalAttributes: defaultAttrs
|
|
417
445
|
};
|
|
446
|
+
|
|
447
|
+
if (settings.checkIsAllError) {
|
|
448
|
+
result.isAllError = awaitingStepItems.every(
|
|
449
|
+
item => item.errorsFound && item.errorsFound.length > 0
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return result;
|
|
418
454
|
}
|
|
419
455
|
|
|
420
456
|
/**
|
|
421
|
-
* Clean up/delete all awaiting multiple steps records
|
|
457
|
+
* Clean up/delete all awaiting multiple steps records for pendingStepId.
|
|
422
458
|
* @async
|
|
423
459
|
* @param {IzContext} _izContext - The Izara context.
|
|
424
460
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -429,7 +465,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
429
465
|
pendingStepId
|
|
430
466
|
});
|
|
431
467
|
|
|
432
|
-
const
|
|
468
|
+
const awaitingStepItems = await _queryAwaitingStepsByPendingStepId(
|
|
433
469
|
_izContext,
|
|
434
470
|
pendingStepId
|
|
435
471
|
);
|
|
@@ -443,7 +479,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
443
479
|
);
|
|
444
480
|
|
|
445
481
|
await Promise.all(
|
|
446
|
-
|
|
482
|
+
awaitingStepItems.flatMap(
|
|
447
483
|
({ awaitingStepId, pendingStepId: itemPendingStepId }) => [
|
|
448
484
|
dynamodbSharedLib.deleteItem(_izContext, tableSteps, {
|
|
449
485
|
awaitingStepId,
|
|
@@ -461,12 +497,12 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
461
497
|
}
|
|
462
498
|
|
|
463
499
|
/**
|
|
464
|
-
* Delete a specific awaiting step from Steps table (and
|
|
500
|
+
* Delete a specific awaiting step from Steps table (and byPending table).
|
|
465
501
|
* @async
|
|
466
502
|
* @param {IzContext} _izContext - The Izara context.
|
|
467
503
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
468
504
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
469
|
-
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
505
|
+
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
470
506
|
* @returns {Promise<void>}
|
|
471
507
|
*/
|
|
472
508
|
export async function removeAwaitingMultipleStep(
|
|
@@ -483,7 +519,8 @@ export async function removeAwaitingMultipleStep(
|
|
|
483
519
|
|
|
484
520
|
if (!awaitingStepId || !pendingStepId) {
|
|
485
521
|
throw new NoRetryError(
|
|
486
|
-
'[Lib:removeAwaitingMultipleStep]
|
|
522
|
+
'[Lib:removeAwaitingMultipleStep]' +
|
|
523
|
+
' awaitingStepId or pendingStepId required'
|
|
487
524
|
);
|
|
488
525
|
}
|
|
489
526
|
|
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
|
|