@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.
@@ -0,0 +1,244 @@
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
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
19
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
20
+ import { createFieldNameUniqueRequestId } from './asyncFlowSharedLib.js';
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
26
+
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
+ export async function createAwaitingStep(
38
+ _izContext,
39
+ awaitingStepId,
40
+ pendingStepId,
41
+ additionalAttributes = {}, // save in top level
42
+ callingFlowConfig = {} // optional
43
+ ) {
44
+ _izContext.logger.debug('Lib createAwaitingStep:', {
45
+ awaitingStepId,
46
+ pendingStepId,
47
+ additionalAttributes,
48
+ callingFlowConfig
49
+ });
50
+
51
+ let attributesWhenCreate = {
52
+ awaitingStepId: awaitingStepId,
53
+ pendingStepId: pendingStepId,
54
+ timestamp: Date.now(),
55
+ uniqueRequestId: _izContext.uniqueRequestId
56
+ };
57
+ // loop if not empty
58
+ for (let additionalAttribute in additionalAttributes) {
59
+ attributesWhenCreate[additionalAttribute] =
60
+ additionalAttributes[additionalAttribute]; // each additionalAttributes
61
+ }
62
+ if (Object.keys(callingFlowConfig).length > 0) {
63
+ Object.assign(attributesWhenCreate, { callingFlowConfig });
64
+ }
65
+
66
+ await dynamodbSharedLib.putItem(
67
+ _izContext,
68
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
69
+ attributesWhenCreate
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Find all pendingStepIds that are waiting on a given awaitingStepId (single).
75
+ * @async
76
+ * @param {IzContext} _izContext
77
+ * @param {string} awaitingStepId
78
+ * @returns {Promise<string[]>}
79
+ */
80
+ export async function findPendingStepIdsAwaitingStep(
81
+ _izContext,
82
+ awaitingStepId
83
+ ) {
84
+ let pendingStepIds = [];
85
+
86
+ let listPendingStepIds = await dynamodbSharedLib.query(
87
+ _izContext,
88
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
89
+ {
90
+ awaitingStepId: awaitingStepId
91
+ }
92
+ );
93
+
94
+ for (let idx = 0; idx < listPendingStepIds.Items.length; idx++) {
95
+ pendingStepIds.push(listPendingStepIds.Items[idx].pendingStepId);
96
+ }
97
+
98
+ return pendingStepIds;
99
+ }
100
+
101
+ /**
102
+ * Return the raw items waiting on a given awaitingStepId (single).
103
+ * @async
104
+ * @param {IzContext} _izContext
105
+ * @param {string} awaitingStepId
106
+ * @returns {Promise<AwaitingStepItem[]>}
107
+ */
108
+ export async function findPendingStepsAwaitingStep(_izContext, awaitingStepId) {
109
+ let listPendingSteps = await dynamodbSharedLib.query(
110
+ _izContext,
111
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
112
+ {
113
+ awaitingStepId: awaitingStepId
114
+ }
115
+ );
116
+ _izContext.logger.debug('Record of awaitingStepId', listPendingSteps);
117
+ return listPendingSteps.Items;
118
+ }
119
+
120
+ /**
121
+ * Fetch exactly one pending step for a given awaitingStepId (throws if not 1).
122
+ * @async
123
+ * @param {IzContext} _izContext
124
+ * @param {string} awaitingStepId
125
+ * @returns {Promise<AwaitingStepItem>}
126
+ * @throws {NoRetryError} if there is not exactly one item
127
+ */
128
+ export async function findPendingStepAwaitingStep(_izContext, awaitingStepId) {
129
+ let listPendingSteps = await dynamodbSharedLib.query(
130
+ _izContext,
131
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
132
+ {
133
+ awaitingStepId: awaitingStepId
134
+ }
135
+ );
136
+ _izContext.logger.debug('Record of awaitingStepId', listPendingSteps);
137
+
138
+ if (listPendingSteps.Items.length !== 1) {
139
+ throw new NoRetryError('listPendingSteps not equals 1');
140
+ }
141
+ return listPendingSteps.Items[0];
142
+ }
143
+
144
+ // for get callingFlow save in awaitingStep and remove property callingFlow in awaitingStep
145
+ /**
146
+ * Get one awaiting step by id and also extract (and remove) its callingFlowConfig (if present).
147
+ * Returns a tuple: [AwaitingStepItemWithoutCallingFlow, callingFlowConfig|null].
148
+ * @async
149
+ * @param {IzContext} _izContext
150
+ * @param {string} awaitingStepId
151
+ * @returns {Promise<[AwaitingStepItem, Attrs|null]>}
152
+ */
153
+ export async function findPendingStepAwaitingStepWithCallingFlow(
154
+ _izContext,
155
+ awaitingStepId
156
+ ) {
157
+ let callingFlowConfig = null;
158
+ let awaitingStep = await findPendingStepAwaitingStep(
159
+ _izContext,
160
+ awaitingStepId
161
+ );
162
+
163
+ // callingFlowConfig.
164
+ // cleaning object awaitingStep not have callingFlowConfig after return.
165
+ if (awaitingStep.hasOwnProperty('callingFlowConfig')) {
166
+ callingFlowConfig = awaitingStep.callingFlowConfig;
167
+ delete awaitingStep.callingFlowConfig;
168
+ _izContext.logger.debug('after delete awaitingStep', awaitingStep);
169
+ }
170
+
171
+ return [awaitingStep, callingFlowConfig];
172
+ }
173
+
174
+ /**
175
+ * Remove a single AwaitingStep record (single dependency table).
176
+ * @async
177
+ * @param {IzContext} _izContext
178
+ * @param {string} awaitingStepId
179
+ * @param {string} pendingStepId
180
+ * @returns {Promise<void>}
181
+ */
182
+ export async function removeAwaitingStep(
183
+ _izContext,
184
+ awaitingStepId,
185
+ pendingStepId
186
+ ) {
187
+ await dynamodbSharedLib.deleteItem(
188
+ _izContext,
189
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
190
+ {
191
+ awaitingStepId: awaitingStepId,
192
+ pendingStepId: pendingStepId
193
+ }
194
+ );
195
+ }
196
+
197
+ // 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
198
+ /**
199
+ * Remove an AwaitingStep item only if its stored UniqueRequestId matches `checkUniqueRequestId`.
200
+ * Uses a conditional expression; will not error if the condition fails.
201
+ * @async
202
+ * @param {IzContext} _izContext
203
+ * @param {string} awaitingStepId
204
+ * @param {string} pendingStepId
205
+ * @param {string} checkUniqueRequestId
206
+ * @param {string} [prefix=""] - Optional prefix for UniqueRequestId field name
207
+ * @returns {Promise<void>}
208
+ */
209
+ export async function removeAwaitingStepWithCheckUniqueRequestId(
210
+ _izContext,
211
+ awaitingStepId,
212
+ pendingStepId,
213
+ checkUniqueRequestId,
214
+ prefix = ''
215
+ ) {
216
+ // if logicalElements add queryElements.logicalElements
217
+ // set condition uniqueRequestId match with exists
218
+ const queryElementsCondition = {
219
+ additionalAttributes: {
220
+ checkUniqueRequestId: checkUniqueRequestId
221
+ },
222
+ logicalElements: [
223
+ {
224
+ type: 'logical',
225
+ comparison: 'equals',
226
+ lhsAttribute: createFieldNameUniqueRequestId(prefix),
227
+ rhsReference: 'checkUniqueRequestId'
228
+ }
229
+ ]
230
+ };
231
+
232
+ await dynamodbSharedLib.deleteItem(
233
+ _izContext,
234
+ dynamodbSharedLib.tableName(_izContext, 'AwaitingStep'),
235
+ {
236
+ awaitingStepId: awaitingStepId,
237
+ pendingStepId: pendingStepId
238
+ },
239
+ queryElementsCondition,
240
+ {
241
+ errorOnConditionalExpNotPass: false
242
+ }
243
+ );
244
+ }