@izara_project/izara-core-generate-service-code 1.0.87 → 1.0.89
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/compile/flowValidator.js +25 -0
- package/src/compile/loadSchemas.js +199 -10
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
- package/src/emit/crud/crudActionPlan.js +105 -6
- package/src/emit/crud/emitCrudApplicationArtifacts.js +63 -3
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
- package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +121 -0
- package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +108 -0
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +140 -340
- package/src/emit/crud/templates/CreateLogicalLib.ejs +338 -82
- package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +90 -0
- package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
- package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +99 -0
- package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +151 -179
- package/src/emit/crud/templates/DeleteLogicalLib.ejs +244 -27
- package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
- package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +112 -0
- package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +166 -185
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +123 -3
- package/src/emit/custom/customFlows.js +3 -1
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
- package/src/emit/resource/emitResourceArtifacts.js +119 -59
- package/test/crudApplicationArtifacts.test.js +876 -0
- package/test/dynamicCrudFlowSchemas.test.js +10 -5
- package/test/dynamicRbacFlowSchemas.test.js +24 -2
- package/test/graphDrivenFlowSchema.test.js +36 -0
- package/test/resourceArtifacts.test.js +271 -12
|
@@ -46,6 +46,135 @@ const { TOPIC_NAME_GENERATE_CODE } = consts;
|
|
|
46
46
|
//(<optionalRequire>)
|
|
47
47
|
//(</optionalRequire>)
|
|
48
48
|
|
|
49
|
+
// ── 1. Delete_Main Helpers ───────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Loads ObjectSchema and resolves submittedByUserId and targetIdentifiers
|
|
53
|
+
* @param {Object} _izContext - Izara context object
|
|
54
|
+
* @param {Object} requestParams - Incoming request parameters
|
|
55
|
+
* @param {Array<string>} errorsFound - Array to collect validation errors
|
|
56
|
+
* @returns {Promise<{ objType: string, objectSchema: Object, targetIdentifiers?: Object }>}
|
|
57
|
+
*/
|
|
58
|
+
export async function resolveSchemaAndIdentity(
|
|
59
|
+
_izContext,
|
|
60
|
+
requestParams,
|
|
61
|
+
errorsFound
|
|
62
|
+
) {
|
|
63
|
+
const objType = createObjType(requestParams.objectType);
|
|
64
|
+
const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
65
|
+
_izContext,
|
|
66
|
+
objType
|
|
67
|
+
);
|
|
68
|
+
_izContext.logger.debug(
|
|
69
|
+
`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchema:`,
|
|
70
|
+
objectSchema
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
let submittedByUserId;
|
|
74
|
+
let targetIdentifiers;
|
|
75
|
+
if (
|
|
76
|
+
objectSchema.generatedBy === 'user' ||
|
|
77
|
+
objectSchema.generatedBy === 'systemWithUser'
|
|
78
|
+
) {
|
|
79
|
+
submittedByUserId =
|
|
80
|
+
requestParams.submittedByUserId ||
|
|
81
|
+
_izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
|
|
82
|
+
|
|
83
|
+
targetIdentifiers = requestParams.targetIdentifiers;
|
|
84
|
+
delete requestParams.targetIdentifiers;
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
!targetIdentifiers &&
|
|
88
|
+
objectSchema.belongTo?.objectType === 'user' &&
|
|
89
|
+
submittedByUserId
|
|
90
|
+
) {
|
|
91
|
+
targetIdentifiers = { submittedByUserId };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!submittedByUserId) {
|
|
95
|
+
errorsFound.push('Not have submittedByUserId');
|
|
96
|
+
}
|
|
97
|
+
if (objectSchema.hasOwnProperty('belongTo') && !targetIdentifiers) {
|
|
98
|
+
errorsFound.push('not have targetIdentifiers');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
requestParams.submittedByUserId = submittedByUserId;
|
|
102
|
+
if (targetIdentifiers) {
|
|
103
|
+
if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
|
|
104
|
+
targetIdentifiers.submittedByUserId = submittedByUserId;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_izContext.logger.debug(
|
|
109
|
+
`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`,
|
|
110
|
+
{
|
|
111
|
+
submittedByUserId: requestParams.submittedByUserId,
|
|
112
|
+
targetIdentifiers,
|
|
113
|
+
belongTo: objectSchema.belongTo,
|
|
114
|
+
generatedBy: objectSchema.generatedBy
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { objType, objectSchema, targetIdentifiers };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Handles delete errors and publishes error response
|
|
124
|
+
* @param {Object} _izContext - Izara context object
|
|
125
|
+
* @param {Object} options - Error context details
|
|
126
|
+
* @returns {Promise<Object>} Error status result
|
|
127
|
+
*/
|
|
128
|
+
export async function handleDeleteError(
|
|
129
|
+
_izContext,
|
|
130
|
+
{
|
|
131
|
+
objType,
|
|
132
|
+
identifiers,
|
|
133
|
+
targetIdentifiers,
|
|
134
|
+
callingFlowConfig,
|
|
135
|
+
errorsFound
|
|
136
|
+
}
|
|
137
|
+
) {
|
|
138
|
+
const deleteNodeCompleteMessage =
|
|
139
|
+
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
140
|
+
callingFlowConfig,
|
|
141
|
+
{
|
|
142
|
+
objType,
|
|
143
|
+
identifiers,
|
|
144
|
+
targetIdentifiers,
|
|
145
|
+
status: 'error',
|
|
146
|
+
errorsFound
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
_izContext.logger.debug(
|
|
151
|
+
'messageParams OutDeleteNodeComplete::',
|
|
152
|
+
deleteNodeCompleteMessage
|
|
153
|
+
);
|
|
154
|
+
await sns.publishAsync(_izContext, {
|
|
155
|
+
Message: JSON.stringify(deleteNodeCompleteMessage),
|
|
156
|
+
MessageAttributes: sns.createStringMessageAttributes(
|
|
157
|
+
_izContext,
|
|
158
|
+
callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
|
|
159
|
+
callingFlowConfig,
|
|
160
|
+
{}
|
|
161
|
+
)
|
|
162
|
+
),
|
|
163
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
164
|
+
_izContext,
|
|
165
|
+
TOPIC_NAME_GENERATE_CODE.outDeleteNodeComplete
|
|
166
|
+
)
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
objType,
|
|
171
|
+
identifiers,
|
|
172
|
+
targetIdentifiers,
|
|
173
|
+
status: 'error',
|
|
174
|
+
errorsFound
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
49
178
|
/**
|
|
50
179
|
* Core logical processing library
|
|
51
180
|
* @param {Object} _izContext - Context object with logger
|
|
@@ -93,36 +222,13 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
93
222
|
|
|
94
223
|
// ── Early exit on errors ──────────────────────────────────────────────────
|
|
95
224
|
if (errorsFound.length) {
|
|
96
|
-
|
|
97
|
-
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
98
|
-
callingFlowConfig,
|
|
99
|
-
{
|
|
100
|
-
objType,
|
|
101
|
-
identifiers: requestParams.identifiers,
|
|
102
|
-
status: 'error',
|
|
103
|
-
errorsFound
|
|
104
|
-
}
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
_izContext.logger.debug(
|
|
108
|
-
'messageParams OutDeleteNodeComplete::',
|
|
109
|
-
deleteNodeCompleteMessage
|
|
110
|
-
);
|
|
111
|
-
await sns.publishAsync(_izContext, {
|
|
112
|
-
Message: JSON.stringify(deleteNodeCompleteMessage),
|
|
113
|
-
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
114
|
-
_izContext,
|
|
115
|
-
TOPIC_NAME_GENERATE_CODE.outDeleteNodeComplete
|
|
116
|
-
)
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return {
|
|
225
|
+
return await handleDeleteError(_izContext, {
|
|
120
226
|
objType,
|
|
121
227
|
identifiers: requestParams.identifiers,
|
|
122
228
|
targetIdentifiers: requestParams.targetIdentifiers,
|
|
123
|
-
|
|
229
|
+
callingFlowConfig,
|
|
124
230
|
errorsFound
|
|
125
|
-
};
|
|
231
|
+
});
|
|
126
232
|
}
|
|
127
233
|
|
|
128
234
|
//(<validateBeforeDelete>)
|
|
@@ -168,6 +274,111 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
168
274
|
}
|
|
169
275
|
|
|
170
276
|
// ── Complete ──────────────────────────────────────────────────────────────
|
|
277
|
+
<%_ if (plan?.hasAfterHookSync) { _%>
|
|
278
|
+
// ── Synchronous After Hooks ──
|
|
279
|
+
<%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
|
|
280
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
|
|
281
|
+
if (objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
|
|
282
|
+
<%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
|
|
283
|
+
if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType?.objectType)) {
|
|
284
|
+
<%_ } _%>
|
|
285
|
+
try {
|
|
286
|
+
let hookPayload_<%= hook.flowTag %> = {
|
|
287
|
+
objType,
|
|
288
|
+
identifiers: requestParams.identifiers,
|
|
289
|
+
relationships: requestParams.relationships || [],
|
|
290
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
291
|
+
submittedByUserId: requestParams.submittedByUserId
|
|
292
|
+
};
|
|
293
|
+
//(<beforeSendHook_<%= hook.flowTag %>>)
|
|
294
|
+
//(</beforeSendHook_<%= hook.flowTag %>>)
|
|
295
|
+
const hookResult = await lambda.invokeSync(
|
|
296
|
+
_izContext,
|
|
297
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
298
|
+
_izContext,
|
|
299
|
+
'<%= hook.flowTag %>'
|
|
300
|
+
),
|
|
301
|
+
{
|
|
302
|
+
requestParams: hookPayload_<%= hook.flowTag %>,
|
|
303
|
+
callingFlowConfig
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
if (
|
|
307
|
+
hookResult?.errorsFound?.length > 0 ||
|
|
308
|
+
hookResult?.status === 'error'
|
|
309
|
+
) {
|
|
310
|
+
errorsFound.push(
|
|
311
|
+
...(hookResult.errorsFound || ['Hook <%= hook.flowTag %> failed'])
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
} catch (hookErr) {
|
|
315
|
+
_izContext.logger.error(
|
|
316
|
+
'[<%- flowTag %>LogicalLib] Sync hook <%= hook.flowTag %> error:',
|
|
317
|
+
hookErr
|
|
318
|
+
);
|
|
319
|
+
errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
|
|
320
|
+
}
|
|
321
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
|
|
322
|
+
}
|
|
323
|
+
<%_ } _%>
|
|
324
|
+
<%_ }) _%>
|
|
325
|
+
<%_ } _%>
|
|
326
|
+
|
|
327
|
+
<%_ if (plan?.hasAfterHookAsync) { _%>
|
|
328
|
+
// ── Asynchronous After Hooks ──
|
|
329
|
+
<%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
|
|
330
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
|
|
331
|
+
if (objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
|
|
332
|
+
<%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
|
|
333
|
+
if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType?.objectType)) {
|
|
334
|
+
<%_ } _%>
|
|
335
|
+
let hookPayload_<%= hook.flowTag %> = {
|
|
336
|
+
objType,
|
|
337
|
+
identifiers: requestParams.identifiers,
|
|
338
|
+
relationships: requestParams.relationships || [],
|
|
339
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
340
|
+
submittedByUserId: requestParams.submittedByUserId
|
|
341
|
+
};
|
|
342
|
+
//(<beforeSendHook_<%= hook.flowTag %>>)
|
|
343
|
+
//(</beforeSendHook_<%= hook.flowTag %>>)
|
|
344
|
+
const hookMsg_<%= hook.flowTag %> =
|
|
345
|
+
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
346
|
+
hookPayload_<%= hook.flowTag %>,
|
|
347
|
+
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
348
|
+
callingFlowConfig,
|
|
349
|
+
callingFlowSharedLib.createCallingFlowConfig(
|
|
350
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
351
|
+
_izContext,
|
|
352
|
+
'DeleteAfterHookCallbackHdrSqs'
|
|
353
|
+
),
|
|
354
|
+
{
|
|
355
|
+
actionData: {
|
|
356
|
+
objType,
|
|
357
|
+
identifiers: requestParams.identifiers,
|
|
358
|
+
relationships: requestParams.relationships || [],
|
|
359
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
360
|
+
parentFlowId: requestParams.parentFlowId,
|
|
361
|
+
status: 'complete',
|
|
362
|
+
errorsFound
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
await sns.publishAsync(_izContext, {
|
|
370
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
371
|
+
_izContext,
|
|
372
|
+
'<%= hook.flowTag %>_In'
|
|
373
|
+
),
|
|
374
|
+
Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
|
|
375
|
+
MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
|
|
376
|
+
});
|
|
377
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
|
|
378
|
+
}
|
|
379
|
+
<%_ } _%>
|
|
380
|
+
<%_ }) _%>
|
|
381
|
+
<%_ } else { _%>
|
|
171
382
|
const outMessage =
|
|
172
383
|
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
173
384
|
callingFlowConfig,
|
|
@@ -198,12 +409,18 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
198
409
|
)
|
|
199
410
|
});
|
|
200
411
|
}
|
|
412
|
+
<%_ } _%>
|
|
413
|
+
|
|
414
|
+
let resultStatus = 'complete';
|
|
415
|
+
if (errorsFound.length > 0) {
|
|
416
|
+
resultStatus = 'error';
|
|
417
|
+
}
|
|
201
418
|
|
|
202
419
|
return {
|
|
203
420
|
objType,
|
|
204
421
|
identifiers: requestParams.identifiers,
|
|
205
422
|
targetIdentifiers: requestParams.targetIdentifiers,
|
|
206
|
-
status:
|
|
423
|
+
status: resultStatus,
|
|
207
424
|
errorsFound
|
|
208
425
|
};
|
|
209
426
|
} catch (err) {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2026 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 asyncFlowSharedLib from '@izara_project/izara-core-library-asynchronous-flow';
|
|
19
|
+
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
20
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
21
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
22
|
+
import { consts } from '@izara_project/izara-core-library-service-schemas';
|
|
23
|
+
|
|
24
|
+
//(<optionalRequire>)
|
|
25
|
+
//(</optionalRequire>)
|
|
26
|
+
|
|
27
|
+
const { TOPIC_NAME_GENERATE_CODE } = consts;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handles callback after async AfterHook completion for Update
|
|
31
|
+
* @param {Object} _izContext - Izara context object
|
|
32
|
+
* @param {Object} requestParams - Callback request parameters
|
|
33
|
+
* @param {Object} passBackProperties - Pass-back properties
|
|
34
|
+
* @returns {Promise<Object>} Result object
|
|
35
|
+
*/
|
|
36
|
+
export default async function UpdateAfterHookCallbackMain(
|
|
37
|
+
_izContext,
|
|
38
|
+
requestParams,
|
|
39
|
+
passBackProperties
|
|
40
|
+
//(<additionalParams>)
|
|
41
|
+
//(</additionalParams>)
|
|
42
|
+
) {
|
|
43
|
+
try {
|
|
44
|
+
_izContext.logger.debug('[UpdateAfterHookCallback] started', {
|
|
45
|
+
requestParams,
|
|
46
|
+
passBackProperties
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
status,
|
|
51
|
+
errorsFound = [],
|
|
52
|
+
returnValue,
|
|
53
|
+
parentFlowId
|
|
54
|
+
} = requestParams;
|
|
55
|
+
|
|
56
|
+
const mergedErrors = Array.isArray(errorsFound) ? [...errorsFound] : [];
|
|
57
|
+
if (status === 'error' && mergedErrors.length === 0) {
|
|
58
|
+
mergedErrors.push('AfterHook execution reported error');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const callingFlowConfig =
|
|
62
|
+
passBackProperties?.callingFlowConfig ||
|
|
63
|
+
requestParams?.passBackProperties?.callingFlowConfig ||
|
|
64
|
+
passBackProperties;
|
|
65
|
+
|
|
66
|
+
const actionData =
|
|
67
|
+
passBackProperties?.actionData ||
|
|
68
|
+
requestParams?.passBackProperties?.actionData ||
|
|
69
|
+
{
|
|
70
|
+
objType: returnValue?.objType || requestParams?.objType,
|
|
71
|
+
objInstanceFull:
|
|
72
|
+
returnValue?.objInstanceFull || requestParams?.objInstanceFull,
|
|
73
|
+
relationships:
|
|
74
|
+
returnValue?.relationships || requestParams?.relationships || [],
|
|
75
|
+
targetIdentifiers: requestParams?.targetIdentifiers,
|
|
76
|
+
parentFlowId: parentFlowId || returnValue?.parentFlowId,
|
|
77
|
+
status: mergedErrors.length > 0 ? 'error' : 'complete',
|
|
78
|
+
errorsFound: mergedErrors
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
|
|
82
|
+
_izContext,
|
|
83
|
+
TOPIC_NAME_GENERATE_CODE.outUpdateNodeComplete
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const completionPayload =
|
|
87
|
+
callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
|
|
88
|
+
callingFlowConfig,
|
|
89
|
+
{
|
|
90
|
+
...actionData,
|
|
91
|
+
status: mergedErrors.length > 0 ? 'error' : 'complete',
|
|
92
|
+
errorsFound: mergedErrors
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await sns.publishAsync(_izContext, {
|
|
97
|
+
TopicArn: topicArn,
|
|
98
|
+
Message: JSON.stringify(completionPayload),
|
|
99
|
+
MessageAttributes: sns.createStringMessageAttributes(
|
|
100
|
+
_izContext,
|
|
101
|
+
callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
|
|
102
|
+
callingFlowConfig,
|
|
103
|
+
{}
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
//(<afterProcessCallback>)
|
|
109
|
+
//(</afterProcessCallback>)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
returnValue,
|
|
113
|
+
status: mergedErrors.length > 0 ? 'error' : 'complete',
|
|
114
|
+
errorsFound: mergedErrors
|
|
115
|
+
};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
_izContext.logger.error('[UpdateAfterHookCallback] error:', error);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2026 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 {
|
|
19
|
+
<%- flowTag %>LogicalLib,
|
|
20
|
+
handleUpdateError
|
|
21
|
+
} from './<%- upperCaseFlowTag %>LogicalLib.js';
|
|
22
|
+
|
|
23
|
+
//(<optionalRequire>)
|
|
24
|
+
//(</optionalRequire>)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Handles callback after async BeforeHook completion
|
|
28
|
+
* @param {Object} _izContext - Izara context object
|
|
29
|
+
* @param {Object} requestParams - Callback request parameters
|
|
30
|
+
* @param {Object} passBackProperties - Pass-back properties
|
|
31
|
+
* @returns {Promise<Object>} Result object
|
|
32
|
+
*/
|
|
33
|
+
export default async function <%- upperCaseFlowTag %>BeforeHookCallbackMain(
|
|
34
|
+
_izContext,
|
|
35
|
+
requestParams,
|
|
36
|
+
passBackProperties
|
|
37
|
+
//(<additionalParams>)
|
|
38
|
+
//(</additionalParams>)
|
|
39
|
+
) {
|
|
40
|
+
try {
|
|
41
|
+
_izContext.logger.debug('[<%- upperCaseFlowTag %>BeforeHookCallback] started', {
|
|
42
|
+
requestParams,
|
|
43
|
+
passBackProperties
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
status,
|
|
48
|
+
errorsFound = [],
|
|
49
|
+
returnValue,
|
|
50
|
+
parentFlowId
|
|
51
|
+
} = requestParams;
|
|
52
|
+
|
|
53
|
+
const mergedErrors = Array.isArray(errorsFound) ? [...errorsFound] : [];
|
|
54
|
+
if (status === 'error' && mergedErrors.length === 0) {
|
|
55
|
+
mergedErrors.push('BeforeHook execution reported error');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const callingFlowConfig =
|
|
59
|
+
passBackProperties?.callingFlowConfig ||
|
|
60
|
+
requestParams?.passBackProperties?.callingFlowConfig ||
|
|
61
|
+
{};
|
|
62
|
+
|
|
63
|
+
const originalRequestParams =
|
|
64
|
+
passBackProperties?.originalRequestParams ||
|
|
65
|
+
requestParams?.passBackProperties?.originalRequestParams ||
|
|
66
|
+
passBackProperties?.requestParams ||
|
|
67
|
+
passBackProperties;
|
|
68
|
+
|
|
69
|
+
if (mergedErrors.length > 0) {
|
|
70
|
+
_izContext.logger.warn(
|
|
71
|
+
'[<%- upperCaseFlowTag %>BeforeHookCallback] BeforeHook reported errors:',
|
|
72
|
+
mergedErrors
|
|
73
|
+
);
|
|
74
|
+
return await handleUpdateError(_izContext, {
|
|
75
|
+
objType: originalRequestParams?.objType,
|
|
76
|
+
identifiers:
|
|
77
|
+
originalRequestParams?.objInstanceFull?.identifiers ||
|
|
78
|
+
originalRequestParams?.identifiers ||
|
|
79
|
+
{},
|
|
80
|
+
fields:
|
|
81
|
+
originalRequestParams?.objInstanceFull?.fields ||
|
|
82
|
+
{},
|
|
83
|
+
versionedDataIds: originalRequestParams?.versionedDataIds,
|
|
84
|
+
callingFlowConfig,
|
|
85
|
+
errorsFound: mergedErrors
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (returnValue?.fields && originalRequestParams?.objInstanceFull?.fields) {
|
|
90
|
+
Object.assign(originalRequestParams.objInstanceFull.fields, returnValue.fields);
|
|
91
|
+
} else if (returnValue?.fieldNames && originalRequestParams?.objInstanceFull?.fields) {
|
|
92
|
+
Object.assign(originalRequestParams.objInstanceFull.fields, returnValue.fieldNames);
|
|
93
|
+
} else if (returnValue?.fieldNames && originalRequestParams?.fieldNames) {
|
|
94
|
+
Object.assign(originalRequestParams.fieldNames, returnValue.fieldNames);
|
|
95
|
+
}
|
|
96
|
+
if (returnValue?.relationships && originalRequestParams) {
|
|
97
|
+
originalRequestParams.relationships = returnValue.relationships;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//(<beforeLogicalExecution>)
|
|
101
|
+
//(</beforeLogicalExecution>)
|
|
102
|
+
|
|
103
|
+
return await <%- flowTag %>LogicalLib(
|
|
104
|
+
_izContext,
|
|
105
|
+
originalRequestParams,
|
|
106
|
+
callingFlowConfig
|
|
107
|
+
);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
_izContext.logger.error('[<%- upperCaseFlowTag %>BeforeHookCallback] error:', error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import asyncFlowSharedLib from '@izara_project/izara-core-library-asynchronous-flow';
|
|
2
2
|
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
3
3
|
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
4
|
+
import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
4
5
|
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
sns,
|
|
8
|
+
lambda
|
|
9
|
+
} from '@izara_project/izara-core-library-external-request';
|
|
6
10
|
import { consts } from '@izara_project/izara-core-library-service-schemas';
|
|
7
11
|
import { NoRetryError } from '@izara_project/izara-core-library-core';
|
|
8
12
|
|
|
@@ -142,18 +146,107 @@ export default async function UpdateComplete(
|
|
|
142
146
|
const callingFlowConfig =
|
|
143
147
|
result?.additionalAttributes?.callingFlowConfig ||
|
|
144
148
|
passBackProperties;
|
|
149
|
+
|
|
150
|
+
const actionData = {
|
|
151
|
+
objType: returnValue.requestParams.objType,
|
|
152
|
+
objInstanceFull: node.objInstanceFull,
|
|
153
|
+
relationships: node.relationships,
|
|
154
|
+
parentFlowId: parentFlowId || returnValue?.parentFlowId,
|
|
155
|
+
existsNode,
|
|
156
|
+
status,
|
|
157
|
+
errorsFound
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
<%_ if (plan?.hasAfterHookSync) { _%>
|
|
161
|
+
// ── Synchronous After Hooks ──
|
|
162
|
+
<%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
|
|
163
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
|
|
164
|
+
if (returnValue.requestParams?.objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
|
|
165
|
+
<%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
|
|
166
|
+
if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(returnValue.requestParams?.objType?.objectType)) {
|
|
167
|
+
<%_ } _%>
|
|
168
|
+
try {
|
|
169
|
+
let hookPayload_<%= hook.flowTag %> = {
|
|
170
|
+
objType: returnValue.requestParams.objType,
|
|
171
|
+
objInstanceFull: node.objInstanceFull,
|
|
172
|
+
relationships: node.relationships || [],
|
|
173
|
+
targetIdentifiers: requestParams.targetIdentifiers
|
|
174
|
+
};
|
|
175
|
+
//(<beforeSendHook_<%= hook.flowTag %>>)
|
|
176
|
+
//(</beforeSendHook_<%= hook.flowTag %>>)
|
|
177
|
+
const hookResult = await lambda.invokeSync(
|
|
178
|
+
_izContext,
|
|
179
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
180
|
+
_izContext,
|
|
181
|
+
'<%= hook.flowTag %>'
|
|
182
|
+
),
|
|
183
|
+
{
|
|
184
|
+
requestParams: hookPayload_<%= hook.flowTag %>,
|
|
185
|
+
callingFlowConfig
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
if (hookResult?.errorsFound?.length > 0 || hookResult?.status === 'error') {
|
|
189
|
+
errorsFound.push(...(hookResult.errorsFound || ['Hook <%= hook.flowTag %> failed']));
|
|
190
|
+
}
|
|
191
|
+
} catch (hookErr) {
|
|
192
|
+
_izContext.logger.error('[UpdateComplete] Sync hook <%= hook.flowTag %> error:', hookErr);
|
|
193
|
+
errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
|
|
194
|
+
}
|
|
195
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
|
|
196
|
+
}
|
|
197
|
+
<%_ } _%>
|
|
198
|
+
<%_ }) _%>
|
|
199
|
+
<%_ } _%>
|
|
200
|
+
|
|
201
|
+
<%_ if (plan?.hasAfterHookAsync) { _%>
|
|
202
|
+
// ── Asynchronous After Hooks ──
|
|
203
|
+
<%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
|
|
204
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
|
|
205
|
+
if (returnValue.requestParams?.objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
|
|
206
|
+
<%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
|
|
207
|
+
if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(returnValue.requestParams?.objType?.objectType)) {
|
|
208
|
+
<%_ } _%>
|
|
209
|
+
let hookPayload_<%= hook.flowTag %> = {
|
|
210
|
+
objType: returnValue.requestParams.objType,
|
|
211
|
+
objInstanceFull: node.objInstanceFull,
|
|
212
|
+
relationships: node.relationships || [],
|
|
213
|
+
targetIdentifiers: requestParams.targetIdentifiers
|
|
214
|
+
};
|
|
215
|
+
//(<beforeSendHook_<%= hook.flowTag %>>)
|
|
216
|
+
//(</beforeSendHook_<%= hook.flowTag %>>)
|
|
217
|
+
const hookMsg_<%= hook.flowTag %> = callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
218
|
+
hookPayload_<%= hook.flowTag %>,
|
|
219
|
+
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
220
|
+
callingFlowConfig,
|
|
221
|
+
callingFlowSharedLib.createCallingFlowConfig(
|
|
222
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
223
|
+
_izContext,
|
|
224
|
+
'UpdateAfterHookCallbackHdrSqs'
|
|
225
|
+
),
|
|
226
|
+
{
|
|
227
|
+
actionData
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
await sns.publishAsync(_izContext, {
|
|
234
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
235
|
+
_izContext,
|
|
236
|
+
'<%= hook.flowTag %>_In'
|
|
237
|
+
),
|
|
238
|
+
Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
|
|
239
|
+
MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
|
|
240
|
+
});
|
|
241
|
+
<%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
|
|
242
|
+
}
|
|
243
|
+
<%_ } _%>
|
|
244
|
+
<%_ }) _%>
|
|
245
|
+
<%_ } else { _%>
|
|
145
246
|
const message =
|
|
146
247
|
callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
|
|
147
248
|
callingFlowConfig,
|
|
148
|
-
|
|
149
|
-
objType: returnValue.requestParams.objType,
|
|
150
|
-
objInstanceFull: node.objInstanceFull,
|
|
151
|
-
relationships: node.relationships,
|
|
152
|
-
parentFlowId: parentFlowId || returnValue?.parentFlowId,
|
|
153
|
-
existsNode,
|
|
154
|
-
status,
|
|
155
|
-
errorsFound
|
|
156
|
-
}
|
|
249
|
+
actionData
|
|
157
250
|
);
|
|
158
251
|
const messageAttributes =
|
|
159
252
|
callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
|
|
@@ -176,6 +269,7 @@ export default async function UpdateComplete(
|
|
|
176
269
|
),
|
|
177
270
|
TopicArn: topicArn
|
|
178
271
|
});
|
|
272
|
+
<%_ } _%>
|
|
179
273
|
//(<afterSendMessage>)
|
|
180
274
|
//(</afterSendMessage>)
|
|
181
275
|
}
|