@izara_project/izara-core-generate-service-code 1.0.88 → 1.0.90

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 (29) hide show
  1. package/package.json +1 -1
  2. package/src/compile/flowValidator.js +25 -0
  3. package/src/compile/loadSchemas.js +154 -0
  4. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
  5. package/src/emit/crud/crudActionPlan.js +67 -9
  6. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
  7. package/src/emit/crud/templates/CreateComplete_Main.ejs +135 -10
  8. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +33 -27
  9. package/src/emit/crud/templates/CreateLogicalLib.ejs +282 -95
  10. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +38 -21
  11. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
  12. package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
  13. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +26 -16
  14. package/src/emit/crud/templates/DeleteLogicalLib.ejs +113 -4
  15. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
  16. package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
  17. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +34 -22
  18. package/src/emit/crud/templates/UpdateLogicalLib.ejs +121 -4
  19. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
  20. package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
  21. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
  22. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +20 -13
  23. package/src/emit/resource/emitResourceArtifacts.js +55 -14
  24. package/test/createTargetRoleHandler.test.js +11 -8
  25. package/test/crudApplicationArtifacts.test.js +440 -0
  26. package/test/dynamicCrudFlowSchemas.test.js +10 -5
  27. package/test/dynamicRbacFlowSchemas.test.js +24 -2
  28. package/test/graphDrivenFlowSchema.test.js +36 -0
  29. package/test/resourceArtifacts.test.js +113 -0
