@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
@@ -21,14 +21,10 @@ import {
21
21
  objectHash as hash,
22
22
  identifierUuid
23
23
  } from '@izara_project/izara-shared-core';
24
- import {
25
- consts as coreConsts,
26
- NoRetryError
27
- } from '@izara_project/izara-core-library-core';
24
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
28
25
 
29
26
  // ==================== Core Service Schemas ====================
30
27
  import {
31
- getObjectSchema,
32
28
  consts,
33
29
  utils,
34
30
  identifiersObject,
@@ -53,7 +49,10 @@ import {
53
49
  lambda
54
50
  } from '@izara_project/izara-core-library-external-request';
55
51
 
56
- import { <%- flowTag %>LogicalLib } from './<%- upperCaseFlowTag %>LogicalLib.js';
52
+ import {
53
+ <%- flowTag %>LogicalLib,
54
+ resolveSchemaAndIdentity
55
+ } from './<%- upperCaseFlowTag %>LogicalLib.js';
57
56
 
58
57
  //(<optionalRequire>)
59
58
  //(</optionalRequire>)
@@ -79,88 +78,44 @@ export default async function <%- flowTag %>(
79
78
  });
80
79
 
81
80
  const errorsFound = [];
82
- const objType = createObjType(requestParams.objectType);
83
-
84
- // ── Load schema ───────────────────────────────────────────────────────────
85
- const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
86
- _izContext,
87
- objType
88
- );
89
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchema:`, objectSchema);
90
-
91
- // ── Resolve submittedByUserId / targetIdentifiers ─────────────────────────
92
- let submittedByUserId, targetIdentifiers;
93
- if (
94
- objectSchema.generatedBy === 'user' ||
95
- objectSchema.generatedBy === 'systemWithUser'
96
- ) {
97
- submittedByUserId =
98
- requestParams.submittedByUserId ||
99
- _izContext.correlationIds?.get(coreConsts.SUBMITTED_BY_USER_ID);
100
-
101
- targetIdentifiers = requestParams.targetIdentifiers;
102
- delete requestParams.targetIdentifiers;
103
-
104
- if (!targetIdentifiers && objectSchema.belongTo?.objectType === 'user' && submittedByUserId) {
105
- targetIdentifiers = { submittedByUserId };
106
- }
107
81
 
108
- if (!submittedByUserId) errorsFound.push('Not have submittedByUserId');
109
- if (objectSchema.hasOwnProperty('belongTo') && !targetIdentifiers)
110
- errorsFound.push('not have targetIdentifiers');
111
-
112
- requestParams.submittedByUserId = submittedByUserId;
113
- if (targetIdentifiers) {
114
- if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
115
- targetIdentifiers.submittedByUserId = submittedByUserId;
116
- }
117
- }
118
-
119
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
120
- submittedByUserId: requestParams.submittedByUserId,
121
- targetIdentifiers,
122
- belongTo: objectSchema.belongTo,
123
- generatedBy: objectSchema.generatedBy
124
- });
125
- } else if (objectSchema.generatedBy === 'searchResultGenerated') {
126
- throw new NoRetryError(
127
- 'error searchResultGenerated should not happen here'
128
- );
129
- }
82
+ // ── Resolve Schema & Identity ─────────────────────────────────────────────
83
+ const { objType, objectSchema, targetIdentifiers } =
84
+ await resolveSchemaAndIdentity(_izContext, requestParams, errorsFound);
130
85
 
131
86
  // ── Build identifiers used by asynchronous processing ────────────────────
132
87
  const identifiers = requestParams.objInstanceFull?.identifiers;
133
88
 
134
- if (!objectSchema.hasOwnProperty('belongTo')) {
135
- if (!targetIdentifiers && identifiers) {
136
- targetIdentifiers = { ...identifiers };
137
- } else if (targetIdentifiers && identifiers) {
138
- for (const [key, value] of Object.entries(identifiers)) {
139
- if (targetIdentifiers[key] === undefined && value !== undefined) {
140
- targetIdentifiers[key] = value;
141
- }
142
- }
143
- }
144
- if (targetIdentifiers) {
145
- if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
146
- targetIdentifiers.submittedByUserId = submittedByUserId;
147
- }
148
- }
149
- }
150
-
151
- if (targetIdentifiers && identifiers) {
152
- for (const [key, value] of Object.entries(identifiers)) {
153
- if (
154
- targetIdentifiers[key] !== undefined &&
155
- value !== undefined &&
156
- targetIdentifiers[key] !== value
157
- ) {
158
- errorsFound.push(
159
- `targetIdentifiers.${key} must match objInstanceFull.identifiers.${key}`
160
- );
161
- }
162
- }
163
- }
89
+ // if (!objectSchema.hasOwnProperty('belongTo')) {
90
+ // if (!targetIdentifiers && identifiers) {
91
+ // targetIdentifiers = { ...identifiers };
92
+ // } else if (targetIdentifiers && identifiers) {
93
+ // for (const [key, value] of Object.entries(identifiers)) {
94
+ // if (targetIdentifiers[key] === undefined && value !== undefined) {
95
+ // targetIdentifiers[key] = value;
96
+ // }
97
+ // }
98
+ // }
99
+ // if (targetIdentifiers) {
100
+ // if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
101
+ // targetIdentifiers.submittedByUserId = submittedByUserId;
102
+ // }
103
+ // }
104
+ // }
105
+
106
+ // if (targetIdentifiers && identifiers) {
107
+ // for (const [key, value] of Object.entries(identifiers)) {
108
+ // if (
109
+ // targetIdentifiers[key] !== undefined &&
110
+ // value !== undefined &&
111
+ // targetIdentifiers[key] !== value
112
+ // ) {
113
+ // errorsFound.push(
114
+ // `targetIdentifiers.${key} must match objInstanceFull.identifiers.${key}`
115
+ // );
116
+ // }
117
+ // }
118
+ // }
164
119
 
165
120
  //(<validateBeforeUpdate>)
166
121
  //(</validateBeforeUpdate>)
@@ -245,110 +200,132 @@ export default async function <%- flowTag %>(
245
200
 
246
201
  // ── Optional phase extension point ────────────────────────────────────────
247
202
  <%_
248
- const groupedByObjType = [];
203
+ const allCrudObjectTypes = (objects || []).filter(isCrudObject).map(o => o.objectType);
204
+ const beforeHooksByObjType = (plan?.beforeHooks || []).reduce((acc, hook) => {
205
+ const targets = (hook?.targetObjectTypes && hook.targetObjectTypes.length > 0)
206
+ ? hook.targetObjectTypes
207
+ : allCrudObjectTypes;
208
+ for (const key of targets) {
209
+ if (!acc[key]) acc[key] = [];
210
+ acc[key].push(hook);
211
+ }
212
+ return acc;
213
+ }, {});
249
214
  _%>
250
- <%_ Object.entries(groupedByObjType).forEach(([objectType, configs]) => { _%>
215
+ <%_ Object.entries(beforeHooksByObjType).forEach(([objectType, configs]) => { _%>
251
216
  <%_
252
- const hasOwnTopic = configs.some(c => c.invocationType === 'ownTopic');
253
- const hasLambdaSyncInv = configs.some(c => c.invocationType === 'lambdaSyncInv');
254
- _%>
217
+ const hasOwnTopic = configs.some(c => c?.generatedCodeConfig?.invocationType === 'ownTopic' || c?.event?.includes('ownTopic'));
218
+ const hasLambdaSyncInv = configs.some(c => c?.generatedCodeConfig?.invocationType === 'lambdaSyncInv' || c?.event?.includes('lambdaSyncInv'));
219
+ _%>
255
220
  if (requestParams.objectType === '<%= objectType %>') {
256
221
  <%_ if (hasLambdaSyncInv) { _%>
257
- const lambdaSyncInvResults = await Promise.all([
258
- <%_ configs.filter(c => c.invocationType === 'lambdaSyncInv').forEach((config, index, arr) => { _%>
259
- // lambdaSyncInv <%= config.flowTag %>
260
- lambda
261
- .invokeSync(
222
+ const lambdaSyncInvResults = [];
223
+ <%_ for (const config of configs.filter(c => c?.generatedCodeConfig?.invocationType === 'lambdaSyncInv' || c?.event?.includes('lambdaSyncInv'))) { _%>
224
+ try {
225
+ let hookPayload_<%= config.flowTag %> = {
226
+ objType,
227
+ objInstanceFull: requestParams.objInstanceFull,
228
+ identifiers: requestParams.objInstanceFull?.identifiers,
229
+ fieldNames:
230
+ requestParams.objInstanceFull?.fields || requestParams.fieldNames,
231
+ relationships: requestParams.relationships || [],
232
+ targetIdentifiers,
233
+ submittedByUserId: requestParams.submittedByUserId
234
+ };
235
+ //(<beforeSendHook_<%= config.flowTag %>>)
236
+ //(</beforeSendHook_<%= config.flowTag %>>)
237
+ const returnValue = await lambda.invokeSync(
238
+ _izContext,
239
+ await lambdaSharedLib.lambdaFunctionName(
262
240
  _izContext,
263
- await lambdaSharedLib.lambdaFunctionName(
264
- _izContext,
265
- 'Process<%= config.flowTag %>HdrInv'
266
- ),
267
- { requestParams, callingFlowConfig }
268
- )
269
- .then((returnValue) => ({
270
- flowTag: '<%= config.flowTag %>',
271
- returnValue
272
- })) /*__EJS_COMMA_14__*/
273
- // lambdaSyncInv <%= config.flowTag %>
274
- <%_ }) _%>
275
- ]);
241
+ '<%= config.flowTag %>'
242
+ ),
243
+ {
244
+ requestParams: hookPayload_<%= config.flowTag %>,
245
+ callingFlowConfig
246
+ }
247
+ );
248
+ lambdaSyncInvResults.push({
249
+ flowTag: '<%= config.flowTag %>',
250
+ returnValue
251
+ });
252
+ if (returnValue?.errorsFound?.length > 0) {
253
+ errorsFound.push(...returnValue.errorsFound);
254
+ } else if (returnValue?.status === 'error') {
255
+ errorsFound.push('Error from <%= config.flowTag %>');
256
+ } else {
257
+ if (returnValue?.fields && requestParams?.objInstanceFull?.fields) {
258
+ Object.assign(requestParams.objInstanceFull.fields, returnValue.fields);
259
+ } else if (returnValue?.fieldNames && requestParams?.objInstanceFull?.fields) {
260
+ Object.assign(requestParams.objInstanceFull.fields, returnValue.fieldNames);
261
+ } else if (returnValue?.fieldNames && requestParams?.fieldNames) {
262
+ Object.assign(requestParams.fieldNames, returnValue.fieldNames);
263
+ }
264
+ if (returnValue?.relationships && requestParams) {
265
+ requestParams.relationships = returnValue.relationships;
266
+ }
267
+ }
268
+ } catch (hookErr) {
269
+ _izContext.logger.error(
270
+ '[<%- flowTag %>] Sync hook <%= config.flowTag %> error:',
271
+ hookErr
272
+ );
273
+ errorsFound.push(hookErr.message || 'Hook <%= config.flowTag %> failed');
274
+ }
275
+ <%_ } _%>
276
+
277
+ if (errorsFound.length > 0) {
278
+ return await <%- flowTag %>LogicalLib(
279
+ _izContext,
280
+ requestParams,
281
+ callingFlowConfig,
282
+ errorsFound
283
+ );
284
+ }
276
285
  <%_ } _%>
277
286
 
278
287
  //(<afterBeforeLogical_<%= objectType %>>)
279
288
  //(</afterBeforeLogical_<%= objectType %>>)
280
289
 
281
290
  <%_ if (hasOwnTopic) { _%>
282
- // ── ownTopic (async — create awaitingSteps and then publish) ──────────
283
- const awaitingMultipleStepIds = [];
284
- const recordMsgs = [];
285
-
286
- <%_ configs.filter(c => c.invocationType === 'ownTopic').forEach(config => { _%>
287
- // ownTopic <%= config.flowTag %>
288
- awaitingMultipleStepIds.push({
289
- awaitingStepId: await asyncFlowSharedLib.createAwaitingStepId(
290
- hash(identifiers),
291
- 'logical_<%= objectType %>_<%= config.flowTag.charAt(0).toUpperCase() + config.flowTag.slice(1) %>'
292
- ),
293
- additionalAttributes: {}
294
- });
295
-
296
- recordMsgs.push({
297
- msg: callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
298
- requestParams,
291
+ // ── ownTopic (async — dispatch to before-hook and await callback) ──────────
292
+ <%_ configs.filter(c => c?.generatedCodeConfig?.invocationType === 'ownTopic' || c?.event?.includes('ownTopic')).forEach(config => { _%>
293
+ let hookPayload_<%= config.flowTag %> = {
294
+ objType,
295
+ objInstanceFull: requestParams.objInstanceFull,
296
+ identifiers: requestParams.objInstanceFull?.identifiers,
297
+ fieldNames:
298
+ requestParams.objInstanceFull?.fields || requestParams.fieldNames,
299
+ relationships: requestParams.relationships || [],
300
+ targetIdentifiers,
301
+ submittedByUserId: requestParams.submittedByUserId
302
+ };
303
+ //(<beforeSendHook_<%= config.flowTag %>>)
304
+ //(</beforeSendHook_<%= config.flowTag %>>)
305
+ const hookMsg_<%= config.flowTag %> =
306
+ callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
307
+ hookPayload_<%= config.flowTag %>,
299
308
  callingFlowSharedLib.addParentCallingFlowConfig(
300
309
  callingFlowConfig,
301
310
  callingFlowSharedLib.createCallingFlowConfig(
302
- //(<callingFlowProperties_<%= config.flowTag %>>)
303
311
  await lambdaSharedLib.lambdaFunctionName(
304
312
  _izContext,
305
- '<%= flowTag.toLowerCase() %>Complete'
313
+ '<%- flowTag %>BeforeHookCallbackHdrSqs'
306
314
  ),
307
- {}
308
- //(</callingFlowProperties_<%= config.flowTag %>>)
315
+ {
316
+ originalRequestParams: {
317
+ ...requestParams,
318
+ objType,
319
+ targetIdentifiers
320
+ },
321
+ callingFlowConfig
322
+ }
309
323
  )
310
324
  )
311
- ),
312
- flowTag: '<%= config.flowTag.charAt(0).toUpperCase() + config.flowTag.slice(1) %>'
313
- });
314
- <%_ }) _%>
315
-
316
- <%_ if (hasLambdaSyncInv) { _%>
317
- // ── Add lambdaSyncInv results to awaitingSteps ────────────────────────
318
- for (const syncResult of lambdaSyncInvResults) {
319
- awaitingMultipleStepIds.push({
320
- awaitingStepId: await asyncFlowSharedLib.createAwaitingStepId(
321
- hash(identifiers),
322
- 'logical_<%= objectType %>_' + syncResult.flowTag
323
- ),
324
- additionalAttributes: {
325
- complete: true,
326
- errorsFound: [],
327
- returnValue: syncResult.returnValue
328
- }
329
- });
330
- }
331
- <%_ } _%>
332
-
333
- await asyncFlowSharedLib.createAwaitingMultipleSteps(
334
- _izContext,
335
- asyncFlowSharedLib.createPendingStepId(
336
- _izContext.uniqueRequestId,
337
- 'logical_<%= objectType %>'
338
- ),
339
- awaitingMultipleStepIds,
340
- {
341
- additionalAttributes: {
342
- callingFlowConfig
343
- }
344
- }
345
- );
346
-
347
- //(<beforePublish_<%= objectType %>>)
348
- //(</beforePublish_<%= objectType %>>)
325
+ );
349
326
 
350
- for (const recordMsg of recordMsgs) {
351
- const messageAttributes = sns.createStringMessageAttributes(
327
+ const messageAttributes_<%= config.flowTag %> =
328
+ sns.createStringMessageAttributes(
352
329
  _izContext,
353
330
  callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
354
331
  callingFlowConfig,
@@ -356,32 +333,36 @@ export default async function <%- flowTag %>(
356
333
  )
357
334
  );
358
335
 
359
- const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
360
- _izContext,
361
- `${recordMsg.flowTag}_In`
362
- );
336
+ const topicArn_<%= config.flowTag %> = snsSharedLib.snsTopicArnByFlowSchema(
337
+ _izContext,
338
+ '<%= config.flowTag %>_In'
339
+ );
363
340
 
364
- await sns.publishAsync(_izContext, {
365
- Message: JSON.stringify(recordMsg.msg),
366
- MessageAttributes: messageAttributes,
367
- TopicArn: topicArn
368
- });
369
- }
341
+ _izContext.logger.debug(
342
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] Dispatching to BeforeHook:`,
343
+ {
344
+ topicArn: topicArn_<%= config.flowTag %>,
345
+ flowTag: '<%= config.flowTag %>'
346
+ }
347
+ );
370
348
 
