@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.
@@ -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. See the
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. If not, see <http://www.gnu.org/licenses/>.
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
- //====================================== AwaitingStep //======================================
23
- // AwaitingStep is when a flow is awaiting one external flow, and will continue it's flow once that one external flow is complete
24
- // One awaitingStepId can have multiple pendingStepIds that are awaiting it
25
- // logic can prepend any prefix to awaitingStepId if want to be share one table and differentiate different external step ids
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 = {}, // save in top level
42
- callingFlowConfig = {} // optional
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: awaitingStepId,
53
- pendingStepId: 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
- Object.assign(attributesWhenCreate, { callingFlowConfig });
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.logger.debug(
86
- '[Lib:AsyncFlow:findPendingStepIdsAwaitingStep] Input: ',
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
- * Return the raw items waiting on a given awaitingStepId (single).
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.logger.debug(
196
- '[Lib:AsyncFlow:findPendingStepAwaitingStepWithCallingFlow] Input: ',
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 [awaitingStep, callingFlowConfig];
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 and pendingStepId are required');
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 and checkUniqueRequestId are required'
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
  }