@izara_project/izara-core-generate-service-code 1.0.88 → 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 (26) 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/CreateEndpoint_Main.ejs +33 -27
  8. package/src/emit/crud/templates/CreateLogicalLib.ejs +247 -89
  9. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +30 -20
  10. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
  11. package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
  12. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +26 -16
  13. package/src/emit/crud/templates/DeleteLogicalLib.ejs +112 -1
  14. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
  15. package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
  16. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +34 -22
  17. package/src/emit/crud/templates/UpdateLogicalLib.ejs +121 -1
  18. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
  19. package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
  20. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
  21. package/src/emit/resource/emitResourceArtifacts.js +9 -2
  22. package/test/crudApplicationArtifacts.test.js +345 -0
  23. package/test/dynamicCrudFlowSchemas.test.js +10 -5
  24. package/test/dynamicRbacFlowSchemas.test.js +24 -2
  25. package/test/graphDrivenFlowSchema.test.js +36 -0
  26. package/test/resourceArtifacts.test.js +71 -0
@@ -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
171
+ );
172
+
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
154
183
  );
155
184
 
156
- for (const [key, value] of Object.entries(identifiers)) {
157
- if (!requestParams.fieldNames.hasOwnProperty(key)) {
158
- requestParams.fieldNames[key] = value;
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
+ }
218
+ }
219
+ }
220
+
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
+ }
159
252
  }
160
253
  }
161
254
 
162
- _izContext.logger.debug(
163
- `[${process.env.iz_serviceTag}:<%- flowTag %>] Identifiers generated:`,
164
- identifiers
165
- );
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
 
@@ -641,60 +756,93 @@ export function resolveQuotaDeductionKeys(
641
756
  }
642
757
 
643
758
  /**
644
- * Forwards quota confirmation message to ConfirmUsage_In or CancelUsage_In
759
+ * Forwards quota confirmation or cancellation to RBAC via synchronous invoke
760
+ * and publishes final completion message to outCreateNodeComplete
645
761
  * @param {Object} _izContext - Izara context object
646
762
  * @param {Object} options - Message forwarding options
647
- * @returns {Promise<void>}
763
+ * @returns {Promise<Object>} Quota invocation result
648
764
  */
649
765
  export async function forwardToQuotaConfirmation(
650
766
  _izContext,
651
767
  {
652
768
  status,
653
- errorsFound,
769
+ errorsFound = [],
654
770
  quotaKeys,
655
771
  actionData,
656
772
  callingFlowConfig
657
773
  }
658
774
  ) {
659
- const nextTargetTopic =
660
- status === 'complete' && errorsFound.length === 0
661
- ? 'ConfirmUsage_In'
662
- : 'CancelUsage_In';
775
+ let quotaResult = null;
776
+ if (quotaKeys && (quotaKeys.limitTag || quotaKeys.flowTypeConcat)) {
777
+ let targetFunction = 'CancelUsageHdrInv';
778
+ if (status === 'complete' && errorsFound.length === 0) {
779
+ targetFunction = 'ConfirmUsageHdrInv';
780
+ }
663
781
 
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
- )
782
+ const functionName = await lambdaSharedLib.lambdaFunctionName(
783
+ _izContext,
784
+ targetFunction,
785
+ 'Rbac'
680
786
  );
681
787
 
682
- const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
683
- _izContext,
684
- nextTargetTopic,
685
- 'Rbac'
788
+ const forwardMessage = {
789
+ ...quotaKeys,
790
+ parentPassBackProperties: actionData,
791
+ callingFlowConfig
792
+ };
793
+
794
+ _izContext.logger.debug('[CreateLogicalLib] invoking RBAC quota sync', {
795
+ targetFunction,
796
+ functionName,
797
+ forwardMessage
798
+ });
799
+
800
+ try {
801
+ quotaResult = await lambda.invokeSync(
802
+ _izContext,
803
+ functionName,
804
+ forwardMessage
805
+ );
806
+ } catch (err) {
807
+ _izContext.logger.error(
808
+ `[CreateLogicalLib] error invoking ${targetFunction}:`,
809
+ err
810
+ );
811
+ if (targetFunction === 'ConfirmUsageHdrInv') {
812
+ try {
813
+ const cancelFunctionName = await lambdaSharedLib.lambdaFunctionName(
814
+ _izContext,
815
+ 'CancelUsageHdrInv',
816
+ 'Rbac'
817
+ );
818
+ await lambda.invokeSync(
819
+ _izContext,
820
+ cancelFunctionName,
821
+ forwardMessage
822
+ );
823
+ } catch (cancelErr) {
824
+ _izContext.logger.error(
825
+ '[CreateLogicalLib] error invoking CancelUsageHdrInv fallback:',
826
+ cancelErr
827
+ );
828
+ }
829
+ }
830
+ errorsFound.push(err.message || 'Quota confirmation failed');
831
+ if (actionData) {
832
+ actionData.status = 'error';
833
+ actionData.errorsFound = errorsFound;
834
+ }
835
+ }
836
+ }
837
+
838
+ const { message, messageAttributes } = buildCompletionPayload(
839
+ callingFlowConfig,
840
+ actionData
686
841
  );