371
- return 'complete logical <%= objectType %>';
349
+ await sns.publishAsync(_izContext, {
350
+ Message: JSON.stringify(hookMsg_<%= config.flowTag %>),
351
+ MessageAttributes: messageAttributes_<%= config.flowTag %>,
352
+ TopicArn: topicArn_<%= config.flowTag %>
353
+ });
354
+ <%_ }) _%>
372
355
 
356
+ return {
357
+ status: 'pending',
358
+ message: 'Dispatched to BeforeHook'
359
+ };
373
360
  <%_ } else { _%>
374
- // ── lambdaSyncInv only — collect results and proceed to updateLogicalLib
375
- const phaseResults = Object.fromEntries(
376
- lambdaSyncInvResults.map(({ flowTag: tag, returnValue }) => [tag, returnValue])
377
- );
378
-
379
361
  return await <%- flowTag %>LogicalLib(
380
362
  _izContext,
381
363
  requestParams,
382
364
  callingFlowConfig,
383
- errorsFound,
384
- phaseResults
365
+ errorsFound
385
366
  );
386
367
  <%_ } _%>
387
368
  }
@@ -108,7 +108,7 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
108
108
  if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
109
109
  targetIdentifiers.submittedByUserId = submittedByUserId;
110
110
  }
111
- requestParams.targetIdentifiers = targetIdentifiers;
111
+ // requestParams.targetIdentifiers = targetIdentifiers;
112
112
  }
