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

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 (38) hide show
  1. package/package.json +1 -1
  2. package/src/compile/loadSchemas.js +45 -10
  3. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +1 -1
  4. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +22 -15
  5. package/src/emit/crud/crudActionPlan.js +47 -6
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +63 -3
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +6 -4
  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 +138 -335
  11. package/src/emit/crud/templates/CreateLogicalLib.ejs +205 -42
  12. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +80 -0
  13. package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +99 -0
  14. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +153 -182
  15. package/src/emit/crud/templates/DeleteLogicalLib.ejs +132 -26
  16. package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +112 -0
  17. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +166 -188
  18. package/src/emit/crud/templates/UpdateLogicalLib.ejs +2 -2
  19. package/src/emit/custom/customFlows.js +3 -1
  20. package/src/emit/function/functionYamlArtifact.js +4 -2
  21. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +0 -3
  22. package/src/emit/rbac/emitRbacApplicationArtifacts.js +1 -116
  23. package/src/emit/rbac/templates/CancelUsage_Main.ejs +14 -10
  24. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +14 -10
  25. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +22 -12
  26. package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +14 -10
  27. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +76 -34
  28. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +12 -9
  29. package/src/emit/resource/emitResourceArtifacts.js +141 -62
  30. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +14 -18
  31. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +4 -2
  32. package/test/createTargetRoleHandler.test.js +1 -15
  33. package/test/crudApplicationArtifacts.test.js +540 -3
  34. package/test/customFlowApplicationArtifacts.test.js +3 -5
  35. package/test/dynamicCrudFlowSchemas.test.js +5 -17
  36. package/test/dynamicRbacFlowSchemas.test.js +0 -3
  37. package/test/functionYamlArtifact.test.js +4 -2
  38. package/test/resourceArtifacts.test.js +203 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.86",
3
+ "version": "1.0.88",
4
4
  "description": "Code for locally generating per service files",
5
5
  "homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
