@izara_project/izara-core-library-asynchronous-flow 1.0.41 → 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 +116 -72
- 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,
|
|
@@ -146,8 +151,14 @@ export async function createAwaitingMultipleSteps(
|
|
|
146
151
|
finalRecords = records.map(record => {
|
|
147
152
|
const recordFlowType = record.flowType || flowType;
|
|
148
153
|
|
|
149
|
-
if (
|
|
150
|
-
|
|
154
|
+
if (
|
|
155
|
+
!recordFlowType ||
|
|
156
|
+
!recordFlowType.flowTag ||
|
|
157
|
+
!recordFlowType.serviceTag
|
|
158
|
+
) {
|
|
159
|
+
throw new NoRetryError(
|
|
160
|
+
'record.flowType must have both flowTag and serviceTag'
|
|
161
|
+
);
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
const awaitingStepId =
|
|
@@ -168,21 +179,27 @@ export async function createAwaitingMultipleSteps(
|
|
|
168
179
|
});
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
finalRecords.map(({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
182
|
+
const mappedStepRecords = finalRecords.map(
|
|
183
|
+
({ awaitingStepId, additionalAttributes: itemAttrs }) => {
|
|
174
184
|
const hasItemAttrs = itemAttrs && Object.keys(itemAttrs).length > 0;
|
|
175
185
|
const hasOptAttrs =
|
|
176
186
|
additionalAttributes && Object.keys(additionalAttributes).length > 0;
|
|
177
187
|
|
|
188
|
+
let mergedAttributes = null;
|
|
189
|
+
if (hasItemAttrs || hasOptAttrs) {
|
|
190
|
+
mergedAttributes = { ...additionalAttributes, ...itemAttrs };
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
return {
|
|
179
194
|
awaitingStepId,
|
|
180
|
-
additionalAttributes:
|
|
181
|
-
hasItemAttrs || hasOptAttrs
|
|
182
|
-
? { ...additionalAttributes, ...itemAttrs }
|
|
183
|
-
: undefined
|
|
195
|
+
additionalAttributes: mergedAttributes
|
|
184
196
|
};
|
|
185
|
-
}
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
await _putAwaitingMultipleStepRecords(
|
|
201
|
+
_izContext,
|
|
202
|
+
mappedStepRecords,
|
|
186
203
|
finalPendingStepId,
|
|
187
204
|
{
|
|
188
205
|
complete: false,
|
|
@@ -192,16 +209,19 @@ export async function createAwaitingMultipleSteps(
|
|
|
192
209
|
|
|
193
210
|
if (hasFlowPublish) {
|
|
194
211
|
await Promise.all(
|
|
195
|
-
finalRecords.map(({ message, topicArn }) =>
|
|
196
|
-
sns.publishAsync(_izContext, {
|
|
212
|
+
finalRecords.map(async ({ message, topicArn }) => {
|
|
213
|
+
await sns.publishAsync(_izContext, {
|
|
197
214
|
TopicArn: `${topicArn}_In`,
|
|
198
215
|
Message: JSON.stringify(message)
|
|
199
|
-
})
|
|
200
|
-
)
|
|
216
|
+
});
|
|
217
|
+
})
|
|
201
218
|
);
|
|
202
219
|
}
|
|
203
220
|
|
|
204
|
-
|
|
221
|
+
if (finalRecords[0] && finalRecords[0].awaitingStepId) {
|
|
222
|
+
return finalRecords[0].awaitingStepId;
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
/**
|
|
@@ -240,11 +260,11 @@ export async function updateAwaitingMultipleStep(
|
|
|
240
260
|
}
|
|
241
261
|
|
|
242
262
|
/**
|
|
243
|
-
* Query and return the first pending step item that matches
|
|
263
|
+
* Query and return the first pending step item that matches awaitingStepId.
|
|
244
264
|
* @async
|
|
245
265
|
* @param {IzContext} _izContext - The Izara context.
|
|
246
266
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
247
|
-
* @returns {Promise<object|
|
|
267
|
+
* @returns {Promise<object|null>} The found item or null.
|
|
248
268
|
*/
|
|
249
269
|
export async function findPendingStepAwaitingMultipleSteps(
|
|
250
270
|
_izContext,
|
|
@@ -259,11 +279,14 @@ export async function findPendingStepAwaitingMultipleSteps(
|
|
|
259
279
|
awaitingStepId
|
|
260
280
|
);
|
|
261
281
|
|
|
262
|
-
|
|
282
|
+
if (items && items.length > 0) {
|
|
283
|
+
return items[0];
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
263
286
|
}
|
|
264
287
|
|
|
265
288
|
/**
|
|
266
|
-
* Query and return all pending step items matching
|
|
289
|
+
* Query and return all pending step items matching awaitingStepId.
|
|
267
290
|
* @async
|
|
268
291
|
* @param {IzContext} _izContext - The Izara context.
|
|
269
292
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
@@ -277,11 +300,11 @@ export async function findPendingStepsAwaitingMultipleSteps(
|
|
|
277
300
|
}
|
|
278
301
|
|
|
279
302
|
/**
|
|
280
|
-
* Retrieve
|
|
303
|
+
* Retrieve pendingStepId of first step matching awaitingStepId.
|
|
281
304
|
* @async
|
|
282
305
|
* @param {IzContext} _izContext - The Izara context.
|
|
283
306
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
284
|
-
* @returns {Promise<string|
|
|
307
|
+
* @returns {Promise<string|null>} pendingStepId of first item found or null.
|
|
285
308
|
*/
|
|
286
309
|
export async function findPendingStepIdAwaitingMultipleSteps(
|
|
287
310
|
_izContext,
|
|
@@ -291,11 +314,14 @@ export async function findPendingStepIdAwaitingMultipleSteps(
|
|
|
291
314
|
_izContext,
|
|
292
315
|
awaitingStepId
|
|
293
316
|
);
|
|
294
|
-
|
|
317
|
+
if (items && items[0] && items[0].pendingStepId) {
|
|
318
|
+
return items[0].pendingStepId;
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
295
321
|
}
|
|
296
322
|
|
|
297
323
|
/**
|
|
298
|
-
* Query and return all awaiting steps matching
|
|
324
|
+
* Query and return all awaiting steps matching pendingStepId.
|
|
299
325
|
* @async
|
|
300
326
|
* @param {IzContext} _izContext - The Izara context.
|
|
301
327
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -309,17 +335,16 @@ export async function findAwaitingMultipleStepByPending(
|
|
|
309
335
|
}
|
|
310
336
|
|
|
311
337
|
/**
|
|
312
|
-
* Check if all awaiting steps for a pendingStepId are completed.
|
|
313
|
-
* is provided, it updates its status to complete and returns values/errors.
|
|
338
|
+
* Check if all awaiting steps for a pendingStepId are completed.
|
|
314
339
|
* @async
|
|
315
340
|
* @param {IzContext} _izContext - The Izara context.
|
|
316
341
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
317
|
-
* @param {string|null} [currentAwaitingStepId=null] -
|
|
318
|
-
* @param {Array<any>} [errorsFound=[]] - Errors collected from
|
|
319
|
-
* @param {object} [returnValues={}] - Values returned from
|
|
320
|
-
* @param {object} [settings={ checkIsAllError: false }] -
|
|
321
|
-
* @param {boolean} [settings.checkIsAllError=false] -
|
|
322
|
-
* @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.
|
|
323
348
|
*/
|
|
324
349
|
export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
325
350
|
_izContext,
|
|
@@ -364,23 +389,29 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
364
389
|
|
|
365
390
|
_izContext.logger.debug(
|
|
366
391
|
'[Lib:checkAllAwaitingStepsFinishedWithReturnParams] awaitingStepItems',
|
|
367
|
-
awaitingStepItems
|
|
392
|
+
{ awaitingStepItems }
|
|
368
393
|
);
|
|
369
394
|
|
|
370
395
|
const hasIncomplete = awaitingStepItems.some(
|
|
371
396
|
item => item.awaitingStepId !== currentAwaitingStepId && !item.complete
|
|
372
397
|
);
|
|
373
398
|
|
|
399
|
+
let defaultAttrs = null;
|
|
400
|
+
if (awaitingStepItems[0] && awaitingStepItems[0].additionalAttributes) {
|
|
401
|
+
defaultAttrs = awaitingStepItems[0].additionalAttributes;
|
|
402
|
+
}
|
|
403
|
+
|
|
374
404
|
if (hasIncomplete) {
|
|
375
405
|
return {
|
|
376
406
|
isComplete: false,
|
|
377
|
-
|
|
407
|
+
collectedReturnValueByAwaitingStepId: null,
|
|
378
408
|
collectedErrors: [],
|
|
379
|
-
returnValues: null
|
|
409
|
+
returnValues: null,
|
|
410
|
+
additionalAttributes: defaultAttrs
|
|
380
411
|
};
|
|
381
412
|
}
|
|
382
413
|
|
|
383
|
-
const
|
|
414
|
+
const collectedReturnValueByAwaitingStepId = {};
|
|
384
415
|
let sharedReturnValues = null;
|
|
385
416
|
|
|
386
417
|
for (const item of awaitingStepItems) {
|
|
@@ -388,30 +419,42 @@ export async function checkAllAwaitingStepsFinishedWithReturnParams(
|
|
|
388
419
|
continue;
|
|
389
420
|
}
|
|
390
421
|
|
|
391
|
-
|
|
392
|
-
|
|
422
|
+
collectedReturnValueByAwaitingStepId[item.awaitingStepId] =
|
|
423
|
+
item.returnValues;
|
|
424
|
+
if (sharedReturnValues === null) {
|
|
425
|
+
sharedReturnValues = item.returnValues;
|
|
426
|
+
}
|
|
393
427
|
}
|
|
394
428
|
|
|
395
|
-
const collectedErrors =
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
+
}
|
|
400
438
|
|
|
401
|
-
|
|
439
|
+
const result = {
|
|
402
440
|
isComplete: true,
|
|
403
|
-
|
|
441
|
+
collectedReturnValueByAwaitingStepId,
|
|
404
442
|
collectedErrors,
|
|
405
443
|
returnValues: sharedReturnValues,
|
|
406
|
-
additionalAttributes:
|
|
407
|
-
...(settings.checkIsAllError && {
|
|
408
|
-
isAllError: awaitingStepItems.every(item => item.errorsFound?.length > 0)
|
|
409
|
-
})
|
|
444
|
+
additionalAttributes: defaultAttrs
|
|
410
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;
|
|
411
454
|
}
|
|
412
455
|
|
|
413
456
|
/**
|
|
414
|
-
* Clean up/delete all awaiting multiple steps records
|
|
457
|
+
* Clean up/delete all awaiting multiple steps records for pendingStepId.
|
|
415
458
|
* @async
|
|
416
459
|
* @param {IzContext} _izContext - The Izara context.
|
|
417
460
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
@@ -422,7 +465,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
422
465
|
pendingStepId
|
|
423
466
|
});
|
|
424
467
|
|
|
425
|
-
const
|
|
468
|
+
const awaitingStepItems = await _queryAwaitingStepsByPendingStepId(
|
|
426
469
|
_izContext,
|
|
427
470
|
pendingStepId
|
|
428
471
|
);
|
|
@@ -436,7 +479,7 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
436
479
|
);
|
|
437
480
|
|
|
438
481
|
await Promise.all(
|
|
439
|
-
|
|
482
|
+
awaitingStepItems.flatMap(
|
|
440
483
|
({ awaitingStepId, pendingStepId: itemPendingStepId }) => [
|
|
441
484
|
dynamodbSharedLib.deleteItem(_izContext, tableSteps, {
|
|
442
485
|
awaitingStepId,
|
|
@@ -454,12 +497,12 @@ export async function clearAllAwaitingSteps(_izContext, pendingStepId) {
|
|
|
454
497
|
}
|
|
455
498
|
|
|
456
499
|
/**
|
|
457
|
-
* Delete a specific awaiting step from Steps table (and
|
|
500
|
+
* Delete a specific awaiting step from Steps table (and byPending table).
|
|
458
501
|
* @async
|
|
459
502
|
* @param {IzContext} _izContext - The Izara context.
|
|
460
503
|
* @param {string} awaitingStepId - The ID of the awaiting step.
|
|
461
504
|
* @param {string} pendingStepId - The ID of the pending step.
|
|
462
|
-
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
505
|
+
* @param {Array<any>} [errorsFound=[]] - List of errors.
|
|
463
506
|
* @returns {Promise<void>}
|
|
464
507
|
*/
|
|
465
508
|
export async function removeAwaitingMultipleStep(
|
|
@@ -476,7 +519,8 @@ export async function removeAwaitingMultipleStep(
|
|
|
476
519
|
|
|
477
520
|
if (!awaitingStepId || !pendingStepId) {
|
|
478
521
|
throw new NoRetryError(
|
|
479
|
-
'[Lib:removeAwaitingMultipleStep]
|
|
522
|
+
'[Lib:removeAwaitingMultipleStep]' +
|
|
523
|
+
' awaitingStepId or pendingStepId required'
|
|
480
524
|
);
|
|
481
525
|
}
|
|
482
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
|
|