@izara_project/izara-core-library-asynchronous-flow 1.0.35 → 1.0.37
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/index.js +8 -87
- package/package.json +1 -1
- package/src/asyncFlowSharedLib.js +51 -100
- package/src/awaitingMultipleSteps.js +253 -713
- package/src/awaitingStep.js +57 -212
package/src/awaitingStep.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright (C) 2021 Sven Mason <http://izara.io>
|
|
1
|
+
/* Copyright (C) 2021 Sven Mason <http://izara.io>
|
|
3
2
|
|
|
4
3
|
This program is free software: you can redistribute it and/or modify
|
|
5
4
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -8,40 +7,54 @@ License, or (at your option) any later version.
|
|
|
8
7
|
|
|
9
8
|
This program is distributed in the hope that it will be useful,
|
|
10
9
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
11
|
GNU Affero General Public License for more details.
|
|
13
12
|
|
|
14
13
|
You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
along with this program.
|
|
14
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
import { NoRetryError } from '@izara_project/izara-core-library-core';
|
|
19
18
|
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
19
|
+
|
|
20
20
|
import { createFieldNameUniqueRequestId } from './asyncFlowSharedLib.js';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
async function _findSinglePendingStep(_izContext, awaitingStepId) {
|
|
23
|
+
_izContext.logger.debug('[Lib:AsyncFlow:findSinglePendingStep] Input', {
|
|
24
|
+
awaitingStepId
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!awaitingStepId) {
|
|
28
|
+
throw new NoRetryError('awaitingStepId is required');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { Items } = await dynamodbSharedLib.query(
|
|
32
|
+
_izContext,
|
|
33
|
+
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
34
|
+
{ awaitingStepId }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (Items.length !== 1) {
|
|
38
|
+
throw new NoRetryError('listPendingSteps not equals 1');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return Items[0];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//======================================
|
|
45
|
+
// AwaitingStep
|
|
46
|
+
//======================================
|
|
47
|
+
// AwaitingStep flow awaiting one external flow, will continue its flow once one external flow completes.
|
|
48
|
+
// One awaitingStepId can have multiple pendingStepIds awaiting it.
|
|
26
49
|
|
|
27
|
-
/**
|
|
28
|
-
* Create a single AwaitingStep record, optionally attaching additional attributes and callingFlowConfig.
|
|
29
|
-
* @async
|
|
30
|
-
* @param {IzContext} _izContext
|
|
31
|
-
* @param {string} awaitingStepId
|
|
32
|
-
* @param {string} pendingStepId
|
|
33
|
-
* @param {Attrs} [additionalAttributes={}]
|
|
34
|
-
* @param {Attrs} [callingFlowConfig={}]
|
|
35
|
-
* @returns {Promise<void>}
|
|
36
|
-
*/
|
|
37
50
|
export async function createAwaitingStep(
|
|
38
51
|
_izContext,
|
|
39
52
|
awaitingStepId,
|
|
40
53
|
pendingStepId,
|
|
41
|
-
additionalAttributes = {},
|
|
42
|
-
callingFlowConfig = {}
|
|
54
|
+
additionalAttributes = {},
|
|
55
|
+
callingFlowConfig = {}
|
|
43
56
|
) {
|
|
44
|
-
_izContext.logger.debug('[Lib:AsyncFlow:createAwaitingStep] Input
|
|
57
|
+
_izContext.logger.debug('[Lib:AsyncFlow:createAwaitingStep] Input', {
|
|
45
58
|
awaitingStepId,
|
|
46
59
|
pendingStepId,
|
|
47
60
|
additionalAttributes,
|
|
@@ -49,19 +62,15 @@ export async function createAwaitingStep(
|
|
|
49
62
|
});
|
|
50
63
|
|
|
51
64
|
const attributesWhenCreate = {
|
|
52
|
-
awaitingStepId
|
|
53
|
-
pendingStepId
|
|
65
|
+
awaitingStepId,
|
|
66
|
+
pendingStepId,
|
|
54
67
|
timestamp: Date.now(),
|
|
55
|
-
uniqueRequestId: _izContext.uniqueRequestId
|
|
68
|
+
uniqueRequestId: _izContext.uniqueRequestId,
|
|
69
|
+
...additionalAttributes
|
|
56
70
|
};
|
|
57
|
-
// loop if not empty
|
|
58
|
-
for (const additionalAttribute in additionalAttributes) {
|
|
59
|
-
attributesWhenCreate[additionalAttribute] =
|
|
60
|
-
additionalAttributes[additionalAttribute]; // each additionalAttributes
|
|
61
|
-
}
|
|
62
71
|
|
|
63
72
|
if (Object.keys(callingFlowConfig).length > 0) {
|
|
64
|
-
|
|
73
|
+
attributesWhenCreate.callingFlowConfig = callingFlowConfig;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
await dynamodbSharedLib.putItem(
|
|
@@ -71,199 +80,49 @@ export async function createAwaitingStep(
|
|
|
71
80
|
);
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
* Find all pendingStepIds that are waiting on a given awaitingStepId (single).
|
|
76
|
-
* @async
|
|
77
|
-
* @param {IzContext} _izContext
|
|
78
|
-
* @param {string} awaitingStepId
|
|
79
|
-
* @returns {Promise<string[]>}
|
|
80
|
-
*/
|
|
81
|
-
export async function findPendingStepIdsAwaitingStep(
|
|
83
|
+
export async function findPendingStepIdAwaitingStep(
|
|
82
84
|
_izContext,
|
|
83
85
|
awaitingStepId
|
|
84
86
|
) {
|
|
85
|
-
_izContext
|
|
86
|
-
|
|
87
|
-
{
|
|
88
|
-
awaitingStepId
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
if (!awaitingStepId) {
|
|
93
|
-
throw NoRetryError('awaitingStepId is required');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const pendingStepIds = [];
|
|
97
|
-
|
|
98
|
-
const listPendingStepIds = await dynamodbSharedLib.query(
|
|
99
|
-
_izContext,
|
|
100
|
-
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
101
|
-
{
|
|
102
|
-
awaitingStepId: awaitingStepId
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
|
|
107
|
-
pendingStepIds.push(listPendingStepIds.Items[idx].pendingStepId);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return pendingStepIds;
|
|
87
|
+
const item = await _findSinglePendingStep(_izContext, awaitingStepId);
|
|
88
|
+
return item.pendingStepId;
|
|
111
89
|
}
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* @async
|
|
116
|
-
* @param {IzContext} _izContext
|
|
117
|
-
* @param {string} awaitingStepId
|
|
118
|
-
* @returns {Promise<AwaitingStepItem[]>}
|
|
119
|
-
*/
|
|
120
|
-
export async function findPendingStepsAwaitingStep(_izContext, awaitingStepId) {
|
|
121
|
-
_izContext.logger.debug(
|
|
122
|
-
'[Lib:AsyncFlow:findPendingStepsAwaitingStep] Input: ',
|
|
123
|
-
{
|
|
124
|
-
awaitingStepId
|
|
125
|
-
}
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
if (!awaitingStepId) {
|
|
129
|
-
throw NoRetryError('awaitingStepId is required');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const listPendingSteps = await dynamodbSharedLib.query(
|
|
133
|
-
_izContext,
|
|
134
|
-
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
135
|
-
{
|
|
136
|
-
awaitingStepId: awaitingStepId
|
|
137
|
-
}
|
|
138
|
-
);
|
|
139
|
-
_izContext.logger.debug('Record of awaitingStepId', listPendingSteps);
|
|
140
|
-
return listPendingSteps.Items;
|
|
91
|
+
export async function findPendingStep(_izContext, awaitingStepId) {
|
|
92
|
+
return _findSinglePendingStep(_izContext, awaitingStepId);
|
|
141
93
|
}
|
|
142
94
|
|
|
143
|
-
/**
|
|
144
|
-
* Fetch exactly one pending step for a given awaitingStepId (throws if not 1).
|
|
145
|
-
* @async
|
|
146
|
-
* @param {IzContext} _izContext
|
|
147
|
-
* @param {string} awaitingStepId
|
|
148
|
-
* @returns {Promise<AwaitingStepItem>}
|
|
149
|
-
* @throws {NoRetryError} if there is not exactly one item
|
|
150
|
-
*/
|
|
151
|
-
export async function findPendingStepAwaitingStep(_izContext, awaitingStepId) {
|
|
152
|
-
_izContext.logger.debug(
|
|
153
|
-
'[Lib:AsyncFlow:findPendingStepAwaitingStep] Input: ',
|
|
154
|
-
{
|
|
155
|
-
awaitingStepId
|
|
156
|
-
}
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
if (!awaitingStepId) {
|
|
160
|
-
throw NoRetryError('awaitingStepId is required');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const listPendingSteps = await dynamodbSharedLib.query(
|
|
164
|
-
_izContext,
|
|
165
|
-
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
166
|
-
{
|
|
167
|
-
awaitingStepId: awaitingStepId
|
|
168
|
-
}
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
_izContext.logger.debug(
|
|
172
|
-
'[Lib:AsyncFlow:findPendingStepAwaitingStep] Found records: ',
|
|
173
|
-
listPendingSteps
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
if (listPendingSteps.Items.length !== 1) {
|
|
177
|
-
throw NoRetryError('listPendingSteps not equals 1');
|
|
178
|
-
}
|
|
179
|
-
return listPendingSteps.Items[0];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// for get callingFlow save in awaitingStep and remove property callingFlow in awaitingStep
|
|
183
|
-
/**
|
|
184
|
-
* Get one awaiting step by id and also extract (and remove) its callingFlowConfig (if present).
|
|
185
|
-
* Returns a tuple: [AwaitingStepItemWithoutCallingFlow, callingFlowConfig|null].
|
|
186
|
-
* @async
|
|
187
|
-
* @param {IzContext} _izContext
|
|
188
|
-
* @param {string} awaitingStepId
|
|
189
|
-
* @returns {Promise<[AwaitingStepItem, Attrs|null]>}
|
|
190
|
-
*/
|
|
191
95
|
export async function findPendingStepAwaitingStepWithCallingFlow(
|
|
192
96
|
_izContext,
|
|
193
97
|
awaitingStepId
|
|
194
98
|
) {
|
|
195
|
-
_izContext
|
|
196
|
-
|
|
197
|
-
{
|
|
198
|
-
awaitingStepId
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
if (!awaitingStepId) {
|
|
203
|
-
throw NoRetryError('awaitingStepId is required');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let callingFlowConfig = null;
|
|
207
|
-
let awaitingStep = await findPendingStepAwaitingStep(
|
|
208
|
-
_izContext,
|
|
209
|
-
awaitingStepId
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
// callingFlowConfig.
|
|
213
|
-
// cleaning object awaitingStep not have callingFlowConfig after return.
|
|
214
|
-
if (awaitingStep.hasOwnProperty('callingFlowConfig')) {
|
|
215
|
-
callingFlowConfig = awaitingStep.callingFlowConfig;
|
|
216
|
-
delete awaitingStep.callingFlowConfig;
|
|
217
|
-
_izContext.logger.debug('after delete awaitingStep', awaitingStep);
|
|
218
|
-
}
|
|
99
|
+
const awaitingStep = await findPendingStep(_izContext, awaitingStepId);
|
|
100
|
+
const { callingFlowConfig = null, ...restAwaitingStep } = awaitingStep;
|
|
219
101
|
|
|
220
|
-
return [
|
|
102
|
+
return [restAwaitingStep, callingFlowConfig];
|
|
221
103
|
}
|
|
222
104
|
|
|
223
|
-
/**
|
|
224
|
-
* Remove a single AwaitingStep record (single dependency table).
|
|
225
|
-
* @async
|
|
226
|
-
* @param {IzContext} _izContext
|
|
227
|
-
* @param {string} awaitingStepId
|
|
228
|
-
* @param {string} pendingStepId
|
|
229
|
-
* @returns {Promise<void>}
|
|
230
|
-
*/
|
|
231
105
|
export async function removeAwaitingStep(
|
|
232
106
|
_izContext,
|
|
233
107
|
awaitingStepId,
|
|
234
108
|
pendingStepId
|
|
235
109
|
) {
|
|
236
|
-
_izContext.logger.debug('[Lib:AsyncFlow:removeAwaitingStep] Input
|
|
110
|
+
_izContext.logger.debug('[Lib:AsyncFlow:removeAwaitingStep] Input', {
|
|
237
111
|
awaitingStepId,
|
|
238
112
|
pendingStepId
|
|
239
113
|
});
|
|
240
114
|
|
|
241
115
|
if (!awaitingStepId || !pendingStepId) {
|
|
242
|
-
throw NoRetryError('awaitingStepId
|
|
116
|
+
throw new NoRetryError('awaitingStepId pendingStepId required');
|
|
243
117
|
}
|
|
244
118
|
|
|
245
119
|
await dynamodbSharedLib.deleteItem(
|
|
246
120
|
_izContext,
|
|
247
121
|
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
248
|
-
{
|
|
249
|
-
awaitingStepId: awaitingStepId,
|
|
250
|
-
pendingStepId: pendingStepId
|
|
251
|
-
}
|
|
122
|
+
{ awaitingStepId, pendingStepId }
|
|
252
123
|
);
|
|
253
124
|
}
|
|
254
125
|
|
|
255
|
-
// 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
|
|
256
|
-
/**
|
|
257
|
-
* Remove an AwaitingStep item only if its stored UniqueRequestId matches `checkUniqueRequestId`.
|
|
258
|
-
* Uses a conditional expression; will not error if the condition fails.
|
|
259
|
-
* @async
|
|
260
|
-
* @param {IzContext} _izContext
|
|
261
|
-
* @param {string} awaitingStepId
|
|
262
|
-
* @param {string} pendingStepId
|
|
263
|
-
* @param {string} checkUniqueRequestId
|
|
264
|
-
* @param {string} [prefix=""] - Optional prefix for UniqueRequestId field name
|
|
265
|
-
* @returns {Promise<void>}
|
|
266
|
-
*/
|
|
267
126
|
export async function removeAwaitingStepWithCheckUniqueRequestId(
|
|
268
127
|
_izContext,
|
|
269
128
|
awaitingStepId,
|
|
@@ -272,27 +131,18 @@ export async function removeAwaitingStepWithCheckUniqueRequestId(
|
|
|
272
131
|
prefix = ''
|
|
273
132
|
) {
|
|
274
133
|
_izContext.logger.debug(
|
|
275
|
-
'[Lib:AsyncFlow:removeAwaitingStepWithCheckUniqueRequestId] Input
|
|
276
|
-
{
|
|
277
|
-
awaitingStepId,
|
|
278
|
-
pendingStepId,
|
|
279
|
-
checkUniqueRequestId,
|
|
280
|
-
prefix
|
|
281
|
-
}
|
|
134
|
+
'[Lib:AsyncFlow:removeAwaitingStepWithCheckUniqueRequestId] Input',
|
|
135
|
+
{ awaitingStepId, pendingStepId, checkUniqueRequestId, prefix }
|
|
282
136
|
);
|
|
283
137
|
|
|
284
138
|
if (!awaitingStepId || !pendingStepId || !checkUniqueRequestId) {
|
|
285
|
-
throw NoRetryError(
|
|
286
|
-
'awaitingStepId, pendingStepId
|
|
139
|
+
throw new NoRetryError(
|
|
140
|
+
'awaitingStepId, pendingStepId checkUniqueRequestId required'
|
|
287
141
|
);
|
|
288
142
|
}
|
|
289
143
|
|
|
290
|
-
// if logicalElements add queryElements.logicalElements
|
|
291
|
-
// set condition uniqueRequestId match with exists
|
|
292
144
|
const queryElementsCondition = {
|
|
293
|
-
additionalAttributes: {
|
|
294
|
-
checkUniqueRequestId: checkUniqueRequestId
|
|
295
|
-
},
|
|
145
|
+
additionalAttributes: { checkUniqueRequestId },
|
|
296
146
|
logicalElements: [
|
|
297
147
|
{
|
|
298
148
|
type: 'logical',
|
|
@@ -306,13 +156,8 @@ export async function removeAwaitingStepWithCheckUniqueRequestId(
|
|
|
306
156
|
await dynamodbSharedLib.deleteItem(
|
|
307
157
|
_izContext,
|
|
308
158
|
dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
|
|
309
|
-
{
|
|
310
|
-
awaitingStepId: awaitingStepId,
|
|
311
|
-
pendingStepId: pendingStepId
|
|
312
|
-
},
|
|
159
|
+
{ awaitingStepId, pendingStepId },
|
|
313
160
|
queryElementsCondition,
|
|
314
|
-
{
|
|
315
|
-
errorOnConditionalExpNotPass: false
|
|
316
|
-
}
|
|
161
|
+
{ errorOnConditionalExpNotPass: false }
|
|
317
162
|
);
|
|
318
163
|
}
|