6
6
  "repository": {
@@ -22,11 +22,21 @@ const VALID_INITIATE_BY_ENUM = [
22
22
  'systemNoUser'
23
23
  ];
24
24
 
25
+ const VALID_CODE_HOOK_TAG_ENUM = ['before', 'after'];
26
+
25
27
  if (basicValidatorSchema?.basicFlowSchema?.properties?.initiateBy) {
26
28
  basicValidatorSchema.basicFlowSchema.properties.initiateBy.enum =
27
29
  VALID_INITIATE_BY_ENUM;
28
30
  }
29
31
 
32
+ if (
33
+ basicValidatorSchema?.basicFlowSchema?.properties?.generatedCodeConfig
34
+ ?.properties?.codeHookTag
35
+ ) {
36
+ basicValidatorSchema.basicFlowSchema.properties.generatedCodeConfig.properties.codeHookTag.enum =
37
+ VALID_CODE_HOOK_TAG_ENUM;
38
+ }
39
+
30
40
  const VALID_GENERATED_BY_ENUM = [
31
41
  'user',
32
42
  'systemWithUser',
@@ -113,9 +123,17 @@ export async function loadLocalSchemaDirectory(
113
123
  if (directoryName === 'FlowSchemas') {
114
124
  for (const schema of loadedSchemas) {
115
125
  if (Object.prototype.hasOwnProperty.call(schema?.generatedCodeConfig ?? {}, 'codeHookTag')) {
116
- throw new Error(
117
- `Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag is no longer supported`
118
- );
126
+ const hookTag = schema.generatedCodeConfig.codeHookTag;
127
+ if (hookTag === 'beforeLogical') {
128
+ throw new Error(
129
+ `Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag is no longer supported`
130
+ );
131
+ }
132
+ if (hookTag !== 'before' && hookTag !== 'after') {
133
+ throw new Error(
134
+ `Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag '${hookTag}' is invalid. Must be 'before' or 'after'`
135
+ );
136
+ }
119
137
  }
120
138
  }
121
139
  }
@@ -177,13 +195,30 @@ export async function loadLocalSchemaDirectory(
177
195
  { logger },
178
196
  schema
179
197
  );
180
- if (!status && Array.isArray(errors) && VALID_INITIATE_BY_ENUM.includes(schema?.initiateBy)) {
181
- errors = errors.filter(
182
- (err) =>
183
- !err.includes('initiateBy') &&
184
- !err.includes('must be equal to one of the allowed values (enum)') &&
185
- !(err.includes('should be one of enum') && err.includes(schema?.initiateBy))
186
- );
198
+ if (!status && Array.isArray(errors)) {
199
+ if (VALID_INITIATE_BY_ENUM.includes(schema?.initiateBy)) {
200
+ errors = errors.filter(
201
+ (err) =>
202
+ !err.includes('initiateBy') &&
203
+ !err.includes('must be equal to one of the allowed values (enum)') &&
204
+ !(err.includes('should be one of enum') && err.includes(schema?.initiateBy))
205
+ );
206
+ }
207
+ if (
208
+ VALID_CODE_HOOK_TAG_ENUM.includes(
209
+ schema?.generatedCodeConfig?.codeHookTag
210
+ )
211
+ ) {
212
+ errors = errors.filter(
213
+ (err) =>
214
+ !err.includes('codeHookTag') &&
215
+ !err.includes('must be equal to one of the allowed values (enum)') &&
216
+ !(
217
+ err.includes('should be one of enum') &&
218
+ err.includes(schema?.generatedCodeConfig?.codeHookTag)
219
+ )
220
+ );
221
+ }
187
222
  if (errors.length === 0) {
188
223
  status = true;
189
224
  }
@@ -95,12 +95,12 @@ function _extractDynamoTableNames({
95
95
  }
96
96
  }
97
97
  if (rbac) {
98
+ dynamoTableNames.add('Roles');
98
99
  dynamoTableNames.add('TargetRoles');
99
100
  dynamoTableNames.add('RolePermissions');
100
101
  dynamoTableNames.add('RoleUsers');
101
102
  dynamoTableNames.add('UserRoles');
102
103
  dynamoTableNames.add('RbacPermissionCaches');
103
- dynamoTableNames.add('RoleLimit');
104
104
  dynamoTableNames.add('RunningTotalUsage');
105
105
  dynamoTableNames.add('RunningTotalReservedUsage');
106
106
  }
@@ -66,12 +66,12 @@ const externalServices = {
66
66
  }
67
67
  });
68
68
  if (typeof rbac !== 'undefined' && rbac) {
69
+ dynamoTableNames.add('Roles');
69
70
  dynamoTableNames.add('TargetRoles');
70
71
  dynamoTableNames.add('RolePermissions');
71
72
  dynamoTableNames.add('RoleUsers');
72
73
  dynamoTableNames.add('UserRoles');
73
74
  dynamoTableNames.add('RbacPermissionCaches');
74
- dynamoTableNames.add('RoleLimit');
75
75
  dynamoTableNames.add('RunningTotalUsage');
76
76
  dynamoTableNames.add('RunningTotalReservedUsage');
77
77
  }
@@ -182,20 +182,6 @@ const tableConfigs = {
182
182
  const currentServiceTag = (typeof serviceTag !== 'undefined' && serviceTag) ? serviceTag : ((typeof project !== 'undefined' && project?.serviceTag) ? project.serviceTag : '');
183
183
  const rbacSubs = [];
184
184
  if (crud && rbac) {
185
- const allRbacTags = Array.from(new Set([
186
- 'Rbac',
187
- currentServiceTag,
188
- ...(typeof rbacValidatorServiceTags !== 'undefined' && rbacValidatorServiceTags ? rbacValidatorServiceTags : [])
189
- ])).filter(Boolean);
190
- const hasGraph = typeof objectGraphServiceTags !== 'undefined' && objectGraphServiceTags && objectGraphServiceTags.length > 0;
191
- const createEndpoint = hasGraph ? 'CreateRbacCallbackHdrSqs' : 'CreateCompleteHdrSqs';
192
- const updateEndpoint = hasGraph ? 'UpdateRbacCallbackHdrSqs' : 'UpdateCompleteHdrSqs';
193
- const deleteEndpoint = hasGraph ? 'DeleteRbacCallbackHdrSqs' : 'DeleteCompleteHdrSqs';
194
- allRbacTags.forEach(function(tag) {
195
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: createEndpoint });
196
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: updateEndpoint });
197
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: deleteEndpoint });
198
- });
199
185
  if (typeof hasQuotaReservation !== 'undefined' && hasQuotaReservation) {
200
186
  rbacSubs.push({ serviceTag: 'Rbac', topicName: 'ConfirmUsage_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
201
187
  rbacSubs.push({ serviceTag: 'Rbac', topicName: 'CancelUsage_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
@@ -318,6 +304,17 @@ function roleConfigs() {
318
304
  <% }); -%>
319
305
  <% } -%>
320
306
  <% if (rbac || (rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0)) { -%>
307
+ {
308
+ Sid: 'FlowObjectsInvokeValidatorRbac',
309
+ Effect: 'Allow',
310
+ Action: ['lambda:InvokeFunction'],
311
+ Resource: [
312
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', 'Rbac')}`<% if (rbac) { %>,
313
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
314
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`<% }); %>
315
+ ]
316
+ },
317
+
321
318
  {
322
319
  Sid: 'FlowObjectsPublishValidatorRbacTopics',
323
320
  Effect: 'Allow',
@@ -340,6 +337,16 @@ function roleConfigs() {
340
337
  {
341
338
  roleName: 'FlowRbac',
342
339
  statement: [
340
+ {
341
+ Sid: 'FlowRbacInvokeValidatorRbac',
342
+ Effect: 'Allow',
343
+ Action: ['lambda:InvokeFunction'],
344
+ Resource: [
345
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', 'Rbac')}`<% if (rbac) { %>,
346
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
347
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`<% }); %>
348
+ ]
349
+ },
343
350
  {
344
351
  Sid: 'FlowRbacPublishValidatorRbacTopics',
345
352
  Effect: 'Allow',
@@ -1,6 +1,16 @@
1
1
  export const CRUD_ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
2
2
 
3
- export function createCrudActionPlans(objects = []) {
3
+ export function findHooksForAction(flows = [], action) {
4
+ if (!Array.isArray(flows)) return [];
5
+ return flows.filter((f) => {
6
+ const config = f?.generatedCodeConfig;
7
+ if (!config) return false;
8
+ const tag = config.generateCodeTag || config.generatedCodeTag;
9
+ return Boolean(tag && tag.toLowerCase() === action.toLowerCase());
10
+ });
11
+ }
12
+
13
+ export function createCrudActionPlans(objects = [], flows = []) {
4
14
  const hasGraph = hasGraphStorageResource(objects);
5
15
  const hasRbac = objects.some(
6
16
  (object) => isCrudObject(object) && hasRbacObject(object, objects)
@@ -12,29 +22,60 @@ export function createCrudActionPlans(objects = []) {
12
22
  return Object.fromEntries(
13
23
  CRUD_ACTIONS.map((action) => [
14
24
  action,
15
- createCrudActionPlan(action, { hasGraph, hasRbac, isBillable })
25
+ createCrudActionPlan(action, { hasGraph, hasRbac, isBillable, flows })
16
26
  ])
17
27
  );
18
28
  }
19
29
 
20
30
  export function createCrudActionPlan(
21
31
  action,
22
- { hasGraph = false, hasRbac = false, isBillable = true } = {}
32
+ { hasGraph = false, hasRbac = false, isBillable = true, flows = [] } = {}
23
33
  ) {
24
34
  const actionHasRbac = action !== 'Get' && hasRbac;
25
35
  const outputTopic = action !== 'Get';
26
36
  const hasQuotaReservation = action === 'Create' && hasRbac && isBillable;
27
37
 
38
+ const actionHooks = findHooksForAction(flows, action);
39
+ const beforeHooks = actionHooks.filter(
40
+ (h) => h?.generatedCodeConfig?.codeHookTag === 'before'
41
+ );
42
+ const afterHooks = actionHooks.filter(
43
+ (h) => h?.generatedCodeConfig?.codeHookTag === 'after'
44
+ );
45
+ const hasBeforeHookSync = beforeHooks.some(
46
+ (h) => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv'
47
+ );
48
+ const hasBeforeHookAsync = beforeHooks.some(
49
+ (h) => h?.generatedCodeConfig?.invocationType === 'ownTopic'
50
+ );
51
+ const hasBeforeHookCallback = outputTopic && hasBeforeHookAsync;
52
+
53
+ const hasAfterHookSync = afterHooks.some(
54
+ (h) => h?.generatedCodeConfig?.invocationType === 'lambdaSyncInv'
55
+ );
56
+ const hasAfterHookAsync = afterHooks.some(
57
+ (h) => h?.generatedCodeConfig?.invocationType === 'ownTopic'
58
+ );
59
+ const hasAfterHookCallback = outputTopic && hasAfterHookAsync;
60
+
28
61
  return {
29
62
  action,
30
63
  outputTopic,
31
64
  hasGraph: outputTopic && hasGraph,
32
65
  hasRbac: actionHasRbac,
33
66
  hasQuotaReservation,
34
- hasComplete: outputTopic && (actionHasRbac || hasGraph),
35
- hasRbacCallback: outputTopic && actionHasRbac && hasGraph,
67
+ hasComplete: outputTopic && (actionHasRbac || hasGraph || hasAfterHookCallback),
68
+ hasRbacCallback: false,
36
69
  hasCreateNodeCallback:
37
- outputTopic && action === 'Create' && actionHasRbac && hasGraph && hasQuotaReservation
70
+ outputTopic && action === 'Create' && actionHasRbac && hasGraph && hasQuotaReservation,
71
+ hasBeforeHookSync,
72
+ hasBeforeHookAsync,
73
+ hasBeforeHookCallback,
74
+ hasAfterHookSync,
75
+ hasAfterHookAsync,
76
+ hasAfterHookCallback,
77
+ beforeHooks,
78
+ afterHooks
38
79
  };
39
80
  }
40
81
 
@@ -51,7 +51,7 @@ export async function emitCrudApplicationArtifacts({
51
51
  }
52
52
 
53
53
  const handlers = await Promise.all(
54
- ACTIONS.map((action) => emitAction(action, objects, serviceTag))
54
+ ACTIONS.map((action) => emitAction(action, objects, serviceTag, flows))
55
55
  );
56
56
  return [
57
57
  ...handlers.flatMap((handler) => handler.artifacts),
@@ -88,6 +88,10 @@ export function crudApplicationManagedPaths() {
88
88
  `${source}/${action}RbacCallback_HdrSqs.js`,
89
89
  `${source}/${action}NodeCallback_Main.js`,
90
90
  `${source}/${action}NodeCallback_HdrSqs.js`,
91
+ `${source}/${action}BeforeHookCallback_Main.js`,
92
+ `${source}/${action}BeforeHookCallback_HdrSqs.js`,
93
+ `${source}/${action}AfterHookCallback_Main.js`,
94
+ `${source}/${action}AfterHookCallback_HdrSqs.js`,
91
95
  `${source}/${action}Complete_Main.js`,
92
96
  `${source}/${action}Complete_HdrSqs.js`
93
97
  );
@@ -98,10 +102,10 @@ export function crudApplicationManagedPaths() {
98
102
  ];
99
103
  }
100
104
 
101
- async function emitAction(action, objects, serviceTag) {
105
+ async function emitAction(action, objects, serviceTag, flows = []) {
102
106
  const source = `app/src/generatedCode/FlowObjects/${action}/source`;
103
107
  const handlerRoot = `src/generatedCode/FlowObjects/${action}/source`;
104
- const plan = createCrudActionPlans(objects)[action];
108
+ const plan = createCrudActionPlans(objects, flows)[action];
105
109
  const mainFileName = `${action}_Main`;
106
110
  const shared = {
107
111
  flowTag: action,
@@ -326,6 +330,62 @@ async function emitAction(action, objects, serviceTag) {
326
330
  });
327
331
  }
328
332
 
333
+ if (plan.hasBeforeHookCallback) {
334
+ const beforeHookCallbackMain = `${action}BeforeHookCallback_Main`;
335
+ artifacts.push(
336
+ {
337
+ path: `${source}/${beforeHookCallbackMain}.js`,
338
+ content: await render(`${action}BeforeHookCallback_Main.ejs`, shared)
339
+ },
340
+ {
341
+ path: `${source}/${action}BeforeHookCallback_HdrSqs.js`,
342
+ content: await renderShared('FlowHandler_HdrSqs.ejs', {
343
+ ...shared,
344
+ mainFileName: beforeHookCallbackMain,
345
+ functionName: `${action}BeforeHookCallbackMain`,
346
+ queueName: `${action}BeforeHookCallbackHdrSqs`
347
+ })
348
+ }
349
+ );
350
+ functions.push({
351
+ type: 'sqs',
352
+ functionName: `${action}BeforeHookCallbackHdrSqs`,
353
+ handlerPath: `${handlerRoot}/${action}BeforeHookCallback_HdrSqs.main`,
354
+ roleName: 'FlowObjectsRole',
355
+ queueName: `${action}BeforeHookCallbackHdrSqs`,
356
+ flowTag: action,
357
+ flowStepName: 'BeforeHookCallback'
358
+ });
359
+ }
360
+
361
+ if (plan.hasAfterHookCallback) {
362
+ const afterHookCallbackMain = `${action}AfterHookCallback_Main`;
363
+ artifacts.push(
364
+ {
365
+ path: `${source}/${afterHookCallbackMain}.js`,
366
+ content: await render(`${action}AfterHookCallback_Main.ejs`, shared)
367
+ },
368
+ {
369
+ path: `${source}/${action}AfterHookCallback_HdrSqs.js`,
370
+ content: await renderShared('FlowHandler_HdrSqs.ejs', {
371
+ ...shared,
372
+ mainFileName: afterHookCallbackMain,
373
+ functionName: `${action}AfterHookCallbackMain`,
374
+ queueName: `${action}AfterHookCallbackHdrSqs`
375
+ })
376
+ }
377
+ );
378
+ functions.push({
379
+ type: 'sqs',
380
+ functionName: `${action}AfterHookCallbackHdrSqs`,
381
+ handlerPath: `${handlerRoot}/${action}AfterHookCallback_HdrSqs.main`,
382
+ roleName: 'FlowObjectsRole',
383
+ queueName: `${action}AfterHookCallbackHdrSqs`,
384
+ flowTag: action,
385
+ flowStepName: 'AfterHookCallback'
386
+ });
387
+ }
388
+
329
389
  if (plan.hasComplete) {
330
390
  const completeMain = `${action}Complete_Main`;
331
391
  let templateName = `${action}Complete_Main.ejs`;
@@ -73,16 +73,17 @@ function _createDynamicFlow(action, plan, serviceTag = '', initiateBy = 'user')
73
73
  }
74
74
  );
75
75
  } else if (plan.hasComplete) {
76
+ const nextStepName = plan.hasCreateNodeCallback ? 'CreateNodeCallback' : 'Complete';
76
77
  startNextSteps.push({
77
78
  targetType: 'ownTopic',
78
- stepName: 'Complete'
79
+ stepName: nextStepName
79
80
  });
80
81
  if (plan.hasGraph) {
81
82
  startNextSteps.push({
82
83
  targetType: 'extTopic',
83
84
  serviceTag: 'GraphHandler',
84
85
  topicName: `${action}Node_In`,
85
- toStepNames: ['Complete']
86
+ toStepNames: [nextStepName]
86
87
  });
87
88
  }
88
89
  }
@@ -143,19 +144,20 @@ function _createDynamicFlow(action, plan, serviceTag = '', initiateBy = 'user')
143
144
  }
144
145
 
145
146
  if (plan.hasCreateNodeCallback) {
147
+ const prevStepName = plan.hasRbacCallback ? 'RbacCallback' : 'Start';
146
148
  flowSteps.push({
147
149
  stepName: 'CreateNodeCallback',
148
150
  event: ['extTopic'],
149
151
  previousSteps: [
150
152
  {
151
153
  sourceType: 'ownTopic',
152
- stepName: 'RbacCallback'
154
+ stepName: prevStepName
153
155
  },
154
156
  {
155
157
  sourceType: 'extTopic',
156
158
  serviceTag: 'GraphHandler',
157
159
  topicName: 'CreateNode_Out',
158
- fromStepNames: ['RbacCallback']
160
+ fromStepNames: [prevStepName]
159
161
  }
160
162
  ],
161
163
  nextSteps: [
@@ -0,0 +1,121 @@
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
+ import { forwardToQuotaConfirmation } from './CreateLogicalLib.js';
24
+
25
+ //(<optionalRequire>)
26
+ //(</optionalRequire>)
27
+
28
+ const { TOPIC_NAME_GENERATE_CODE } = consts;
29
+
30
+ /**
31
+ * Handles callback after async AfterHook completion
32
+ * @param {Object} _izContext - Izara context object
33
+ * @param {Object} requestParams - Callback request parameters
34
+ * @param {Object} passBackProperties - Pass-back properties
35
+ * @returns {Promise<Object>} Result object
36
+ */
37
+ export default async function CreateAfterHookCallbackMain(
38
+ _izContext,
39
+ requestParams,
40
+ passBackProperties
41
+ //(<additionalParams>)
42
+ //(</additionalParams>)
43
+ ) {
44
+ try {
45
+ _izContext.logger.debug('[CreateAfterHookCallback] started', {
46
+ requestParams,
47
+ passBackProperties
48
+ });
49
+
50
+ const {
51
+ status,
52
+ errorsFound = [],
53
+ returnValue,
54
+ parentFlowId
55
+ } = requestParams;
56
+
57
+ const mergedErrors = Array.isArray(errorsFound) ? [...errorsFound] : [];
58
+ if (status === 'error' && mergedErrors.length === 0) {
59
+ mergedErrors.push('AfterHook execution reported error');
60
+ }
61
+
62
+ const callingFlowConfig =
63
+ passBackProperties?.callingFlowConfig ||
64
+ requestParams?.passBackProperties?.callingFlowConfig ||
65
+ passBackProperties;
66
+
67
+ const quotaKeys =
68
+ passBackProperties?.quotaKeys ||
69
+ requestParams?.passBackProperties?.quotaKeys;
70
+
71
+ const actionData =
72
+ passBackProperties?.actionData ||
73
+ requestParams?.passBackProperties?.actionData ||
74
+ {
75
+ objType: returnValue?.objType || requestParams?.objType,
76
+ objInstanceFull: returnValue?.objInstanceFull || requestParams?.objInstanceFull,
77
+ relationships: returnValue?.relationships || requestParams?.relationships || [],
78
+ targetIdentifiers: requestParams?.targetIdentifiers,
79
+ parentFlowId: parentFlowId || returnValue?.parentFlowId,
80
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
81
+ errorsFound: mergedErrors
82
+ };
83
+
84
+ if (quotaKeys) {
85
+ await forwardToQuotaConfirmation(_izContext, {
86
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
87
+ errorsFound: mergedErrors,
88
+ quotaKeys,
89
+ actionData,
90
+ callingFlowConfig
91
+ });
92
+ } else {
93
+ const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
94
+ _izContext,
95
+ TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
96
+ );
97
+ const completionPayload = {
98
+ ...actionData,
99
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
100
+ errorsFound: mergedErrors
101
+ };
102
+ await sns.publishAsync(_izContext, {
103
+ TopicArn: topicArn,
104
+ Message: JSON.stringify(completionPayload),
105
+ MessageAttributes: sns.createStringMessageAttributes(_izContext, {})
106
+ });
107
+ }
108
+
109
+ //(<afterProcessCallback>)
110
+ //(</afterProcessCallback>)
111
+
112
+ return {
113
+ returnValue,
114
+ status: mergedErrors.length > 0 ? 'error' : 'complete',
115
+ errorsFound: mergedErrors
116
+ };
117
+ } catch (error) {
118
+ _izContext.logger.error('[CreateAfterHookCallback] error:', error);
119
+ throw error;
120
+ }
121
+ }
@@ -0,0 +1,108 @@
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
+ handleCreateError
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 handleCreateError(_izContext, {
75
+ objType: originalRequestParams?.objType,
76
+ objInstanceFull: {
77
+ identifiers:
78
+ originalRequestParams?.identifiers ||
79
+ originalRequestParams?.targetIdentifiers ||
80
+ {},
81
+ fields: originalRequestParams?.fieldNames || {}
82
+ },
83
+ requestParams: originalRequestParams || {},
84
+ callingFlowConfig,
85
+ errorsFound: mergedErrors
86
+ });
87
+ }
88
+
89
+ if (returnValue?.fieldNames && originalRequestParams?.fieldNames) {
90
+ Object.assign(originalRequestParams.fieldNames, returnValue.fieldNames);
91
+ }
92
+ if (returnValue?.relationships && originalRequestParams) {
93
+ originalRequestParams.relationships = returnValue.relationships;
94
+ }
95
+
96
+ //(<beforeLogicalExecution>)
97
+ //(</beforeLogicalExecution>)
98
+
99
+ return await <%- flowTag %>LogicalLib(
100
+ _izContext,
101
+ originalRequestParams,
102
+ callingFlowConfig
103
+ );
104
+ } catch (error) {
105
+ _izContext.logger.error('[<%- upperCaseFlowTag %>BeforeHookCallback] error:', error);
106
+ throw error;
107
+ }
108
+ }