@izara_project/izara-core-library-asynchronous-flow 1.0.20 → 1.0.22

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.
@@ -1,1311 +0,0 @@
1
- /*
2
- Copyright (C) 2021 Sven Mason <http://izara.io>
3
-
4
- This program is free software: you can redistribute it and/or modify
5
- it under the terms of the GNU Affero General Public License as
6
- published by the Free Software Foundation, either version 3 of the
7
- License, or (at your option) any later version.
8
-
9
- This program is distributed in the hope that it will be useful,
10
- but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- GNU Affero General Public License for more details.
13
-
14
- You should have received a copy of the GNU Affero General Public License
15
- along with this program. If not, see <http://www.gnu.org/licenses/>.
16
- */
17
-
18
- 'use strict';
19
-
20
- // Core libraries
21
- import { NoRetryError } from '@izara_project/izara-core-library-core';
22
- import Logger from '@izara_project/izara-core-library-logger';
23
-
24
- // Data and service interaction libraries
25
- import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
26
- import sqsSharedLib from '@izara_project/izara-core-library-sqs';
27
-
28
- // External service interactions
29
- import { sqs } from '@izara_project/izara-core-library-external-request';
30
-
31
-
32
- /**
33
- * Create a field name for storing the unique-request id, with optional prefix.
34
- * @param {string} prefix
35
- * @param {string} [uniqueRequestIdFieldName='UniqueRequestId']
36
- * @returns {string}
37
- */
38
- function createFieldNameUniqueRequestId(
39
- prefix,
40
- uniqueRequestIdFieldName = 'UniqueRequestId'
41
- ) {
42
- return prefix + uniqueRequestIdFieldName;
43
- }
44
-
45
- /**
46
- * Create a concatenated awaitingStepId from composite parts.
47
- * @param {string} partitionKey
48
- * @param {string|number} sortId
49
- * @param {string} [prefix=""]
50
- * @returns {string}
51
- */
52
- function createConcatenatedAwaitingStepId(
53
- partitionKey,
54
- sortId,
55
- prefix = ""
56
- ) {
57
- return prefix + partitionKey + "_" + sortId
58
- }
59
-
60
- /**
61
- * Create a concatenated pendingStepId from parent/child ids.
62
- * @param {string} parentId
63
- * @param {string} childId
64
- * @returns {string}
65
- */
66
- function createConcatenatedPendingStepId(
67
- parentId,
68
- childId
69
- ) {
70
- return parentId + "_" + childId
71
- }
72
-
73
- /**
74
- * Create a simple awaitingStepId (prefix + partitionKey).
75
- * @param {string} partitionKey
76
- * @param {string} [prefix=""]
77
- * @returns {string}
78
- */
79
- function createAwaitingStepId(
80
- partitionKey,
81
- prefix = ""
82
- ) {
83
- return prefix + partitionKey
84
- }
85
-
86
- /**
87
- * Create a simple pendingStepId (prefix + identifierId).
88
- * @param {string} identifierId
89
- * @param {string} [prefix=""]
90
- * @returns {string}
91
- */
92
- function createPendingStepId(
93
- identifierId,
94
- prefix = ""
95
- ) {
96
- return prefix + identifierId
97
- }
98
-
99
- /**
100
- * Remove the known prefix from a pendingStepId and validate it is not equal to the prefix.
101
- * If prefix is empty, the original ID is returned.
102
- * @param {string} pendingStepId
103
- * @param {string} [prefix=""]
104
- * @returns {string}
105
- * @throws {NoRetryError} If the stripped value equals the prefix (invalid)
106
- */
107
- function explodePendingStepId(
108
- pendingStepId,
109
- prefix = ""
110
- ) {
111
- if (prefix === "") {
112
- return pendingStepId;
113
- } else {
114
- let identifierId = pendingStepId.slice(prefix.length)
115
- // ** validate identifierId == prefix
116
- if (identifierId == prefix) {
117
- throw new NoRetryError("IdentifierId should not be like prefix.");
118
- }
119
-
120
- Logger.debug("return explode:", identifierId);
121
- return identifierId;
122
- }
123
-
124
- }
125
-
126
- // copy from izara-core-library-trigger-cache
127
- /**
128
- * Build the field name that stores "cache complete" marker.
129
- * @param {string} [prefix='cache']
130
- * @returns {string}
131
- */
132
- function createFieldNameCacheComplete(prefix = 'cache') {
133
- return prefix + 'CacheComplete';
134
- }
135
-
136
- /**
137
- * Build the field name that stores a cache "status".
138
- * @param {string} [prefix='cache']
139
- * @returns {string}
140
- */
141
- function createFieldNameStatus(prefix = 'cache') {
142
- return prefix + 'Status';
143
- }
144
-
145
-
146
- //====================================== AwaitingMultipleSteps
147
- // AwaitingMultipleSteps is when a flow is awaiting multiple external flows before it can continue
148
- // One awaitingStepId can have multiple pendingStepIds that are awaiting it
149
- // One pendingStepId can have multiple awaitingStepIds it is awaiting
150
- // logic can prepend any prefix to awaitingStepId if want to be share one table and differentiate different external step ids
151
-
152
- /**
153
- * Create multiple awaiting records with optional per-record additional attributes.
154
- * Writes to both "AwaitingMultipleSteps" and "AwaitingMultipleStepByPending".
155
- * @async
156
- * @param {IzContext} _izContext
157
- * @param {AwaitingMultiInputRecord[]} records
158
- * @param {string} pendingStepId
159
- * @returns {Promise<void>}
160
- */
161
- async function createAwaitingMultipleStepsWithAdditionalAttributes(
162
- _izContext,
163
- records, // array of object including properties: awaitingStepId / (optional) additionalAttributes
164
- pendingStepId
165
- ) {
166
- _izContext.logger.debug("Lib:createAwaitingMultipleStepsWithAdditionalAttributes", {
167
- records: records,
168
- pendingStepId: pendingStepId
169
- });
170
- let promiseArray = []
171
-
172
- for (let record of records) {
173
-
174
- let attributesWhenCreate = {
175
- awaitingStepId: record.awaitingStepId,
176
- pendingStepId: pendingStepId,
177
- timestamp: Date.now(),
178
- ...record.additionalAttributes
179
- }
180
-
181
- promiseArray.push(
182
- dynamodbSharedLib.putItem(
183
- _izContext,
184
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
185
- attributesWhenCreate
186
- )
187
- );
188
- promiseArray.push(
189
- dynamodbSharedLib.putItem(
190
- _izContext,
191
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
192
- {
193
- pendingStepId: pendingStepId,
194
- awaitingStepId: record.awaitingStepId,
195
- timestamp: Date.now(),
196
- }
197
- ));
198
-
199
- }
200
-
201
- await Promise.all(promiseArray);
202
-
203
- }
204
-
205
- /**
206
- * Create multiple awaiting records given an array of awaitingStepIds.
207
- * Writes to both "AwaitingMultipleSteps" and "AwaitingMultipleStepByPending".
208
- * @async
209
- * @param {IzContext} _izContext
210
- * @param {string[]} records - array of awaitingStepIds
211
- * @param {string} pendingStepId
212
- * @returns {Promise<void>}
213
- */
214
- async function createAwaitingMultipleSteps(
215
- _izContext,
216
- records, // array of awaitingStepIds
217
- pendingStepId
218
- ) {
219
- _izContext.logger.debug("Lib:createAwaitingMultipleSteps", {
220
- records: records,
221
- pendingStepId: pendingStepId
222
- });
223
-
224
- // // 0001 ORG.
225
- let promiseArray = []
226
-
227
- for (let record of records) {
228
-
229
- let attributesWhenCreate = {
230
- awaitingStepId: record,
231
- pendingStepId: pendingStepId,
232
- timestamp: Date.now()
233
- }
234
- _izContext.logger.debug("putItem:AwaitingMultipleSteps +++++++", { awaitingStepId: attributesWhenCreate.awaitingStepId })
235
- promiseArray.push(
236
- dynamodbSharedLib.putItem(
237
- _izContext,
238
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
239
- attributesWhenCreate
240
- )
241
- );
242
-
243
-
244
- _izContext.logger.debug("putItem:AwaitingMultipleStepByPending", { awaitingStepId: attributesWhenCreate.awaitingStepId })
245
- promiseArray.push(
246
- dynamodbSharedLib.putItem(
247
- _izContext,
248
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
249
- attributesWhenCreate
250
- )
251
- );
252
-
253
- _izContext.logger.debug("check 0")
254
-
255
- }
256
- await Promise.all(promiseArray);
257
-
258
- }
259
-
260
- /**
261
- * Find all pendingStepIds that are waiting on a given awaitingStepId (multiple).
262
- * @async
263
- * @param {IzContext} _izContext
264
- * @param {string} awaitingStepId
265
- * @returns {Promise<string[]>}
266
- */
267
- async function findPendingStepIdsAwaitingMultipleSteps(
268
- _izContext,
269
- awaitingStepId
270
- ) {
271
-
272
- let pendingStepIds = [];
273
-
274
- let listPendingStepIds = await dynamodbSharedLib.query(
275
- _izContext,
276
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
277
- {
278
- awaitingStepId: awaitingStepId
279
- }
280
- );
281
-
282
- for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
283
- pendingStepIds.push(listPendingStepIds.Items[idx].pendingStepId)
284
- }
285
-
286
- return pendingStepIds
287
- }
288
-
289
- /**
290
- * Return the raw items waiting on a given awaitingStepId (multiple).
291
- * @async
292
- * @param {IzContext} _izContext
293
- * @param {string} awaitingStepId
294
- * @returns {Promise<AwaitingMultipleStepsItem[]>}
295
- */
296
- async function findPendingStepsAwaitingMultipleSteps(
297
- _izContext,
298
- awaitingStepId
299
- ) {
300
-
301
- let listPendingSteps = await dynamodbSharedLib.query(
302
- _izContext,
303
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
304
- {
305
- awaitingStepId: awaitingStepId
306
- }
307
- );
308
- _izContext.logger.debug("Record of awaitingStepId", listPendingSteps);
309
- return listPendingSteps.Items;
310
- }
311
-
312
- /**
313
- * Fetch exactly one pending step for a given awaitingStepId (throws if not 1).
314
- * @async
315
- * @param {IzContext} _izContext
316
- * @param {string} awaitingStepId
317
- * @returns {Promise<AwaitingMultipleStepsItem>}
318
- * @throws {NoRetryError} if there is not exactly one item
319
- */
320
- async function findPendingStepAwaitingMultipleSteps(
321
- _izContext,
322
- awaitingStepId
323
- ) {
324
-
325
- let listPendingSteps = await dynamodbSharedLib.query(
326
- _izContext,
327
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
328
- {
329
- awaitingStepId: awaitingStepId
330
- }
331
- );
332
- _izContext.logger.debug("Record of awaitingStepId", listPendingSteps)
333
-
334
- if (listPendingSteps.Items.length !== 1) {
335
- throw new NoRetryError("listPendingSteps not equals 1")
336
- };
337
- return listPendingSteps.Items[0];
338
- }
339
-
340
- // Use case:
341
- // Use only after checkAllAwaitingStepsFinished
342
- // Do NOT delete records in both tables (AwaitingMultipleSteps, AwaitingMultipleStepByPending) if AwaitingMultipleSteps is not yet finished
343
- // Delete records from both tables only when checkAllAwaitingStepsFinished = true
344
- // Example usage: flow validateCart lambda ProcessCartOrderFromTraversal
345
- /**
346
- * Given a pendingStepId, return all awaiting steps (by the ByPending table).
347
- * Use only after confirming all awaiting steps are finished, and clean up both tables accordingly.
348
- * @async
349
- * @param {IzContext} _izContext
350
- * @param {string} pendingStepId
351
- * @returns {Promise<AwaitingMultipleStepByPendingItem[]>}
352
- */
353
- async function findAwaitingMultipleStepByPending(
354
- _izContext,
355
- pendingStepId
356
- ) {
357
-
358
- let listAwitingSteps = await dynamodbSharedLib.query(
359
- _izContext,
360
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
361
- {
362
- pendingStepId: pendingStepId
363
- }
364
- );
365
- _izContext.logger.debug("Record of AwaitingMultipleStepByPending", listAwitingSteps);
366
- return listAwitingSteps.Items;
367
- }
368
-
369
- //====================================== AwaitingStep //======================================
370
- // AwaitingStep is when a flow is awaiting one external flow, and will continue it's flow once that one external flow is complete
371
- // One awaitingStepId can have multiple pendingStepIds that are awaiting it
372
- // logic can prepend any prefix to awaitingStepId if want to be share one table and differentiate different external step ids
373
-
374
- /**
375
- * Create a single AwaitingStep record, optionally attaching additional attributes and callingFlowConfig.
376
- * @async
377
- * @param {IzContext} _izContext
378
- * @param {string} awaitingStepId
379
- * @param {string} pendingStepId
380
- * @param {Attrs} [additionalAttributes={}]
381
- * @param {Attrs} [callingFlowConfig={}]
382
- * @returns {Promise<void>}
383
- */
384
- async function createAwaitingStep(
385
- _izContext,
386
- awaitingStepId,
387
- pendingStepId,
388
- additionalAttributes = {}, // save in top level
389
- callingFlowConfig = {} // optional
390
- ) {
391
- console.log("Lib createAwaitingStep:", {
392
- awaitingStepId,
393
- pendingStepId,
394
- additionalAttributes,
395
- callingFlowConfig
396
- });
397
-
398
- let attributesWhenCreate = {
399
- awaitingStepId: awaitingStepId,
400
- pendingStepId: pendingStepId,
401
- timestamp: Date.now(),
402
- uniqueRequestId: _izContext.uniqueRequestId,
403
- }
404
- // loop if not empty
405
- for (let additionalAttribute in additionalAttributes) {
406
- attributesWhenCreate[additionalAttribute] = additionalAttributes[additionalAttribute] // each additionalAttributes
407
- }
408
- if (Object.keys(callingFlowConfig).length > 0) {
409
- Object.assign(attributesWhenCreate, { callingFlowConfig })
410
- };
411
-
412
- await dynamodbSharedLib.putItem(
413
- _izContext,
414
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
415
- attributesWhenCreate
416
- );
417
- }
418
-
419
- /**
420
- * Find all pendingStepIds that are waiting on a given awaitingStepId (single).
421
- * @async
422
- * @param {IzContext} _izContext
423
- * @param {string} awaitingStepId
424
- * @returns {Promise<string[]>}
425
- */
426
- async function findPendingStepIdsAwaitingStep(
427
- _izContext,
428
- awaitingStepId
429
- ) {
430
-
431
- let pendingStepIds = [];
432
-
433
- let listPendingStepIds = await dynamodbSharedLib.query(
434
- _izContext,
435
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
436
- {
437
- awaitingStepId: awaitingStepId
438
- }
439
- );
440
-
441
- for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
442
- pendingStepIds.push(listPendingStepIds.Items[idx].pendingStepId)
443
- }
444
-
445
- return pendingStepIds
446
- }
447
-
448
- /**
449
- * Return the raw items waiting on a given awaitingStepId (single).
450
- * @async
451
- * @param {IzContext} _izContext
452
- * @param {string} awaitingStepId
453
- * @returns {Promise<AwaitingStepItem[]>}
454
- */
455
- async function findPendingStepsAwaitingStep(
456
- _izContext,
457
- awaitingStepId
458
- ) {
459
-
460
- let listPendingSteps = await dynamodbSharedLib.query(
461
- _izContext,
462
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
463
- {
464
- awaitingStepId: awaitingStepId
465
- }
466
- );
467
- _izContext.logger.debug("Record of awaitingStepId", listPendingSteps);
468
- return listPendingSteps.Items;
469
- }
470
-
471
- /**
472
- * Fetch exactly one pending step for a given awaitingStepId (throws if not 1).
473
- * @async
474
- * @param {IzContext} _izContext
475
- * @param {string} awaitingStepId
476
- * @returns {Promise<AwaitingStepItem>}
477
- * @throws {NoRetryError} if there is not exactly one item
478
- */
479
- async function findPendingStepAwaitingStep(
480
- _izContext,
481
- awaitingStepId
482
- ) {
483
-
484
- let listPendingSteps = await dynamodbSharedLib.query(
485
- _izContext,
486
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
487
- {
488
- awaitingStepId: awaitingStepId
489
- }
490
- );
491
- _izContext.logger.debug("Record of awaitingStepId", listPendingSteps)
492
-
493
- if (listPendingSteps.Items.length !== 1) {
494
- throw new NoRetryError("listPendingSteps not equals 1")
495
- };
496
- return listPendingSteps.Items[0];
497
- }
498
-
499
- // for get callingFlow save in awaitingStep and remove property callingFlow in awaitingStep
500
- /**
501
- * Get one awaiting step by id and also extract (and remove) its callingFlowConfig (if present).
502
- * Returns a tuple: [AwaitingStepItemWithoutCallingFlow, callingFlowConfig|null].
503
- * @async
504
- * @param {IzContext} _izContext
505
- * @param {string} awaitingStepId
506
- * @returns {Promise<[AwaitingStepItem, Attrs|null]>}
507
- */
508
- async function findPendingStepAwaitingStepWithCallingFlow(
509
- _izContext,
510
- awaitingStepId,
511
- ) {
512
-
513
- let callingFlowConfig = null;
514
- let awaitingStep = await findPendingStepAwaitingStep(
515
- _izContext,
516
- awaitingStepId
517
- );
518
-
519
- // callingFlowConfig.
520
- // cleaning object awaitingStep not have callingFlowConfig after return.
521
- if (awaitingStep.hasOwnProperty("callingFlowConfig")) {
522
- callingFlowConfig = awaitingStep.callingFlowConfig
523
- delete awaitingStep.callingFlowConfig
524
- console.log("after delete awaitingStep", awaitingStep);
525
- };
526
-
527
- return [awaitingStep, callingFlowConfig]
528
- }
529
-
530
- /**
531
- * Mark current awaiting step as complete (in *_ByPending), then check if all awaiting steps are done.
532
- * Returns true if all are finished; false otherwise.
533
- * @async
534
- * @param {IzContext} _izContext
535
- * @param {string} pendingStepId
536
- * @param {string|null} [currentAwaitingStepId=null]
537
- * @param {any[]} [errorsFound=[]]
538
- * @param {Attrs} [additionalAttributes={}]
539
- * @returns {Promise<boolean>}
540
- */
541
- async function checkAllAwaitingStepsFinished(
542
- _izContext,
543
- pendingStepId,
544
- currentAwaitingStepId = null,
545
- errorsFound = [],
546
- additionalAttributes = {}
547
- ) {
548
-
549
- let remaining = await checkAllAwaitingStepsFinishedWithError(
550
- _izContext,
551
- pendingStepId,
552
- currentAwaitingStepId,
553
- errorsFound,
554
- additionalAttributes
555
- );
556
- return remaining;
557
- }
558
-
559
- /**
560
- * Same as checkAllAwaitingStepsFinished but writes errors/additionalAttributes to the *_ByPending row.
561
- * @async
562
- * @param {IzContext} _izContext
563
- * @param {string} pendingStepId
564
- * @param {string|null} [currentAwaitingStepId=null]
565
- * @param {any[]} [errorsFound=[]]
566
- * @param {Attrs} [additionalAttributes={}]
567
- * @returns {Promise<boolean>}
568
- */
569
- async function checkAllAwaitingStepsFinishedWithError(
570
- _izContext,
571
- pendingStepId,
572
- currentAwaitingStepId = null,
573
- errorsFound = [],
574
- additionalAttributes = {}
575
- ) {
576
- _izContext.logger.debug("Lib:checkAllAwaitingStepsFinishedWithError", {
577
- pendingStepId: pendingStepId,
578
- currentAwaitingStepId: currentAwaitingStepId,
579
- errorsFound: errorsFound
580
- });
581
-
582
- if (currentAwaitingStepId) {
583
- await dynamodbSharedLib.updateItem(
584
- _izContext,
585
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
586
- {
587
- pendingStepId: pendingStepId,
588
- awaitingStepId: currentAwaitingStepId
589
- },
590
- {
591
- complete: true,
592
- errorsFound: errorsFound,
593
- ...additionalAttributes
594
- },
595
- );
596
-
597
- }
598
- let listPendingStepIds = await dynamodbSharedLib.query(
599
- _izContext,
600
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
601
- {
602
- pendingStepId: pendingStepId
603
- },
604
- {
605
- limit: 50 // arbitrary limit so not pull too many results, need >2 because some others might be marked as "complete" = true
606
- }
607
- );
608
- for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
609
-
610
- // if have error found return errorsFound
611
- // wrap function
612
-
613
- if (currentAwaitingStepId && listPendingStepIds.Items[idx].awaitingStepId == currentAwaitingStepId) {
614
- continue;
615
- }
616
- // if any record found not set to complete then there are steps not yet finished
617
- if (!listPendingStepIds.Items[idx].complete) {
618
- return false;
619
- }
620
- }
621
-
622
- return true; // all passed
623
-
624
-
625
- // // mark currentAwaitingStepId as complete, this is done to protect against race condition in between checking all complete
626
- // // and then removing the record, we need to let other requests know this record is complete and need to do this before
627
- // // querying all remaining records, otherwise if have only two remaining requests they might both show as records remaining
628
- // // (seeing each other) and not continue flow, then each deletes their record and no records remain to trigger next step.
629
- // // can result in race condition where multiple requests see all records finished, code has to account for this, but should already do so for idempotence
630
- // // add query that updates the currentAwaitingStepId to have attribute "complete" = true
631
- // if (currentAwaitingStepId) {
632
- // await dynamodbSharedLib.updateItem(
633
- // _izContext,
634
- // dynamodbSharedLib.tableName(tableName),
635
- // {
636
- // awaitingStepId: currentAwaitingStepId,
637
- // pendingStepId: pendingStepId
638
- // },
639
- // {
640
- // complete: true
641
- // },
642
- // )
643
- // }
644
- // let listPendingStepIds = await dynamodbSharedLib.query(
645
- // _izContext,
646
- // dynamodbSharedLib.tableName(tableName),
647
- // {
648
- // pendingStepId: pendingStepId
649
- // },
650
- // {
651
- // indexName: dynamodbSharedLib.tableName(tableName + 'Index'),
652
- // limit: 50 // arbitrary limit so not pull too many results, need >2 because some others might be marked as "complete" = true
653
- // }
654
- // );
655
- // for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
656
-
657
- // if (currentAwaitingStepId && listPendingStepIds.Items[idx].awaitingStepId == currentAwaitingStepId) {
658
- // continue;
659
- // }
660
- // // if any record found not set to complete then there are steps not yet finished
661
- // if (!listPendingStepIds.Items[idx].complete) {
662
- // return false;
663
- // }
664
- // }
665
-
666
- // return true;
667
- }
668
-
669
- /**
670
- * Remove all awaiting records for a given pendingStepId from both tables.
671
- * @async
672
- * @param {IzContext} _izContext
673
- * @param {string} pendingStepId
674
- * @returns {Promise<boolean>} true if delete flow processed
675
- */
676
- async function clearAllAwaitingSteps(
677
- _izContext,
678
- pendingStepId
679
- ) {
680
- // ----- query items from GSI table
681
- let listPendingStepIds = await dynamodbSharedLib.query(
682
- _izContext,
683
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
684
- {
685
- pendingStepId: pendingStepId
686
- },
687
- );
688
- _izContext.logger.debug('listPendingStepIds', listPendingStepIds);
689
-
690
- let promiseArray = []
691
-
692
- for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
693
-
694
- _izContext.logger.debug(`delete item in dynamodb AwaitingMultipleSteps`)
695
- promiseArray.push(
696
- dynamodbSharedLib.deleteItem(
697
- _izContext,
698
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
699
- {
700
- awaitingStepId: listPendingStepIds.Items[idx].awaitingStepId,
701
- pendingStepId: listPendingStepIds.Items[idx].pendingStepId
702
- }
703
- )
704
- );
705
-
706
- _izContext.logger.debug(`delete item in dynamodb AwaitingMultipleStepByPending`)
707
- promiseArray.push(
708
- dynamodbSharedLib.deleteItem(
709
- _izContext,
710
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
711
- {
712
- pendingStepId: listPendingStepIds.Items[idx].pendingStepId,
713
- awaitingStepId: listPendingStepIds.Items[idx].awaitingStepId
714
- }
715
- )
716
- );
717
- }
718
-
719
- await Promise.all(promiseArray);
720
-
721
- return true;
722
- }
723
-
724
- /**
725
- * Remove a single AwaitingStep record (single dependency table).
726
- * @async
727
- * @param {IzContext} _izContext
728
- * @param {string} awaitingStepId
729
- * @param {string} pendingStepId
730
- * @returns {Promise<void>}
731
- */
732
- async function removeAwaitingStep(
733
- _izContext,
734
- awaitingStepId,
735
- pendingStepId
736
- ) {
737
-
738
- await dynamodbSharedLib.deleteItem(
739
- _izContext,
740
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
741
- {
742
- awaitingStepId: awaitingStepId,
743
- pendingStepId: pendingStepId
744
- }
745
- );
746
- }
747
-
748
- /**
749
- * Remove items for AwaitingMultipleSteps and (conditionally) AwaitingMultipleStepByPending.
750
- * If errorsFound is empty, delete from ByPending table as well.
751
- * @async
752
- * @param {IzContext} _izContext
753
- * @param {string} awaitingStepId
754
- * @param {string} pendingStepId
755
- * @param {any[]} [errorsFound=[]]
756
- * @returns {Promise<void>}
757
- */
758
- async function removeAwaitingMultipleStep(
759
- _izContext,
760
- awaitingStepId,
761
- pendingStepId,
762
- errorsFound = []
763
- ) {
764
- _izContext.logger.debug("Lib:removeAwaitingMultipleStep", {
765
- awaitingStepId: awaitingStepId,
766
- pendingStepId: pendingStepId
767
- })
768
-
769
- let promiseArray = []
770
-
771
-
772
- let keyValues = {
773
- awaitingStepId: awaitingStepId,
774
- pendingStepId: pendingStepId
775
- }
776
-
777
- let keyValuesForByPending = {
778
- pendingStepId: pendingStepId,
779
- awaitingStepId: awaitingStepId
780
- }
781
- _izContext.logger.debug('deleting ... ', {
782
- keyValues,
783
- keyValuesForByPending
784
- });
785
-
786
- _izContext.logger.debug(`delete item in dynamodb AwaitingMultipleSteps ==> awaitingStepId:${keyValues.awaitingStepId}`)
787
- promiseArray.push(
788
- dynamodbSharedLib.deleteItem(
789
- _izContext,
790
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleSteps"),
791
- keyValues
792
- ));
793
-
794
- if (errorsFound.length == 0) {
795
- _izContext.logger.debug(`delete item in dynamodb AwaitingMultipleStepByPending ==> awaitingStepId:${keyValues.awaitingStepId}`)
796
- promiseArray.push(
797
- dynamodbSharedLib.deleteItem(
798
- _izContext,
799
- await dynamodbSharedLib.tableName(_izContext, "AwaitingMultipleStepByPending"),
800
- keyValuesForByPending
801
- ));
802
- }
803
- await Promise.all(promiseArray);
804
-
805
- }
806
-
807
- // only works with AwaitingStep, does not work with AwaitingMultipleSteps, need to make new function that checks conditional pass? If pass delete from byPending table too
808
- /**
809
- * Remove an AwaitingStep item only if its stored UniqueRequestId matches `checkUniqueRequestId`.
810
- * Uses a conditional expression; will not error if the condition fails.
811
- * @async
812
- * @param {IzContext} _izContext
813
- * @param {string} awaitingStepId
814
- * @param {string} pendingStepId
815
- * @param {string} checkUniqueRequestId
816
- * @param {string} [prefix=""] - Optional prefix for UniqueRequestId field name
817
- * @returns {Promise<void>}
818
- */
819
- async function removeAwaitingStepWithCheckUniqueRequestId(
820
- _izContext,
821
- awaitingStepId,
822
- pendingStepId,
823
- checkUniqueRequestId,
824
- prefix = ""
825
- ) {
826
-
827
- // if logicalElements add queryElements.logicalElements
828
- // set condition uniqueRequestId match with exists
829
- const queryElementsCondition = {
830
- additionalAttributes: {
831
- checkUniqueRequestId: checkUniqueRequestId,
832
- },
833
- logicalElements: [
834
- {
835
- type: "logical",
836
- comparison: "equals",
837
- lhsAttribute: createFieldNameUniqueRequestId(prefix),
838
- rhsReference: "checkUniqueRequestId"
839
- },
840
- ]
841
- }
842
-
843
- await dynamodbSharedLib.deleteItem(
844
- _izContext,
845
- await dynamodbSharedLib.tableName(_izContext, "AwaitingStep"),
846
- {
847
- awaitingStepId: awaitingStepId,
848
- pendingStepId: pendingStepId
849
- },
850
- queryElementsCondition,
851
- {
852
- errorOnConditionalExpNotPass: false
853
- }
854
- );
855
- }
856
-
857
- //====================================== end AwaitingStep
858
-
859
- /**
860
- * Checks if record has uniqueRequestId set, if not set to this requests uniqueRequestId and continue to process
861
- * if already set check if matches this request uniqueRequestId, if yes continue to process, if not then stop processing
862
- * status: "process"|"stop"|"recordNotFound"
863
-
864
- * if unable to set uniqueRequestId (because other thread updated it) throw NoRetryError
865
- * conditional update existingRecord set uniqueRequestId = _izContext.uniqueRequestId
866
- * conditional check uniqueRequestId still not exist in case another thread added
867
- * if conditional not pass "continue" and try again
868
-
869
- * @returns [string] [returnStatus, existingRecord]
870
- */
871
-
872
-
873
- /**
874
- * Coordinate unique-request processing over a record:
875
- * - If no record: returns ["recordNotFound", {}]
876
- * - If record exists but no uniqueRequestId: tries to set it via conditional update
877
- * - If record has a different uniqueRequestId: returns ["stop", existingRecord]
878
- * - If record has the same uniqueRequestId: returns ["process", existingRecord]
879
- *
880
- * Will loop up to 2 attempts to avoid races; throws on 3rd iteration.
881
- *
882
- * @async
883
- * @param {IzContext} _izContext
884
- * @param {string} tableName - Logical table name known by dynamodbSharedLib.tableName
885
- * @param {DynamoKey} primaryKey - Primary key for the target record
886
- * @param {string} [prefix=''] - Optional field-name prefix
887
- * @param {string|null} [overwriteUniqueRequestId=null] - If provided, use this as the "current" unique request id
888
- * @param {string} [uniqueRequestIdFieldName="UniqueRequestId"] - Base field name before prefix
889
- * @returns {Promise<[UniqueRequestStatus, Attrs]>}
890
- * @throws {NoRetryError} If unable to set uniqueRequestId after 2 tries
891
- */
892
- async function checkUniqueRequestProcessing(
893
- _izContext,
894
- tableName,
895
- primaryKey,
896
- prefix = '',
897
- overwriteUniqueRequestId = null,
898
- uniqueRequestIdFieldName = "UniqueRequestId" // if set will check/add this fieldname with _izContext.uniqueRequestId value
899
- ) {
900
- try {
901
-
902
- _izContext.logger.debug('current uniqueRequestId:', _izContext.uniqueRequestId)
903
-
904
- let uniqueRequestId = _izContext.uniqueRequestId;
905
- if (overwriteUniqueRequestId) {
906
- uniqueRequestId = overwriteUniqueRequestId;
907
- }
908
-
909
- // add prefix
910
- uniqueRequestIdFieldName = createFieldNameUniqueRequestId(
911
- prefix,
912
- uniqueRequestIdFieldName
913
- );
914
-
915
- //loop 3 times incase status changes, on 3rd iteration throw error (so checks 2 times)
916
- for (let i = 1; i <= 3; i++) {
917
- if (i == 3) {
918
- throw new NoRetryError(`unable to set uniqueRequestId in table ${tableName}, record`, primaryKey);
919
- }
920
- //* get record from dynamo
921
- let existingRecord = await dynamodbSharedLib.getItem(
922
- _izContext,
923
- await dynamodbSharedLib.tableName(_izContext, tableName),
924
- primaryKey,
925
- )
926
- _izContext.logger.debug('existingRecord:', existingRecord);
927
-
928
- let returnStatus = "process";
929
-
930
- if (!existingRecord) {
931
- // return ["recordNotFound", null]; // cannot return null, js cannot found object and throw error and dont know where it is?
932
- return ["recordNotFound", {}];
933
-
934
- } else if (!existingRecord[uniqueRequestIdFieldName]) {
935
- // uniqueRequestId not yet exist
936
-
937
- let updateAttributes = [
938
- {
939
- attributeName: uniqueRequestIdFieldName,
940
- action: "set",
941
- value: _izContext.uniqueRequestId,
942
- },
943
- ]
944
- //* conditional check uniqueRequestId still not exist in case another thread added
945
- const queryElementsWhenUpdate = {
946
- logicalElements: [
947
- {
948
- type: "function",
949
- function: "attribute_not_exists",
950
- attribute: uniqueRequestIdFieldName,
951
- }
952
- ],
953
- returnValues: 'ALL_NEW'
954
- }
955
- // // ----- main query ---------
956
- let updateRecord = null;
957
- try {
958
- updateRecord = await dynamodbSharedLib.updateItem(
959
- _izContext,
960
- await dynamodbSharedLib.tableName(_izContext, tableName),
961
- primaryKey,
962
- updateAttributes,
963
- queryElementsWhenUpdate,
964
- { complexAttributes: true }, // force to complex
965
- )
966
-
967
- return [returnStatus, updateRecord];
968
- } catch (err) { // catch error when put and handle errors.
969
- _izContext.logger.debug('updateItem storedCache expired err', err);
970
- if (err.name == 'ConditionalCheckFailedException') {
971
- continue;
972
- } else { // e.g. ProvisionedThroughputExceededException
973
- // throw new Error('Unhandled Error when putItem', err) // TT, if throw will stop
974
- throw (err) // TT, if throw will stop
975
- }
976
- } //end catch err
977
-
978
- } else if (existingRecord[uniqueRequestIdFieldName] !== uniqueRequestId) {
979
-
980
- // when another request/uniqueRequestId need to stop process
981
- return ["stop", existingRecord];
982
-
983
- } else if (existingRecord[uniqueRequestIdFieldName] == uniqueRequestId) {
984
-
985
- // if existingRecord[uniqueRequestIdFieldName] == uniqueRequestId then return "process
986
- return [returnStatus, existingRecord];
987
-
988
- }
989
-
990
- } // end loop 2 times
991
- //should never get here
992
- throw new NoRetryError('unexpected logic flow in checkUniqueRequestProcessing');
993
-
994
- } catch (err) {
995
- _izContext.logger.error('ERROR checkUniqueRequestProcessing:', err)
996
- throw (err)
997
- }
998
- };
999
-
1000
- // ----- Helper funtion, common use case in triggerFlow ---------
1001
- /**
1002
- * Convenience wrapper around checkAndGetTimeCacheComplete.
1003
- * Returns [isSameOrUnset, uniqueRequestIdWhenComplete].
1004
- * @async
1005
- * @param {IzContext} _izContext
1006
- * @param {string} fullMainTableName - Fully resolved table name (not logical)
1007
- * @param {DynamoKey} keyValues
1008
- * @param {number|string} timeCacheComplete - Caller’s expected cache mark
1009
- * @param {string} prefix - Field prefix used in cache fields
1010
- * @returns {Promise<[boolean, string|undefined]>}
1011
- */
1012
- async function checkTimeCacheComplete(
1013
- _izContext,
1014
- fullMainTableName,
1015
- keyValues,
1016
- timeCacheComplete,
1017
- prefix
1018
- ) {
1019
- let [returnValue, newTimeCacheComplete, uniqueRequestId] = await checkAndGetTimeCacheComplete(
1020
- _izContext,
1021
- fullMainTableName,
1022
- keyValues,
1023
- timeCacheComplete,
1024
- prefix
1025
- );
1026
- return [returnValue, uniqueRequestId];
1027
- }
1028
-
1029
- /**
1030
- * Read cache fields from a record and compare cacheComplete value to caller’s `timeCacheComplete`.
1031
- * If stored differs (and is present), returns [false, storedValue].
1032
- * Otherwise returns [true, storedValue, uniqueRequestId].
1033
- * @async
1034
- * @param {IzContext} _izContext
1035
- * @param {string} fullMainTableName - Fully resolved table name (not logical)
1036
- * @param {DynamoKey} keyValues
1037
- * @param {number|string} timeCacheComplete
1038
- * @param {string} prefix
1039
- * @returns {Promise<[boolean, any, string|undefined]>}
1040
- * @throws {NoRetryError} If record cannot be fetched
1041
- */
1042
- async function checkAndGetTimeCacheComplete(
1043
- _izContext,
1044
- fullMainTableName,
1045
- keyValues,
1046
- timeCacheComplete,
1047
- prefix
1048
- ) {
1049
- _izContext.logger.debug('event checkTimeCacheComplete', {
1050
- fullMainTableName: fullMainTableName,
1051
- keyValues: keyValues,
1052
- timeCacheComplete: timeCacheComplete,
1053
- prefix: prefix
1054
- });
1055
-
1056
- let uniqueRequestIdFieldName = createFieldNameUniqueRequestId('cache');
1057
- let cacheCompleteFieldName = createFieldNameCacheComplete(prefix);
1058
- let statusFieldName = createFieldNameStatus(prefix);
1059
-
1060
- let getTimeCacheComplete = await dynamodbSharedLib.getItem(
1061
- _izContext,
1062
- fullMainTableName,
1063
- keyValues
1064
- )
1065
- _izContext.logger.debug(`getTimeCacheComplete from ${fullMainTableName}: `, getTimeCacheComplete);
1066
-
1067
- if (!getTimeCacheComplete) {
1068
- throw new NoRetryError(`cannot getTimeCacheComplete from ${fullMainTableName}`);
1069
- }
1070
-
1071
- // check if exist and match with initail create not changing in flow.
1072
- // if (!getTimeCacheComplete[uniqueRequestIdFieldName]
1073
- // // || !getTimeCacheComplete[cacheCompleteFieldName]
1074
- // || (getTimeCacheComplete[cacheCompleteFieldName]
1075
- // && getTimeCacheComplete[cacheCompleteFieldName] !== timeCacheComplete
1076
- // && getTimeCacheComplete[statusFieldName] != "complete")
1077
- // ) {
1078
- // return [false, getTimeCacheComplete[cacheCompleteFieldName]];
1079
- // } else {
1080
- // return [true, getTimeCacheComplete[cacheCompleteFieldName], getTimeCacheComplete[uniqueRequestIdFieldName]];
1081
- // }
1082
-
1083
- if (getTimeCacheComplete[cacheCompleteFieldName]
1084
- && getTimeCacheComplete[cacheCompleteFieldName] !== timeCacheComplete
1085
- ) {
1086
- return [false, getTimeCacheComplete[cacheCompleteFieldName]];
1087
- } else {
1088
- return [true, getTimeCacheComplete[cacheCompleteFieldName], getTimeCacheComplete[uniqueRequestIdFieldName]];
1089
- }
1090
- }
1091
-
1092
-
1093
- // ----- shared both stored cache and triggered cache, check uniqueRequestId changed ---------
1094
- /**
1095
- * Ensure the cache object’s "uniqueRequestIdCompleteFieldName" equals the given checkUniqueRequestId.
1096
- * @async
1097
- * @param {IzContext} _izContext
1098
- * @param {string} fullMainTableName
1099
- * @param {DynamoKey} keyValues
1100
- * @param {string} checkUniqueRequestId
1101
- * @param {string} uniqueRequestIdCompleteFieldName
1102
- * @returns {Promise<boolean>}
1103
- */
1104
- async function checkCacheUniqueRequestId(
1105
- _izContext,
1106
- fullMainTableName,
1107
- keyValues,
1108
- checkUniqueRequestId,
1109
- uniqueRequestIdCompleteFieldName
1110
- ) {
1111
-
1112
- let cacheObject = await dynamodbSharedLib.getItem(
1113
- _izContext,
1114
- fullMainTableName,
1115
- keyValues
1116
- )
1117
- _izContext.logger.debug('cacheObject', cacheObject);
1118
-
1119
- if (!cacheObject[uniqueRequestIdCompleteFieldName] || cacheObject[uniqueRequestIdCompleteFieldName] !== checkUniqueRequestId) {
1120
- return false
1121
- } else {
1122
- return true
1123
- }
1124
- }
1125
-
1126
-
1127
- // use shared findPendingStepIdsAwaitingStep
1128
- // module.exports.findPendingStepIdsAwaitingStepM = async (
1129
- // _izContext,
1130
- // awaitingStepId,
1131
- // tableName = "AwaitingMultipleSteps"
1132
- // ) => {
1133
-
1134
- // let pendingStepIds = [];
1135
-
1136
- // let listPendingStepIds = await dynamodbSharedLib.query(
1137
- // _izContext,
1138
- // dynamodbSharedLib.tableName(tableName),
1139
- // {
1140
- // awaitingStepId: awaitingStepId
1141
- // }
1142
- // );
1143
-
1144
- // for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
1145
- // pendingStepIds.push(listPendingStepIds.Items[idx].pendingStepId)
1146
- // }
1147
-
1148
- // return pendingStepIds
1149
- // }
1150
-
1151
-
1152
- // use shared removeAwaitingStep
1153
- // module.exports.removeAwaitingStep = async (
1154
- // _izContext,
1155
- // awaitingStepId,
1156
- // pendingStepId,
1157
- // tableName = "AwaitingMultipleSteps"
1158
- // ) => {
1159
-
1160
- // await dynamodbSharedLib.deleteItem(
1161
- // _izContext,
1162
- // dynamodbSharedLib.tableName(tableName),
1163
- // {
1164
- // awaitingStepId: awaitingStepId,
1165
- // pendingStepId: pendingStepId
1166
- // }
1167
- // );
1168
- // }
1169
-
1170
-
1171
- //====================================== end AwaitingMultipleSteps
1172
-
1173
-
1174
-
1175
- //====================================== end Multiple Lambda Invocations (Logic pagination of handling results)
1176
-
1177
- /**
1178
- * Validate and normalize a DynamoDB pagination startKey object.
1179
- * Ensures partitionKeyFieldName/sortKeyFieldName are alphanumeric/underscore/hyphen.
1180
- * If startKey is empty object, returns `null` for easier processing downstream.
1181
- * @param {IzContext} _izContext
1182
- * @param {Attrs|null|undefined} startKey
1183
- * @param {string} partitionKeyFieldName
1184
- * @param {string} sortKeyFieldName
1185
- * @returns {Attrs|null}
1186
- * @throws {NoRetryError} On invalid field names or malformed startKey
1187
- */
1188
- function validateStartKeyParam(
1189
- _izContext,
1190
- startKey,
1191
- partitionKeyFieldName,
1192
- sortKeyFieldName
1193
- ) {
1194
- const stringNotEmptyRegex = /^[A-Za-z0-9_-]+$/;
1195
-
1196
- if (
1197
- !partitionKeyFieldName || !sortKeyFieldName
1198
- || !stringNotEmptyRegex.test(partitionKeyFieldName)
1199
- || !stringNotEmptyRegex.test(sortKeyFieldName)
1200
- ) {
1201
- throw new NoRetryError("validateStartKeyParam: Invalid partitionKeyFieldName or sortKeyFieldName");
1202
- }
1203
-
1204
- if (startKey && Object.keys(startKey).length != 0) {
1205
- if (!startKey[partitionKeyFieldName] || !startKey[sortKeyFieldName]) {
1206
- throw new NoRetryError("validateStartKeyParam: Invalid startKey, missing partitionKeyFieldName or sortKeyFieldName");
1207
- }
1208
- } else {
1209
- startKey = null; // if empty set to null so lib functions process correctly
1210
- }
1211
-
1212
- return startKey;
1213
- }
1214
-
1215
- /**
1216
- * For paginated multi-invocation flows: validates the current invocation count,
1217
- * attaches the (optional) next startKey to the message, increments the count,
1218
- * and re-enqueues the message to the specified SQS queue.
1219
- * @async
1220
- * @param {IzContext} _izContext
1221
- * @param {Attrs} messageProperty - The message body object to re-dispatch
1222
- * @param {Attrs} [passOnStartKey={}] - Optional startKey to pass along
1223
- * @param {number} numberInvocation - Current invocation count
1224
- * @param {string} queueName - Logical queue name resolvable via sqsSharedLib.sqsQueueUrl
1225
- * @returns {Promise<void>}
1226
- * @throws {NoRetryError} If `numberInvocation` exceeds internal safety limit
1227
- */
1228
- async function validateMultipleInvocations(
1229
- _izContext,
1230
- messageProperty,
1231
- passOnStartKey = {},
1232
- numberInvocation,
1233
- queueName
1234
- ) {
1235
- _izContext.logger.debug("Lib:validateMultipleInvocations", {
1236
- messageProperty: messageProperty,
1237
- passOnStartKey: passOnStartKey,
1238
- numberInvocation: numberInvocation,
1239
- queueName: queueName
1240
- });
1241
- try {
1242
- const maxProcessInvocationCountImport = 20
1243
- if (numberInvocation >= maxProcessInvocationCountImport) {
1244
- _izContext.logger.error("numberInvocation exceeds maxProcessInvocationCountImport limit");
1245
- throw new NoRetryError("numberInvocation exceeds maxProcessInvocationCountImport limit");
1246
- };
1247
-
1248
- if (passOnStartKey) {
1249
- messageProperty["startKey"] = passOnStartKey
1250
- };
1251
- messageProperty["numberInvocation"] = numberInvocation;
1252
-
1253
- let messageReInvokeFunction = {
1254
- MessageBody: JSON.stringify(
1255
- messageProperty
1256
- ),
1257
- QueueUrl: await sqsSharedLib.sqsQueueUrl(_izContext, queueName)
1258
- };
1259
- _izContext.logger.debug(`Send mesage to Dsq:${queueName} `, messageReInvokeFunction);
1260
- await sqs.sendMessage(_izContext, messageReInvokeFunction);
1261
-
1262
-
1263
- } catch (err) {
1264
- throw (err)
1265
- }
1266
- }
1267
-
1268
- export default {
1269
- // Helper functions
1270
- createFieldNameUniqueRequestId,
1271
- createConcatenatedAwaitingStepId,
1272
- createConcatenatedPendingStepId,
1273
- createAwaitingStepId,
1274
- createPendingStepId,
1275
- explodePendingStepId,
1276
- createFieldNameCacheComplete,
1277
- createFieldNameStatus,
1278
-
1279
- // AwaitingMultipleSteps functions
1280
- createAwaitingMultipleStepsWithAdditionalAttributes,
1281
- createAwaitingMultipleSteps,
1282
- checkAllAwaitingStepsFinishedWithError,
1283
- checkAllAwaitingStepsFinished,
1284
- clearAllAwaitingSteps,
1285
- removeAwaitingMultipleStep,
1286
- findPendingStepIdsAwaitingMultipleSteps,
1287
- findPendingStepsAwaitingMultipleSteps,
1288
- findPendingStepAwaitingMultipleSteps,
1289
- findAwaitingMultipleStepByPending,
1290
-
1291
- // AwaitingStep functions
1292
- createAwaitingStep,
1293
- findPendingStepIdsAwaitingStep,
1294
- findPendingStepAwaitingStep,
1295
- findPendingStepsAwaitingStep,
1296
- findPendingStepAwaitingStepWithCallingFlow,
1297
- removeAwaitingStep,
1298
- removeAwaitingStepWithCheckUniqueRequestId,
1299
-
1300
- // Unique Request Processing functions
1301
- checkUniqueRequestProcessing,
1302
-
1303
- // Cache functions
1304
- checkTimeCacheComplete,
1305
- checkAndGetTimeCacheComplete,
1306
- checkCacheUniqueRequestId,
1307
-
1308
- // Validation functions
1309
- validateStartKeyParam,
1310
- validateMultipleInvocations
1311
- };