687
842
 
688
- _izContext.logger.debug('[CreateNodeCallback] forwarding to RBAC', {
689
- nextTargetTopic,
690
- TopicArn: topicArn,
691
- forwardMessage
692
- });
843
+ await publishCompletionMessage(_izContext, message, messageAttributes);
693
844
 
694
- await sns.publishAsync(_izContext, {
695
- Message: JSON.stringify(forwardMessage),
696
- TopicArn: topicArn
697
- });
845
+ return quotaResult;
698
846
  }
699
847
 
700
848
  // ── 4. CreateComplete_Main Helpers ───────────────────────────────────────────
@@ -1357,10 +1505,24 @@ export async function <%- flowTag %>LogicalLib(
1357
1505
  <%_ if (plan?.hasAfterHookSync) { _%>
1358
1506
  // ── Synchronous After Hooks ──
1359
1507
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
1360
- <%_ if (hook.objType?.objectType) { _%>
1361
- if (objType.objectType === '<%= hook.objType.objectType %>') {
1508
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
1509
+ if (objType.objectType === '<%= hook.targetObjectTypes[0] %>') {
1510
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
1511
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType.objectType)) {
1362
1512
  <%_ } _%>
1363
1513
  try {
1514
+ let hookPayload_<%= hook.flowTag %> = {
1515
+ objType,
1516
+ objInstanceFull: {
1517
+ identifiers: identifiersForCreateData,
1518
+ fields: fieldsForHook
1519
+ },
1520
+ relationships: requestParams.relationships || [],
1521
+ targetIdentifiers: requestParams.targetIdentifiers,
1522
+ submittedByUserId: requestParams.submittedByUserId
1523
+ };
1524
+ //(<beforeSendHook_<%= hook.flowTag %>>)
1525
+ //(</beforeSendHook_<%= hook.flowTag %>>)
1364
1526
  const hookResult = await lambda.invokeSync(
1365
1527
  _izContext,
1366
1528
  await lambdaSharedLib.lambdaFunctionName(
@@ -1368,16 +1530,7 @@ export async function <%- flowTag %>LogicalLib(
1368
1530
  '<%= hook.flowTag %>'
1369
1531
  ),
1370
1532
  {
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
- },
1533
+ requestParams: hookPayload_<%= hook.flowTag %>,
1381
1534
  callingFlowConfig
1382
1535
  }
1383
1536
  );
@@ -1388,7 +1541,7 @@ export async function <%- flowTag %>LogicalLib(
1388
1541
  _izContext.logger.error('[<%- flowTag %>LogicalLib] Sync hook <%= hook.flowTag %> error:', hookErr);
1389
1542
  errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
1390
1543
  }
1391
- <%_ if (hook.objType?.objectType) { _%>
1544
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
1392
1545
  }
1393
1546
  <%_ } _%>
1394
1547
  <%_ }) _%>
@@ -1397,20 +1550,25 @@ export async function <%- flowTag %>LogicalLib(
1397
1550
  <%_ if (plan?.hasAfterHookAsync) { _%>
1398
1551
  // ── Asynchronous After Hooks ──
1399
1552
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
1400
- <%_ if (hook.objType?.objectType) { _%>
1401
- if (objType.objectType === '<%= hook.objType.objectType %>') {
1553
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
1554
+ if (objType.objectType === '<%= hook.targetObjectTypes[0] %>') {
1555
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
1556
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(objType.objectType)) {
1402
1557
  <%_ } _%>
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
1558
+ let hookPayload_<%= hook.flowTag %> = {
1559
+ objType,
1560
+ objInstanceFull: {
1561
+ identifiers: identifiersForCreateData,
1562
+ fields: fieldsForHook
1413
1563
  },
1564
+ relationships: requestParams.relationships || [],
1565
+ targetIdentifiers: requestParams.targetIdentifiers,
1566
+ submittedByUserId: requestParams.submittedByUserId
1567
+ };
1568
+ //(<beforeSendHook_<%= hook.flowTag %>>)
1569
+ //(</beforeSendHook_<%= hook.flowTag %>>)
1570
+ const hookMsg_<%= hook.flowTag %> = callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
1571
+ hookPayload_<%= hook.flowTag %>,
1414
1572
  callingFlowSharedLib.addParentCallingFlowConfig(
1415
1573
  callingFlowConfig,
1416
1574
  callingFlowSharedLib.createCallingFlowConfig(
@@ -1443,7 +1601,7 @@ export async function <%- flowTag %>LogicalLib(
1443
1601
  Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
1444
1602
  MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
1445
1603
  });
1446
- <%_ if (hook.objType?.objectType) { _%>
1604
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
1447
1605
  }
1448
1606
  <%_ } _%>
1449
1607
  <%_ }) _%>
@@ -175,10 +175,21 @@ export default async function CreateNodeCallbackMain(
175
175
  <%_ if (plan?.hasAfterHookSync) { _%>
176
176
  // ── Synchronous After Hooks ──
177
177
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv').forEach(hook => { _%>
178
- <%_ if (hook.objType?.objectType) { _%>
179
- if (returnValue.requestParams?.objType?.objectType === '<%= hook.objType.objectType %>') {
178
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
179
+ if (returnValue.requestParams?.objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
180
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
181
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(returnValue.requestParams?.objType?.objectType)) {
180
182
  <%_ } _%>
181
183
  try {
184
+ let hookPayload_<%= hook.flowTag %> = {
185
+ objType: returnValue.requestParams.objType,
186
+ objInstanceFull: node.objInstanceFull,
187
+ relationships: node.relationships || [],
188
+ targetIdentifiers: requestParams.targetIdentifiers,
189
+ submittedByUserId
190
+ };
191
+ //(<beforeSendHook_<%= hook.flowTag %>>)
192
+ //(</beforeSendHook_<%= hook.flowTag %>>)
182
193
  const hookResult = await lambda.invokeSync(
183
194
  _izContext,
184
195
  await lambdaSharedLib.lambdaFunctionName(
@@ -186,13 +197,7 @@ export default async function CreateNodeCallbackMain(
186
197
  '<%= hook.flowTag %>'
187
198
  ),
188
199
  {
189
- requestParams: {
190
- objType: returnValue.requestParams.objType,
191
- objInstanceFull: node.objInstanceFull,
192
- relationships: node.relationships || [],
193
- targetIdentifiers: requestParams.targetIdentifiers,
194
- submittedByUserId
195
- },
200
+ requestParams: hookPayload_<%= hook.flowTag %>,
196
201
  callingFlowConfig
197
202
  }
198
203
  );
@@ -203,7 +208,7 @@ export default async function CreateNodeCallbackMain(
203
208
  _izContext.logger.error('[CreateNodeCallback] Sync hook <%= hook.flowTag %> error:', hookErr);
204
209
  errorsFound.push(hookErr.message || 'Hook <%= hook.flowTag %> failed');
205
210
  }
206
- <%_ if (hook.objType?.objectType) { _%>
211
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
207
212
  }
208
213
  <%_ } _%>
209
214
  <%_ }) _%>
@@ -238,17 +243,22 @@ export default async function CreateNodeCallbackMain(
238
243
  <%_ if (plan?.hasAfterHookAsync) { _%>
239
244
  // ── Asynchronous After Hooks ──
240
245
  <%_ plan.afterHooks.filter(h => h?.generatedCodeConfig?.invocationType === 'ownTopic').forEach(hook => { _%>
241
- <%_ if (hook.objType?.objectType) { _%>
242
- if (returnValue.requestParams?.objType?.objectType === '<%= hook.objType.objectType %>') {
246
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length === 1) { _%>
247
+ if (returnValue.requestParams?.objType?.objectType === '<%= hook.targetObjectTypes[0] %>') {
248
+ <%_ } else if (hook.targetObjectTypes && hook.targetObjectTypes.length > 1) { _%>
249
+ if (<%- JSON.stringify(hook.targetObjectTypes) %>.includes(returnValue.requestParams?.objType?.objectType)) {
243
250
  <%_ } _%>
251
+ let hookPayload_<%= hook.flowTag %> = {
252
+ objType: returnValue.requestParams.objType,
253
+ objInstanceFull: node.objInstanceFull,
254
+ relationships: node.relationships || [],
255
+ targetIdentifiers: requestParams.targetIdentifiers,
256
+ submittedByUserId
257
+ };
258
+ //(<beforeSendHook_<%= hook.flowTag %>>)
259
+ //(</beforeSendHook_<%= hook.flowTag %>>)
244
260
  const hookMsg_<%= hook.flowTag %> = callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
245
- {
246
- objType: returnValue.requestParams.objType,
247
- objInstanceFull: node.objInstanceFull,
248
- relationships: node.relationships || [],
249
- targetIdentifiers: requestParams.targetIdentifiers,
250
- submittedByUserId
251
- },
261
+ hookPayload_<%= hook.flowTag %>,
252
262
  callingFlowSharedLib.addParentCallingFlowConfig(
253
263
  callingFlowConfig,
254
264
  callingFlowSharedLib.createCallingFlowConfig(
@@ -272,7 +282,7 @@ export default async function CreateNodeCallbackMain(
272
282
  Message: JSON.stringify(hookMsg_<%= hook.flowTag %>),
273
283
  MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
274
284
  });
275
- <%_ if (hook.objType?.objectType) { _%>
285
+ <%_ if (hook.targetObjectTypes && hook.targetObjectTypes.length > 0) { _%>
276
286
  }
277
287
  <%_ } _%>
278
288
  <%_ }) _%>
@@ -0,0 +1,118 @@
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 Delete
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 DeleteAfterHookCallbackMain(
37
+ _izContext,
38
+ requestParams,
39
+ passBackProperties
40
+ //(<additionalParams>)
41
+ //(</additionalParams>)
42
+ ) {
43
+ try {
44
+ _izContext.logger.debug('[DeleteAfterHookCallback] 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
+ identifiers:
72
+ returnValue?.identifiers || requestParams?.identifiers || {},
73
+ targetIdentifiers: requestParams?.targetIdentifiers,
74
+ parentFlowId: parentFlowId || returnValue?.parentFlowId,
75
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
76
+ errorsFound: mergedErrors
77
+ };
78
+
79
+ const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
80
+ _izContext,
81
+ TOPIC_NAME_GENERATE_CODE.outDeleteNodeComplete
82
+ );
83
+
84
+ const completionPayload =
85
+ callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
86
+ callingFlowConfig,
87
+ {
88
+ ...actionData,
89
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
90
+ errorsFound: mergedErrors
91
+ }
92
+ );
93
+
94
+ await sns.publishAsync(_izContext, {
95
+ TopicArn: topicArn,
96
+ Message: JSON.stringify(completionPayload),
97
+ MessageAttributes: sns.createStringMessageAttributes(
98
+ _izContext,
99
+ callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
100
+ callingFlowConfig,
101
+ {}
102
+ )
103
+ )
104
+ });
105
+
106
+ //(<afterProcessCallback>)
107
+ //(</afterProcessCallback>)
108
+
109
+ return {
110
+ returnValue,
111
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
112
+ errorsFound: mergedErrors
113
+ };
114
+ } catch (error) {
115
+ _izContext.logger.error('[DeleteAfterHookCallback] error:', error);
116
+ throw error;
117
+ }
118
+ }