@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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/src/compile/flowValidator.js +25 -0
  3. package/src/compile/loadSchemas.js +199 -10
  4. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
  5. package/src/emit/crud/crudActionPlan.js +105 -6
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +63 -3
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
  8. package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +121 -0
  9. package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +108 -0
  10. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +140 -340
  11. package/src/emit/crud/templates/CreateLogicalLib.ejs +338 -82
  12. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +90 -0
  13. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
  14. package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +99 -0
  15. package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
  16. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +151 -179
  17. package/src/emit/crud/templates/DeleteLogicalLib.ejs +244 -27
  18. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
  19. package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +112 -0
  20. package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
  21. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +166 -185
  22. package/src/emit/crud/templates/UpdateLogicalLib.ejs +123 -3
  23. package/src/emit/custom/customFlows.js +3 -1
  24. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
  25. package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
  26. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
  27. package/src/emit/resource/emitResourceArtifacts.js +119 -59
  28. package/test/crudApplicationArtifacts.test.js +876 -0
  29. package/test/dynamicCrudFlowSchemas.test.js +10 -5
  30. package/test/dynamicRbacFlowSchemas.test.js +24 -2
  31. package/test/graphDrivenFlowSchema.test.js +36 -0
  32. 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
- const deleteNodeCompleteMessage =
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
- status: 'error',
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: 'complete',
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 { sns } from '@izara_project/izara-core-library-external-request';
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
  }