@@ -51,10 +51,6 @@ import {
51
51
  //(<optionalRequire>)
52
52
  //(</optionalRequire>)
53
53
 
54
- const { createObjType, firstLetterUpperCase } = utils;
55
- const { createDataDetailsLib } = dataDetailsLib;
56
- const { createIdentifiersByUseCase } = identifiersObject;
57
-
58
54
  // ── Main Entry Function ───────────────────────────────────────────────────────
59
55
 
60
56
  /**
@@ -83,7 +79,7 @@ export default async function <%- flowTag %>(
83
79
  const errorsFound = [];
84
80
 
85
81
  // Step 1: Resolve Schema & Identity
86
- const { objType, objectSchema, targetIdentifiers } =
82
+ const { objType, objectSchema } =
87
83
  await resolveSchemaAndIdentity(_izContext, requestParams, errorsFound);
88
84
 
89
85
  //(<beforeQuery>)
@@ -94,8 +90,6 @@ export default async function <%- flowTag %>(
94
90
  _izContext,
95
91
  requestParams,
96
92
  objectSchema,
97
- objType,
98
- targetIdentifiers,
99
93
  errorsFound
100
94
  );
101
95
 
@@ -167,7 +161,9 @@ export default async function <%- flowTag %>(
167
161
  },
168
162
  parameterGroups,
169
163
  {
170
- targetIdentifiers,
164
+ targetIdentifiers:
165
+ requestParams.rbacTargetIdentifiers ||
166
+ requestParams.targetIdentifiers,
171
167
  rbacObjType: {
172
168
  serviceTag: <%- rbacServiceTags.length === 1 ? rbacServiceTags[0] : `{ ${Object.entries(rbacServiceTagMap).map(([k, v]) => `'${k}': ${v}`).join(', ')} }[requestParams.objectType] || process.env.iz_serviceTag` %>
173
169
  },
@@ -198,11 +194,15 @@ export default async function <%- flowTag %>(
198
194
 
199
195
  // ── Optional phase extension point ────────────────────────────────────────
200
196
  <%_
197
+ const allCrudObjectTypes = (objects || []).filter(isCrudObject).map(o => o.objectType);
201
198
  const beforeHooksByObjType = (plan?.beforeHooks || []).reduce((acc, hook) => {
202
- const key = hook?.objType?.objectType;
203
- if (!key) return acc;
204
- if (!acc[key]) acc[key] = [];
205
- acc[key].push(hook);
199
+ const targets = (hook?.targetObjectTypes && hook.targetObjectTypes.length > 0)
200
+ ? hook.targetObjectTypes
201
+ : allCrudObjectTypes;
202
+ for (const key of targets) {
203
+ if (!acc[key]) acc[key] = [];
204
+ acc[key].push(hook);
205
+ }
206
206
  return acc;
207
207
  }, {});
208
208
  _%>
@@ -216,6 +216,15 @@ export default async function <%- flowTag %>(
216
216
  const lambdaSyncInvResults = [];
217
217
  <%_ for (const config of configs.filter(c => c?.generatedCodeConfig?.invocationType === 'lambdaSyncInv' || c?.event?.includes('lambdaSyncInv'))) { _%>
218
218
  try {
219
+ let hookPayload_<%= config.flowTag %> = {
220
+ objType,
221
+ fieldNames: requestParams.fieldNames,
222
+ relationships: requestParams.relationships || [],
223
+ targetIdentifiers: requestParams.targetIdentifiers,
224
+ submittedByUserId: requestParams.submittedByUserId
225
+ };
226
+ //(<beforeSendHook_<%= config.flowTag %>>)
227
+ //(</beforeSendHook_<%= config.flowTag %>>)
219
228
  const returnValue = await lambda.invokeSync(
220
229
  _izContext,
221
230
  await lambdaSharedLib.lambdaFunctionName(
@@ -223,13 +232,7 @@ export default async function <%- flowTag %>(
223
232
  '<%= config.flowTag %>'
224
233
  ),
225
234
  {
226
- requestParams: {
227
- objType,
228
- fieldNames: requestParams.fieldNames,
229
- relationships: requestParams.relationships || [],
230
- targetIdentifiers,
231
- submittedByUserId: requestParams.submittedByUserId
232
- },
235
+ requestParams: hookPayload_<%= config.flowTag %>,
233
236
  callingFlowConfig
234
237
  }
235
238
  );
@@ -274,15 +277,18 @@ export default async function <%- flowTag %>(
274
277
  <%_ if (hasOwnTopic) { _%>
275
278
  // ── ownTopic (async — dispatch to before-hook and await callback) ──────────
276
279
  <%_ configs.filter(c => c?.generatedCodeConfig?.invocationType === 'ownTopic' || c?.event?.includes('ownTopic')).forEach(config => { _%>
280
+ let hookPayload_<%= config.flowTag %> = {
281
+ objType,
282
+ fieldNames: requestParams.fieldNames,
283
+ relationships: requestParams.relationships || [],
284
+ targetIdentifiers: requestParams.targetIdentifiers,
285
+ submittedByUserId: requestParams.submittedByUserId
286
+ };
287
+ //(<beforeSendHook_<%= config.flowTag %>>)
288
+ //(</beforeSendHook_<%= config.flowTag %>>)
277
289
  const hookMsg_<%= config.flowTag %> =
278
290
  callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
279
- {
280
- objType,
281
- fieldNames: requestParams.fieldNames,
282
- relationships: requestParams.relationships || [],
283
- targetIdentifiers,
284
- submittedByUserId: requestParams.submittedByUserId
285
- },
291
+ hookPayload_<%= config.flowTag %>,
286
292
  callingFlowSharedLib.addParentCallingFlowConfig(
287
293
  callingFlowConfig,
288
294
  callingFlowSharedLib.createCallingFlowConfig(
@@ -294,7 +300,7 @@ export default async function <%- flowTag %>(
294
300
  originalRequestParams: {
295
301
  ...requestParams,
296
302
  objType,
297
- targetIdentifiers
303
+ targetIdentifiers: requestParams.targetIdentifiers
298
304
  },
299
305
  callingFlowConfig
300
306
  }
@@ -89,7 +89,11 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
89
89
 
90
90
  targetIdentifiers = requestParams.targetIdentifiers;
91
91
 
92
- if (!targetIdentifiers && submittedByUserId) {
92
+ if (
93
+ !targetIdentifiers &&
94
+ objectSchema.belongTo?.objectType === 'user' &&
95
+ submittedByUserId
96
+ ) {
93
97
  targetIdentifiers = { submittedByUserId };
94
98
  }
95
99
 
@@ -98,10 +102,6 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
98
102
  errorsFound.push('not have targetIdentifiers');
99
103
 
100
104
  if (targetIdentifiers) {
101
- if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
102
- targetIdentifiers.submittedByUserId = submittedByUserId;
103
- }
104
-
105
105
  // if (Array.isArray(objectSchema.identifiers)) {
106
106
  // for (const identifier of objectSchema.identifiers) {
107
107
  // if (identifier.fieldName) {
@@ -122,7 +122,11 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
122
122
  // }
123
123
  // }
124
124
 
125
- // requestParams.targetIdentifiers = targetIdentifiers;
125
+ requestParams.targetIdentifiers = targetIdentifiers;
126
+ }
127
+
128
+ if (requestParams.rbacTargetIdentifiers === undefined && targetIdentifiers) {
129
+ requestParams.rbacTargetIdentifiers = { ...targetIdentifiers };
126
130
  }
127
131
 
128
132
  _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
@@ -140,50 +144,161 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, errors
140
144
  }
141
145
 
142
146
  /**
143
- * Resolves and generates primary identifiers for the object
147
+ * Resolves fromRequiredOnCreate fields and generates identifiers
144
148
  * @param {Object} _izContext - Izara context object
145
- * @param {Object} objectSchemas - Combined object schema
146
- * @param {Object} requestParams - Incoming request parameters
147
- * @returns {Promise<void>}
149
+ * @param {Object} requestParams - Request parameters
150
+ * @param {Object} objectSchema - Base object schema
151
+ * @param {Array<string>} errorsFound - Error list
152
+ * @returns {Promise<{ objectSchemas: Object }>}
148
153
  */
149
- export async function resolveAndGenerateIdentifiers(_izContext, objectSchemas, requestParams) {
150
- const identifiers = await createIdentifiersByUseCase(
151
- _izContext,
152
- objectSchemas,
153
- requestParams.fieldNames
154
+ export async function resolveAndGenerateIdentifiers(
155
+ _izContext,
156
+ requestParams,
157
+ objectSchema,
158
+ errorsFound
159
+ ) {
160
+ const objType = createObjType(requestParams.objectType);
161
+ const targetIdentifiers = requestParams.targetIdentifiers;
162
+
163
+ const objectSchemas =
164
+ uploadUseCase.generateObjectSchemaForCombineFieldNames(
165
+ _izContext,
166
+ objectSchema
167
+ );
168
+ _izContext.logger.debug(
169
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchemas:`,
170
+ objectSchemas
154
171
  );
155
172
 
156
- for (const [key, value] of Object.entries(identifiers)) {
157
- if (!requestParams.fieldNames.hasOwnProperty(key)) {
158
- requestParams.fieldNames[key] = value;
173
+ errorsFound.push(
174
+ ...(await createNodeLib.validateRequiredOnCreateLinks(
175
+ _izContext,
176
+ objType,
177
+ requestParams.relationships
178
+ ))
179
+ );
180
+ _izContext.logger.debug(
181
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] errorsFound:`,
182
+ errorsFound
183
+ );
184
+
185
+ // ── Resolve fromRequiredOnCreate fields ──────────────────────────────────
186
+ if (objectSchemas.fieldNames) {
187
+ if (!requestParams.fieldNames) {
188
+ requestParams.fieldNames = {};
189
+ }
190
+ for (const [fieldName, val] of Object.entries(objectSchemas.fieldNames)) {
191
+ if (
192
+ val.fromRequiredOnCreate &&
193
+ requestParams.fieldNames[fieldName] === undefined
194
+ ) {
195
+ const fromConfig = val.fromRequiredOnCreate;
196
+ const sourceField = fromConfig.fieldName || fieldName;
197
+ if (fromConfig.belongToObjType === true && targetIdentifiers) {
198
+ let targetValue = targetIdentifiers[sourceField];
199
+ if (targetValue === undefined) {
200
+ targetValue = Object.values(targetIdentifiers)[0];
201
+ }
202
+ requestParams.fieldNames[fieldName] = targetValue;
203
+ } else if (
204
+ fromConfig.relType &&
205
+ requestParams.relationships?.length > 0
206
+ ) {
207
+ const matchedRel = requestParams.relationships.find(
208
+ (rel) =>
209
+ rel.relType?.serviceTag === fromConfig.relType.serviceTag &&
210
+ rel.relType?.relationshipTag === fromConfig.relType.relationshipTag
211
+ );
212
+ if (matchedRel?.targetIdentifiers?.[sourceField] !== undefined) {
213
+ requestParams.fieldNames[fieldName] =
214
+ matchedRel.targetIdentifiers[sourceField];
215
+ }
216
+ }
217
+ }
159
218
  }
160
219
  }
161
220
 
162
- _izContext.logger.debug(
163
- `[${process.env.iz_serviceTag}:<%- flowTag %>] Identifiers generated:`,
164
- identifiers
165
- );
221
+ // ── Resolve identifiers ───────────────────────────────────────────────────
222
+ let errors;
223
+ const identifiers = objectSchemas.identifiers;
224
+ let optionalOnCreateIdentifiers;
225
+
226
+ if (
227
+ identifiers?.length === 1 &&
228
+ !identifiers[0].hasOwnProperty('fieldNames')
229
+ ) {
230
+ [requestParams, errors] = await createIdentifiersByUseCase(
231
+ _izContext,
232
+ requestParams,
233
+ objectSchemas,
234
+ identifiers[0],
235
+ optionalOnCreateIdentifiers
236
+ );
237
+ if (errors?.length > 0) {
238
+ errorsFound.push(...errors);
239
+ }
240
+ } else if (identifiers?.length > 1) {
241
+ for (const identifier of identifiers) {
242
+ [requestParams, errors] = await createIdentifiersByUseCase(
243
+ _izContext,
244
+ requestParams,
245
+ objectSchemas,
246
+ identifier,
247
+ optionalOnCreateIdentifiers
248
+ );
249
+ if (errors?.length > 0) {
250
+ errorsFound.push(...errors);
251
+ }
252
+ }
253
+ }
254
+
255
+ return { objectSchemas };
166
256
  }
167
257
 
168
258
  /**
169
259
  * Validates request field names against schema definition
170
260
  * @param {Object} _izContext - Izara context object
171
- * @param {Object} objectSchemas - Combined object schema
172
261
  * @param {Object} requestParams - Incoming request parameters
262
+ * @param {Object} objectSchemas - Combined object schema
173
263
  * @param {Array<string>} errorsFound - Array to collect validation errors
174
264
  */
175
- export function validateFieldNames(_izContext, objectSchemas, requestParams, errorsFound) {
176
- const invalidFieldNames = [];
177
- for (const fieldName of Object.keys(requestParams.fieldNames || {})) {
178
- if (!objectSchemas.fieldNames.includes(fieldName)) {
179
- invalidFieldNames.push(fieldName);
265
+ export function validateFieldNames(
266
+ _izContext,
267
+ requestParams,
268
+ objectSchemas,
269
+ errorsFound
270
+ ) {
271
+ const listOfRequiredOnCreate = [];
272
+ const listOfOptionalOnCreate = [];
273
+
274
+ for (const [key, val] of Object.entries(objectSchemas?.fieldNames || {})) {
275
+ if (
276
+ val.randomOnCreate === true ||
277
+ val.requiredOnCreate === true ||
278
+ val.fromRequiredOnCreate ||
279
+ val.hashOnCreate?.length
280
+ ) {
281
+ listOfRequiredOnCreate.push(key);
282
+ }
283
+ if (val.requiredOnCreate === false && val.optionalOnCreate === true) {
284
+ listOfOptionalOnCreate.push(key);
180
285
  }
181
286
  }
182
287
 
183
- if (invalidFieldNames.length > 0) {
184
- errorsFound.push(
185
- `Invalid fieldName not match from Schema: ${invalidFieldNames.join(',')}`
186
- );
288
+ for (const fieldName of Object.keys(requestParams.fieldNames || {})) {
289
+ if (
290
+ !listOfRequiredOnCreate.includes(fieldName) &&
291
+ !listOfOptionalOnCreate.includes(fieldName)
292
+ ) {
293
+ errorsFound.push('[invalid] over fieldNames create object');
294
+ }
295
+ if (errorsFound.length > 0) {
296
+ _izContext.logger.debug(
297
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] HaveError:`,
298
+ errorsFound
299
+ );
300
+ break;
301
+ }
187
302
  }
188
303
  }
189
304
 
@@ -372,14 +487,43 @@ export async function saveDynamoDbRecords(_izContext, listOfRecords) {
372
487
  );
373
488
  }
374
489
 
490
+ <%_
491
+ const rootRbacObjects = (objects || []).filter(obj => {
492
+ const hasSelfRbac = Array.isArray(obj?.addOnDataStructure) && obj.addOnDataStructure.some(addOn => addOn?.type === 'rbac');
493
+ return hasSelfRbac && !obj?.belongTo && obj?.objectType !== 'user' && obj?.generatedBy !== 'systemNoUser';
494
+ });
495
+ _%>
496
+ const ROOT_RBAC_OBJECT_TYPES = new Set([
497
+ <%_ for (const obj of rootRbacObjects) { _%>
498
+ '<%- obj.objectType %>',
499
+ <%_ } _%>
500
+ ]);
501
+
375
502
  /**
376
503
  * Auto-provisions owner TargetRole for Root RBAC object after Graph and DynamoDB write
377
504
  * @param {Object} _izContext - Izara context object
378
505
  * @param {Object} options - Target node and submitter details
379
506
  * @returns {Promise<void>}
380
507
  */
381
- export async function autoProvisionOwnerTargetRole(_izContext, { node, submittedByUserId }) {
508
+ export async function autoProvisionOwnerTargetRole(_izContext, { node, submittedByUserId, objType }) {
382
509
  try {
510
+ let objectType = null;
511
+ if (typeof objType === 'string') {
512
+ objectType = objType;
513
+ } else if (objType && objType.objectType) {
514
+ objectType = objType.objectType;
515
+ } else if (typeof node?.objType === 'string') {
516
+ objectType = node.objType;
517
+ } else if (node?.objType?.objectType) {
518
+ objectType = node.objType.objectType;
519
+ } else if (node?.objectType) {
520
+ objectType = node.objectType;
521
+ }
522
+
523
+ if (!objectType || !ROOT_RBAC_OBJECT_TYPES.has(objectType)) {
524
+ return;
525
+ }
526
+
383
527
  if (!submittedByUserId || !node?.objInstanceFull?.identifiers) {
384
528
  return;
385
529
  }
@@ -641,60 +785,93 @@ export function resolveQuotaDeductionKeys(
641
785
  }
642
786
 
643
787
  /**
644
- * Forwards quota confirmation message to ConfirmUsage_In or CancelUsage_In
788
+ * Forwards quota confirmation or cancellation to RBAC via synchronous invoke
789
+ * and publishes final completion message to outCreateNodeComplete
645
790
  * @param {Object} _izContext - Izara context object
646
791
  * @param {Object} options - Message forwarding options
647
- * @returns {Promise<void>}
792
+ * @returns {Promise<Object>} Quota invocation result
648
793
  */
649
794
  export async function forwardToQuotaConfirmation(
650
795
  _izContext,
651
796
  {
652
797
  status,
653
- errorsFound,
798
+ errorsFound = [],
654
799
  quotaKeys,
655
800
  actionData,
656
801
  callingFlowConfig
657
802
  }
658
803
  ) {
659
- const nextTargetTopic =
660
- status === 'complete' && errorsFound.length === 0
661
- ? 'ConfirmUsage_In'
662
- : 'CancelUsage_In';
804
+ let quotaResult = null;
805
+ if (quotaKeys && (quotaKeys.limitTag || quotaKeys.flowTypeConcat)) {
806
+ let targetFunction = 'CancelUsageHdrInv';
807
+ if (status === 'complete' && errorsFound.length === 0) {
808
+ targetFunction = 'ConfirmUsageHdrInv';
809
+ }
663
810
 
664
- const forwardMessage =
665
- callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
666
- {
667
- ...quotaKeys,
668
- parentPassBackProperties: actionData
669
- },
670
- callingFlowSharedLib.addParentCallingFlowConfig(
671
- callingFlowConfig,
672
- callingFlowSharedLib.createCallingFlowConfig(
673
- await lambdaSharedLib.lambdaFunctionName(
674
- _izContext,
675
- 'CreateCompleteHdrSqs'
676
- ),
677
- {}
678
- )
679
- )
811
+ const functionName = await lambdaSharedLib.lambdaFunctionName(
812
+ _izContext,
813
+ targetFunction,
814
+ 'Rbac'
680
815
  );
681
816
 
682
- const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
683
- _izContext,
684
- nextTargetTopic,
685
- 'Rbac'
817
+ const forwardMessage = {
818
+ ...quotaKeys,
819
+ parentPassBackProperties: actionData,
820
+ callingFlowConfig
821
+ };
822
+
823
+ _izContext.logger.debug('[CreateLogicalLib] invoking RBAC quota sync', {
824
+ targetFunction,
825
+ functionName,
826
+ forwardMessage
827
+ });
828
+
829
+ try {
830
+ quotaResult = await lambda.invokeSync(
831
+ _izContext,
832
+ functionName,
833
+ forwardMessage
834
+ );
835
+ } catch (err) {
836
+ _izContext.logger.error(
837
+ `[CreateLogicalLib] error invoking ${targetFunction}:`,
838
+ err
839
+ );
840
+ if (targetFunction === 'ConfirmUsageHdrInv') {
841
+ try {
842
+ const cancelFunctionName = await lambdaSharedLib.lambdaFunctionName(
843
+ _izContext,
844
+ 'CancelUsageHdrInv',
845
+ 'Rbac'
846
+ );
847
+ await lambda.invokeSync(
848
+ _izContext,
849
+ cancelFunctionName,
850
+ forwardMessage
851
+ );
852
+ } catch (cancelErr) {
853
+ _izContext.logger.error(
854
+ '[CreateLogicalLib] error invoking CancelUsageHdrInv fallback:',
855
+ cancelErr
856
+ );
857
+ }
858
+ }
859
+ errorsFound.push(err.message || 'Quota confirmation failed');
860
+ if (actionData) {
861
+ actionData.status = 'error';
862
+ actionData.errorsFound = errorsFound;
863
+ }
864
+ }
865
+ }
866
+
867
+ const { message, messageAttributes } = buildCompletionPayload(
868
+ callingFlowConfig,
869
+ actionData
686
870
  );
687
871
 
688
- _izContext.logger.debug('[CreateNodeCallback] forwarding to RBAC', {
689
- nextTargetTopic,
690
- TopicArn: topicArn,
691
- forwardMessage
692
- });
872
+ await publishCompletionMessage(_izContext, message, messageAttributes);
693
873
 
694
- await sns.publishAsync(_izContext, {
695
- Message: JSON.stringify(forwardMessage),
696
- TopicArn: topicArn
697
- });
874
+ return quotaResult;
698
875
  }
699
876
 
700
877
  // ── 4. CreateComplete_Main Helpers ───────────────────────────────────────────
@@ -1357,10 +1534,24 @@ export async function <%- flowTag %>LogicalLib(
1357
1534
  <%_ if (plan?.hasAfterHookSync) { _%>
1358
1535
  // ── Synchronous After Hooks ──
1359
1536
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
1360
- <%_ if (hook.objType?.objectType) { _%>
1361
- if (objType.objectType === '<%= hook.objType.objectType %>') {
1537
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
1538
+ if (objType.objectType === '<%= hook.targetObjectTypes[0] %>') {
1539
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
1540
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType.objectType)) {
1362
1541
  <%_ } _%>
1363
1542
  try {
1543
+ let hookPayload_<%= hook.flowTag %> = {
1544
+ objType,
1545
+ objInstanceFull: {
1546
+ identifiers: identifiersForCreateData,
1547
+ fields: fieldsForHook
1548
+ },
1549
+ relationships: requestParams.relationships || [],
1550
+ targetIdentifiers: requestParams.targetIdentifiers,
1551
+ submittedByUserId: requestParams.submittedByUserId
1552
+ };
1553
+ //(<beforeSendHook_<%= hook.flowTag %>>)
1554
+ //(</beforeSendHook_<%= hook.flowTag %>>)
1364
1555
  const hookResult = await lambda.invokeSync(
1365
1556
  _izContext,
1366
1557
  await lambdaSharedLib.lambdaFunctionName(
@@ -1368,16 +1559,7 @@ export async function <%- flowTag %>LogicalLib(
1368
1559
  '<%= hook.flowTag %>'
1369
1560
  ),
1370
1561
  {
1371
- requestParams: {
1372
- objType,
1373
- objInstanceFull: {
1374
- identifiers: identifiersForCreateData,
1375
- fields: fieldsForHook
1376
- },
1377
- relationships: requestParams.relationships || [],
1378
- targetIdentifiers: requestParams.targetIdentifiers,
1379
- submittedByUserId: requestParams.submittedByUserId
1380
- },
1562
+ requestParams: hookPayload_<%= hook.flowTag %>,
1381
1563
  callingFlowConfig
1382
1564
  }
1383
1565
  );
@@ -1388,7 +1570,7 @@ export async function <%- flowTag %>LogicalLib(
1388
1570
  _izContext.logger.error('[<%- flowTag %>LogicalLib] Sync hook <%= hook.flowTag %> error:', hookErr);
1389
1571
  errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
1390
1572
  }
1391
- <%_ if (hook.objType?.objectType) { _%>
1573
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
1392
1574
  }
1393
1575
  <%_ } _%>
1394
1576
  <%_ }) _%>
@@ -1397,20 +1579,25 @@ export async function <%- flowTag %>LogicalLib(
1397
1579
  <%_ if (plan?.hasAfterHookAsync) { _%>
1398
1580
  // ── Asynchronous After Hooks ──
1399
1581
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
1400
- <%_ if (hook.objType?.objectType) { _%>
1401
- if (objType.objectType === '<%= hook.objType.objectType %>') {
1582
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
1583
+ if (objType.objectType === '<%= hook.targetObjectTypes[0] %>') {
1584
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
1585
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType.objectType)) {
1402
1586
  <%_ } _%>
1403
- const hookMsg_<%= hook.flowTag %> = callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
1404
- {
1405
- objType,
1406
- objInstanceFull: {
1407
- identifiers: identifiersForCreateData,
1408
- fields: fieldsForHook
1409
- },
1410
- relationships: requestParams.relationships || [],
1411
- targetIdentifiers: requestParams.targetIdentifiers,
1412
- submittedByUserId: requestParams.submittedByUserId
1587
+ let hookPayload_<%= hook.flowTag %> = {
1588
+ objType,
1589
+ objInstanceFull: {
1590
+ identifiers: identifiersForCreateData,
1591
+ fields: fieldsForHook
1413
1592
  },
1593
+ relationships: requestParams.relationships || [],
1594
+ targetIdentifiers: requestParams.targetIdentifiers,
1595
+ submittedByUserId: requestParams.submittedByUserId
1596
+ };
1597
+ //(<beforeSendHook_<%= hook.flowTag %>>)
1598
+ //(</beforeSendHook_<%= hook.flowTag %>>)
1599
+ const hookMsg_<%= hook.flowTag %> = callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
1600
+ hookPayload_<%= hook.flowTag %>,
1414
1601
  callingFlowSharedLib.addParentCallingFlowConfig(
1415
1602
  callingFlowConfig,
1416
1603
  callingFlowSharedLib.createCallingFlowConfig(
@@ -1443,7 +1630,7 @@ export async function <%- flowTag %>LogicalLib(
1443
1630
  Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
1444
1631
  MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
1445
1632
  });
1446
- <%_ if (hook.objType?.objectType) { _%>
1633
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
1447
1634
  }
1448
1635
  <%_ } _%>
1449
1636
  <%_ }) _%>