113
113
 
114
114
  _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
@@ -124,7 +124,7 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
124
124
  }
125
125
 
126
126
  requestParams.submittedByUserId = submittedByUserId;
127
- return { objType, objectSchema };
127
+ return { objType, objectSchema, targetIdentifiers };
128
128
  }
129
129
 
130
130
  // ── 2. UpdateRbacCallback_Main Helpers ────────────────────────────────────────
@@ -852,12 +852,132 @@ export async function <%- flowTag %>LogicalLib(
852
852
 
853
853
  // 5. If no graph storage, complete immediately
854
854
  if (!hasGraphStorage) {
855
+ <%_ if (plan?.hasAfterHookSync) { _%>
856
+ // ── Synchronous After Hooks ──
857
+ <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
858
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
859
+ if (objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
860
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
861
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType?.objectType)) {
862
+ <%_ } _%>
863
+ try {
864
+ let hookPayload_<%= hook.flowTag %> = {
865
+ objType,
866
+ objInstanceFull: createObjInstanceFullFromParams(
867
+ updateItem,
868
+ requestParams
869
+ ),
870
+ relationships: requestParams.relationships || [],
871
+ targetIdentifiers: requestParams.targetIdentifiers,
872
+ submittedByUserId: requestParams.submittedByUserId
873
+ };
874
+ //(<beforeSendHook_<%= hook.flowTag %>>)
875
+ //(</beforeSendHook_<%= hook.flowTag %>>)
876
+ const hookResult = await lambda.invokeSync(
877
+ _izContext,
878
+ await lambdaSharedLib.lambdaFunctionName(
879
+ _izContext,
880
+ '<%= hook.flowTag %>'
881
+ ),
882
+ {
883
+ requestParams: hookPayload_<%= hook.flowTag %>,
884
+ callingFlowConfig
885
+ }
886
+ );
887
+ if (
888
+ hookResult?.errorsFound?.length > 0 ||
889
+ hookResult?.status === 'error'
890
+ ) {
891
+ errorsFound.push(
892
+ ...(hookResult.errorsFound || ['Hook <%= hook.flowTag %> failed'])
893
+ );
894
+ }
895
+ } catch (hookErr) {
896
+ _izContext.logger.error(
897
+ '[<%- flowTag %>LogicalLib] Sync hook <%= hook.flowTag %> error:',
898
+ hookErr
899
+ );
900
+ errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
901
+ }
902
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
903
+ }
904
+ <%_ } _%>
905
+ <%_ }) _%>
906
+ <%_ } _%>
907
+
908
+ <%_ if (plan?.hasAfterHookAsync) { _%>
909
+ // ── Asynchronous After Hooks ──
910
+ <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
911
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
912
+ if (objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
913
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
914
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType?.objectType)) {
915
+ <%_ } _%>
916
+ let hookPayload_<%= hook.flowTag %> = {
917
+ objType,
918
+ objInstanceFull: createObjInstanceFullFromParams(
919
+ updateItem,
920
+ requestParams
921
+ ),
922
+ relationships: requestParams.relationships || [],
923
+ targetIdentifiers: requestParams.targetIdentifiers,
924
+ submittedByUserId: requestParams.submittedByUserId
925
+ };
926
+ //(<beforeSendHook_<%= hook.flowTag %>>)
927
+ //(</beforeSendHook_<%= hook.flowTag %>>)
928
+ const hookMsg_<%= hook.flowTag %> =
929
+ callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
930
+ hookPayload_<%= hook.flowTag %>,
931
+ callingFlowSharedLib.addParentCallingFlowConfig(
932
+ callingFlowConfig,
933
+ callingFlowSharedLib.createCallingFlowConfig(
934
+ await lambdaSharedLib.lambdaFunctionName(
935
+ _izContext,
936
+ 'UpdateAfterHookCallbackHdrSqs'
937
+ ),
938
+ {
939
+ actionData: {
940
+ objType,
941
+ objInstanceFull: createObjInstanceFullFromParams(
942
+ updateItem,
943
+ requestParams
944
+ ),
945
+ relationships: requestParams.relationships || [],
946
+ targetIdentifiers: requestParams.targetIdentifiers,
947
+ parentFlowId: requestParams.parentFlowId,
948
+ status: 'complete',
949
+ errorsFound
950
+ }
951
+ }
952
+ )
953
+ )
954
+ );
955
+
956
+ await sns.publishAsync(_izContext, {
957
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
958
+ _izContext,
959
+ '<%= hook.flowTag %>_In'
960
+ ),
961
+ Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
962
+ MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
963
+ });
964
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
965
+ }
966
+ <%_ } _%>
967
+ <%_ }) _%>
968
+ <%_ } else { _%>
855
969
  await handleDynamoDbOnlyComplete(_izContext, {
856
970
  objType,
857
971
  updateItem,
858
972
  requestParams,
859
973
  callingFlowConfig
860
974
  });
975
+ <%_ } _%>
976
+ }
977
+
978
+ let resultStatus = 'complete';
979
+ if (errorsFound.length > 0) {
980
+ resultStatus = 'error';
861
981
  }
862
982
 
863
983
  return {
@@ -866,7 +986,7 @@ export async function <%- flowTag %>LogicalLib(
866
986
  objInstanceFull: { identifiers, fields },
867
987
  versionedDataIds
868
988
  },
869
- status: 'complete',
989
+ status: resultStatus,
870
990
  errorsFound
871
991
  };
872
992
  } catch (err) {
@@ -16,7 +16,9 @@ export function supportedCustomFlows(flows) {
16
16
  (flow) =>
17
17
  typeof flow?.flowTag === 'string' &&
18
18
  flow.flowTag !== '' &&
19
- !flow.generatedCodeConfig &&
19
+ (!flow.generatedCodeConfig ||
20
+ (flow.generatedCodeConfig.codeHookTag &&
21
+ !flow.event.includes('ownTopic'))) &&
20
22
  Array.isArray(flow.event) &&
21
23
  flow.event.some((event) => SUPPORTED_EVENTS.has(event))
22
24
  );
@@ -19,8 +19,14 @@ const RBAC_CORE_FLOW_DEFINITIONS = [
19
19
 
20
20
  const LIMIT_FLOW_DEFINITIONS = [
21
21
  { flowTag: 'ReservedDynamicUsage', event: ['ownTopic'] },
22
- { flowTag: 'ConfirmUsage', event: ['ownTopic'] },
23
- { flowTag: 'CancelUsage', event: ['ownTopic'] }
22
+ {
23
+ flowTag: 'ConfirmUsage',
24
+ event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi']
25
+ },
26
+ {
27
+ flowTag: 'CancelUsage',
28
+ event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi']
29
+ }
24
30
  ];
25
31
 
26
32
  const RBAC_FLOW_DEFINITIONS = [
@@ -146,20 +146,40 @@ export default async function CancelUsage(_izContext, requestParams) {
146
146
  }
147
147
  }
148
148
 
149
- await sns.publishMessage(_izContext, {
150
- message: {
151
- returnResult: {
152
- status: true,
153
- uniqueIdentify: usedTimestampId,
154
- usedTimestampId,
155
- runningTotalReservedId
156
- }
157
- },
158
- TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
159
- _izContext,
160
- 'CancelUsage_Out'
161
- )
162
- });
149
+ const returnResult = {
150
+ status: true,
151
+ uniqueIdentify: usedTimestampId,
152
+ usedTimestampId,
153
+ runningTotalReservedId
154
+ };
155
+
156
+ const eventSourceTag =
157
+ _izContext.izEventSource?.eventSourceTag ||
158
+ requestParams?.izEventSource?.eventSourceTag;
159
+
160
+ let isSync = false;
161
+ if (
162
+ eventSourceTag === 'DirectInvoke' ||
163
+ eventSourceTag === 'ApiGateway' ||
164
+ eventSourceTag === 'ApiGatewayV2' ||
165
+ requestParams?.hasOwnProperty('__context__')
166
+ ) {
167
+ isSync = true;
168
+ }
169
+
170
+ if (!isSync) {
171
+ await sns.publishMessage(_izContext, {
172
+ message: {
173
+ returnResult
174
+ },
175
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
176
+ _izContext,
177
+ 'CancelUsage_Out'
178
+ )
179
+ });
180
+ }
181
+
182
+ return returnResult;
163
183
  } catch (error) {
164
184
  _izContext.logger.error(`[${process.env.iz_serviceTag}:CancelUsage] error:`, error);
165
185
  throw error;