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

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 (82) hide show
  1. package/package.json +7 -10
  2. package/src/compile/flowValidator.js +6 -0
  3. package/src/compile/loadSchemas.js +177 -69
  4. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +2 -2
  5. package/src/emit/crud/generateCrudApplicationArtifacts.js +15 -17
  6. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +16 -21
  7. package/src/emit/crud/loadLocalSchemaArrays.js +4 -8
  8. package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +6 -6
  9. package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +3 -3
  10. package/src/emit/crud/templates/CreateComplete_Main.ejs +22 -22
  11. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +18 -18
  12. package/src/emit/crud/templates/CreateLogicalLib.ejs +47 -47
  13. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +24 -24
  14. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +5 -5
  15. package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +3 -3
  16. package/src/emit/crud/templates/DeleteComplete_Main.ejs +23 -23
  17. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +15 -15
  18. package/src/emit/crud/templates/DeleteLogicalLib.ejs +19 -19
  19. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +3 -3
  20. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +5 -5
  21. package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +3 -3
  22. package/src/emit/crud/templates/UpdateComplete_Main.ejs +22 -22
  23. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +15 -15
  24. package/src/emit/crud/templates/UpdateLogicalLib.ejs +33 -33
  25. package/src/emit/custom/customFlowPaths.js +149 -0
  26. package/src/emit/custom/customFlowS3.js +156 -0
  27. package/src/emit/custom/customFlowSteps.js +385 -0
  28. package/src/emit/custom/customFlowTemplates.js +28 -0
  29. package/src/emit/custom/customFlows.js +48 -19
  30. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +57 -692
  31. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +27 -25
  32. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +7 -7
  33. package/src/emit/custom/templates/s3/ConfirmReserved_HdrSqs.ejs +3 -3
  34. package/src/emit/custom/templates/s3/ConfirmReserved_Main.ejs +3 -3
  35. package/src/emit/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +3 -3
  36. package/src/emit/custom/templates/s3/CreatePreSignUrl_Main.ejs +4 -4
  37. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +26 -6
  38. package/src/emit/rbac/emitRbacApplicationArtifacts.js +40 -13
  39. package/src/emit/rbac/generateCreateTargetRoleHandler.js +11 -13
  40. package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +14 -18
  41. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +13 -7
  42. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  43. package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +16 -19
  44. package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +19 -19
  45. package/src/emit/relationship/templates/ChangeRelationshipComplete_HdrSqs.ejs +2 -2
  46. package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +6 -6
  47. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +17 -17
  48. package/src/emit/relationship/templates/CreateRelationshipComplete_HdrSqs.ejs +2 -2
  49. package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +6 -6
  50. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +14 -14
  51. package/src/emit/relationship/templates/DeleteRelationshipComplete_HdrSqs.ejs +2 -2
  52. package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +6 -6
  53. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +10 -10
  54. package/src/emit/relationship/templates/GetRelationshipComplete_HdrSqs.ejs +2 -2
  55. package/src/emit/relationship/templates/GetRelationshipComplete_Main.ejs +5 -5
  56. package/src/emit/relationship/templates/GetRelationship_Main.ejs +9 -9
  57. package/src/emit/relationship/templates/MoveRelationshipComplete_HdrSqs.ejs +2 -2
  58. package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +6 -6
  59. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +15 -15
  60. package/src/emit/relationship/templates/UpdateRelationshipComplete_HdrSqs.ejs +2 -2
  61. package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +6 -6
  62. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +11 -11
  63. package/src/emit/resource/dynamoResourceArtifacts.js +18 -0
  64. package/src/emit/resource/emitResourceArtifacts.js +13 -1251
  65. package/src/emit/resource/generateResourceArtifacts.js +20 -20
  66. package/src/emit/resource/iamResourceArtifacts.js +612 -0
  67. package/src/emit/resource/resourceState.js +564 -0
  68. package/src/emit/resource/resourceTemplate.js +30 -0
  69. package/src/emit/resource/s3ResourceArtifacts.js +14 -0
  70. package/src/emit/resource/sqsResourceArtifacts.js +113 -0
  71. package/src/emit/shared/runSubsystemGeneration.js +55 -0
  72. package/src/emit/shared/templates/FindData_Main.ejs +19 -19
  73. package/src/emit/shared/templates/PaginateProcessLogical_Main.ejs +3 -3
  74. package/src/emit/shared/templates/ProcessLogical_Main.ejs +15 -15
  75. package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
  76. package/test/createTargetRoleHandler.test.js +62 -4
  77. package/test/crudApplicationArtifacts.test.js +75 -3
  78. package/test/customFlowApplicationArtifacts.test.js +98 -0
  79. package/test/dynamicCrudFlowSchemas.test.js +2 -2
  80. package/test/dynamicRbacFlowSchemas.test.js +32 -4
  81. package/test/relationshipApplicationArtifacts.test.js +1 -1
  82. package/test/resourceArtifacts.test.js +99 -0
@@ -0,0 +1,113 @@
1
+ import { indentYaml, renderResourceTemplate } from './resourceTemplate.js';
2
+
3
+ export async function renderResourceGroup({
4
+ ownTopics = [],
5
+ sqsQueues = [],
6
+ dsqQueues = [],
7
+ webSocketSubscriptions = [],
8
+ s3Subscriptions = []
9
+ }) {
10
+ const hasResources =
11
+ ownTopics.length > 0 ||
12
+ sqsQueues.length > 0 ||
13
+ dsqQueues.length > 0 ||
14
+ webSocketSubscriptions.length > 0 ||
15
+ s3Subscriptions.length > 0;
16
+ if (!hasResources) {
17
+ return '';
18
+ }
19
+
20
+ const chunks = await Promise.all([
21
+ ...ownTopics.map((queueName) =>
22
+ renderResourceTemplate('ResourceYaml.ejs', { queueName })
23
+ ),
24
+ ...sqsQueues.map((queueName) =>
25
+ renderResourceTemplate('ResourceSqsYaml.ejs', { queueName })
26
+ ),
27
+ ...dsqQueues.map((queueName) =>
28
+ renderResourceTemplate('ResourceSqsYaml.ejs', { queueName })
29
+ )
30
+ ]);
31
+ const wsSubs = await Promise.all(
32
+ webSocketSubscriptions.map((topicName) =>
33
+ renderWebSocketSubscription(topicName)
34
+ )
35
+ );
36
+ const s3Subs = await Promise.all(
37
+ s3Subscriptions.map(({ subscriptionName, topicName, queueName }) =>
38
+ renderResourceTemplate('S3Subscription_Yaml.ejs', {
39
+ subscriptionName,
40
+ topicName,
41
+ queueName
42
+ })
43
+ )
44
+ );
45
+ const parts = [chunks.join('\n')];
46
+ if (wsSubs.length > 0) {
47
+ parts.push(wsSubs.join('\n'));
48
+ }
49
+ if (s3Subs.length > 0) {
50
+ parts.push(s3Subs.join('\n'));
51
+ }
52
+ const body = parts.filter(Boolean).join('\n');
53
+ if (!body.trim()) {
54
+ return '';
55
+ }
56
+ return `Resources:\n${indentYaml(body, 2)}`;
57
+ }
58
+
59
+ export async function renderCrudInSqs(state) {
60
+ return renderResourceGroup({
61
+ ownTopics: state.crudOwnTopics,
62
+ sqsQueues: state.crudSqsQueues
63
+ });
64
+ }
65
+
66
+ export async function renderRelationshipInSqs(state) {
67
+ return renderResourceGroup({
68
+ ownTopics: state.relationshipOwnTopics,
69
+ sqsQueues: state.relationshipSqsQueues,
70
+ dsqQueues: state.relationshipDsqQueues
71
+ });
72
+ }
73
+
74
+ export async function renderRbacInSqs(state) {
75
+ return renderResourceGroup({
76
+ ownTopics: state.rbacOwnTopics,
77
+ sqsQueues: state.rbacSqsQueues
78
+ });
79
+ }
80
+
81
+ export async function renderCustomFlowInSqs(state) {
82
+ return renderResourceGroup({
83
+ ownTopics: state.customOwnTopics,
84
+ sqsQueues: state.customSqsQueues,
85
+ dsqQueues: state.customDsqQueues,
86
+ s3Subscriptions: state.s3Subscriptions
87
+ });
88
+ }
89
+
90
+ export async function renderSystemInSqs(state) {
91
+ return renderResourceGroup({
92
+ ownTopics: state.systemOwnTopics,
93
+ sqsQueues: state.systemSqsQueues,
94
+ dsqQueues: state.systemDsqQueues,
95
+ webSocketSubscriptions: state.webSocketSubscriptions
96
+ });
97
+ }
98
+
99
+ export async function renderOut(state) {
100
+ const renderedTopics = await Promise.all(
101
+ state.outTopics.map((topicName) =>
102
+ renderResourceTemplate('FlowOut_Yaml.ejs', { topicName })
103
+ )
104
+ );
105
+ const body = renderedTopics.join('\n');
106
+ return `Resources:\n${indentYaml(body, 2)}`;
107
+ }
108
+
109
+ export function renderWebSocketSubscription(topicName) {
110
+ return renderResourceTemplate('WebSocketSubscription_Yaml.ejs', {
111
+ topicName
112
+ });
113
+ }
@@ -0,0 +1,55 @@
1
+ import { compileProject } from '../../compile/compileProject.js';
2
+ import { buildGenerationPlan } from '../../plan/buildGenerationPlan.js';
3
+ import { commitArtifacts } from '../../output/commitArtifacts.js';
4
+
5
+ /**
6
+ * Common pipeline runner for emitting and committing subsystem artifacts.
7
+ *
8
+ * @param {Object} params
9
+ * @param {string} params.rootPath Root path of the service project.
10
+ * @param {Object} [params.options={}] Options passed to compile and load.
11
+ * @param {Function} [params.loadInputs] Function to load schemas or inputs.
12
+ * @param {Function} params.emitArtifacts Function to emit artifacts.
13
+ * @param {string} params.managedScope Scope identifier for generation plan.
14
+ * @param {Array<string>|Function} params.resolveManagedPaths Managed paths.
15
+ * @returns {Promise<{ project: Object, files: Array<string> }>}
16
+ */
17
+ export async function runSubsystemGeneration({
18
+ rootPath,
19
+ options = {},
20
+ loadInputs,
21
+ emitArtifacts,
22
+ managedScope,
23
+ resolveManagedPaths
24
+ }) {
25
+ const project = compileProject({ ...options, rootPath });
26
+
27
+ let inputs = {};
28
+ if (typeof loadInputs === 'function') {
29
+ inputs = await loadInputs(project, options);
30
+ }
31
+
32
+ const artifacts = await emitArtifacts({
33
+ project,
34
+ options,
35
+ ...inputs
36
+ });
37
+
38
+ let managedPaths = resolveManagedPaths;
39
+ if (typeof resolveManagedPaths === 'function') {
40
+ managedPaths = await resolveManagedPaths({
41
+ project,
42
+ options,
43
+ artifacts,
44
+ ...inputs
45
+ });
46
+ }
47
+
48
+ const plan = buildGenerationPlan(project, artifacts, {
49
+ managedPaths,
50
+ managedScope
51
+ });
52
+ const files = await commitArtifacts(plan);
53
+
54
+ return { project, files };
55
+ }
@@ -87,7 +87,7 @@ export default async function findData(
87
87
  }
88
88
  };
89
89
 
90
- let errorsFound = [];
90
+ let foundErrors = [];
91
91
 
92
92
  let standardErrorParams = [
93
93
  _izContext,
@@ -139,8 +139,8 @@ export default async function findData(
139
139
  _izContext.logger.debug('objInstanceBase: ', objInstanceBase)
140
140
 
141
141
  if (!objInstanceBase) {
142
- errorsFound.push(`can't get data in dynamo by ${identifiersBase}`)
143
- standardErrorParams.push(errorsFound)
142
+ foundErrors.push(`can't get data in dynamo by ${identifiersBase}`)
143
+ standardErrorParams.push(foundErrors)
144
144
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
145
145
  return {
146
146
  returnResponse: 'FindData cannot get data in dynamo'
@@ -148,8 +148,8 @@ export default async function findData(
148
148
  }
149
149
 
150
150
  if (!objInstanceBase.hasOwnProperty(fieldName.fieldName)) {
151
- errorsFound.push(`data in dynamoDB have no ${fieldName.fieldName}`)
152
- standardErrorParams.push(errorsFound)
151
+ foundErrors.push(`data in dynamoDB have no ${fieldName.fieldName}`)
152
+ standardErrorParams.push(foundErrors)
153
153
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
154
154
  return {
155
155
  returnResponse: 'Data have no fieldName'
@@ -195,8 +195,8 @@ export default async function findData(
195
195
  _izContext.logger.debug('objInstanceBase: ', objInstanceBase)
196
196
 
197
197
  if (!objInstanceBase) {
198
- errorsFound.push(`can't get data in dynamo by ${identifiersBase}`)
199
- standardErrorParams.push(errorsFound)
198
+ foundErrors.push(`can't get data in dynamo by ${identifiersBase}`)
199
+ standardErrorParams.push(foundErrors)
200
200
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
201
201
  return {
202
202
  returnResponse: 'FindData cannot get data in dynamo'
@@ -204,8 +204,8 @@ export default async function findData(
204
204
  }
205
205
 
206
206
  if (!objInstanceBase.hasOwnProperty(fieldName.fieldName)) {
207
- errorsFound.push(`data in dynamoDB have no ${fieldName.fieldName}`)
208
- standardErrorParams.push(errorsFound)
207
+ foundErrors.push(`data in dynamoDB have no ${fieldName.fieldName}`)
208
+ standardErrorParams.push(foundErrors)
209
209
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
210
210
  return {
211
211
  returnResponse: 'Data have no fieldName'
@@ -255,8 +255,8 @@ export default async function findData(
255
255
  _izContext.logger.debug('getDataByGraph: ', getDataByGraph)
256
256
 
257
257
  if (!getDataByGraph) {
258
- errorsFound.push(`can't get data of ${objType.objectType} in graph`)
259
- standardErrorParams.push(errorsFound)
258
+ foundErrors.push(`can't get data of ${objType.objectType} in graph`)
259
+ standardErrorParams.push(foundErrors)
260
260
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
261
261
  return {
262
262
  returnResponse: 'FindData cannot get data in graph'
@@ -271,8 +271,8 @@ export default async function findData(
271
271
 
272
272
  <% if (graphDetail.getInIdentifiers) { %>
273
273
  if (!objInstanceFull.identifiers.hasOwnProperty(fieldName.fieldName)) {
274
- errorsFound.push(`nodeProperties have no this ${fieldName.fieldName}`)
275
- standardErrorParams.push(errorsFound)
274
+ foundErrors.push(`nodeProperties have no this ${fieldName.fieldName}`)
275
+ standardErrorParams.push(foundErrors)
276
276
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
277
277
  return {
278
278
  returnResponse: 'Data have no fieldName'
@@ -283,8 +283,8 @@ export default async function findData(
283
283
  _izContext.logger.debug('this fieldName is identifier, get dataValue in identifiers: ', dataValue)
284
284
  <% } else { %>
285
285
  if (!objInstanceFull.fields.hasOwnProperty(fieldName.fieldName)) {
286
- errorsFound.push(`nodeProperties have no this ${fieldName.fieldName}`)
287
- standardErrorParams.push(errorsFound)
286
+ foundErrors.push(`nodeProperties have no this ${fieldName.fieldName}`)
287
+ standardErrorParams.push(foundErrors)
288
288
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
289
289
  return {
290
290
  returnResponse: 'Data have no fieldName'
@@ -314,8 +314,8 @@ export default async function findData(
314
314
  else
315
315
 
316
316
  <% } %> {
317
- errorsFound.push('unrecognized fieldName')
318
- standardErrorParams.push(errorsFound)
317
+ foundErrors.push('unrecognized fieldName')
318
+ standardErrorParams.push(foundErrors)
319
319
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
320
320
  return;
321
321
  }
@@ -326,8 +326,8 @@ export default async function findData(
326
326
  else
327
327
 
328
328
  <% } %> {
329
- errorsFound.push('unrecognized objectType')
330
- standardErrorParams.push(errorsFound)
329
+ foundErrors.push('unrecognized objectType')
330
+ standardErrorParams.push(foundErrors)
331
331
  await findDataSharedLib.completeFindDataMain(...standardErrorParams);
332
332
  return {
333
333
  returnResponse: 'Function FindData Error'
@@ -98,7 +98,7 @@ export default async function paginateProcessLogical(
98
98
  };
99
99
  }
100
100
 
101
- let errorsFound = [];
101
+ let foundErrors = [];
102
102
  let standardErrorParams = [
103
103
  _izContext,
104
104
  logicalResultId,
@@ -153,8 +153,8 @@ export default async function paginateProcessLogical(
153
153
 
154
154
  if (!startKey && queryDatas.Items.length == 0) {
155
155
  _izContext.logger.debug(`can't find data in table ${tableName}`);
156
- errorsFound.push(`can't find data in table ${tableName}`);
157
- standardErrorParams.push(errorsFound);
156
+ foundErrors.push(`can't find data in table ${tableName}`);
157
+ standardErrorParams.push(foundErrors);
158
158
  await logicalResultsSharedLib.completeLogicalResultsMain(
159
159
  ...standardErrorParams
160
160
  );
@@ -96,7 +96,7 @@ export default async function processLogical(
96
96
  };
97
97
  }
98
98
 
99
- let errorsFound = [];
99
+ let foundErrors = [];
100
100
 
101
101
  let standardErrorParams = [
102
102
  _izContext,
@@ -114,16 +114,16 @@ export default async function processLogical(
114
114
  element.comparison
115
115
  )
116
116
  ) {
117
- errorsFound.push('comparison is not possible comparison');
117
+ foundErrors.push('comparison is not possible comparison');
118
118
  }
119
119
 
120
120
  // validate partitionKeyValues
121
121
  if (!element.hasOwnProperty('partitionKeyValues')) {
122
122
  _izContext.logger.debug('element not have partitionKeyValues');
123
- errorsFound.push('element not have partitionKeyValues');
123
+ foundErrors.push('element not have partitionKeyValues');
124
124
  } else if (Object.keys(element.partitionKeyValues).length === 0) {
125
125
  _izContext.logger.debug('partitionKeyValues is empty');
126
- errorsFound.push('partitionKeyValues is empty');
126
+ foundErrors.push('partitionKeyValues is empty');
127
127
  }
128
128
 
129
129
  const objectSchemas = await getObjectSchema.getObjSchemaS3WithHierarchy(
@@ -186,7 +186,7 @@ export default async function processLogical(
186
186
  if (element.hasOwnProperty('sortKeyValues')) {
187
187
  continue;
188
188
  } else {
189
- errorsFound.push(
189
+ foundErrors.push(
190
190
  'schema error: dynamo storageResource set to groupByPartitionKeyField but no sortKey'
191
191
  );
192
192
  break;
@@ -219,7 +219,7 @@ export default async function processLogical(
219
219
  }
220
220
 
221
221
  if (!tableName) {
222
- errorsFound.push('no valid dynamo storageResource found');
222
+ foundErrors.push('no valid dynamo storageResource found');
223
223
  }
224
224
 
225
225
  if (
@@ -230,19 +230,19 @@ export default async function processLogical(
230
230
  _izContext.logger.debug(
231
231
  'element have sortKeyValues but cannot find sortKey in object schemas'
232
232
  );
233
- errorsFound.push(
233
+ foundErrors.push(
234
234
  'element have sortKeyValues but cannot find sortKey in object schemas'
235
235
  );
236
236
  }
237
237
 
238
- if (errorsFound.length > 0) {
239
- _izContext.logger.debug('Have errorsFound: ', errorsFound);
240
- standardErrorParams.push(errorsFound);
238
+ if (foundErrors.length > 0) {
239
+ _izContext.logger.debug('Have foundErrors: ', foundErrors);
240
+ standardErrorParams.push(foundErrors);
241
241
  await logicalResultsSharedLib.completeLogicalResultsMain(
242
242
  ...standardErrorParams
243
243
  );
244
244
  return {
245
- returnResponse: 'Have errorsFound'
245
+ returnResponse: 'Have foundErrors'
246
246
  };
247
247
  }
248
248
 
@@ -332,8 +332,8 @@ export default async function processLogical(
332
332
 
333
333
  if (queryDatas.Items.length == 0) {
334
334
  _izContext.logger.debug(`can't find data in ${tableName}`);
335
- errorsFound.push(`can't find data in ${tableName}`);
336
- standardErrorParams.push(errorsFound);
335
+ foundErrors.push(`can't find data in ${tableName}`);
336
+ standardErrorParams.push(foundErrors);
337
337
  await logicalResultsSharedLib.completeLogicalResultsMain(
338
338
  ...standardErrorParams
339
339
  );
@@ -411,8 +411,8 @@ export default async function processLogical(
411
411
  );
412
412
  } else {
413
413
  _izContext.logger.debug(`can't find data in ${tableName}`);
414
- errorsFound.push(`can't find data in ${tableName}`);
415
- standardErrorParams.push(errorsFound);
414
+ foundErrors.push(`can't find data in ${tableName}`);
415
+ standardErrorParams.push(foundErrors);
416
416
  await logicalResultsSharedLib.completeLogicalResultsMain(
417
417
  ...standardErrorParams
418
418
  );
@@ -1,5 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import ejsLib from 'ejs';
3
+ import { flowHasOwnTopic } from '../custom/customFlows.js';
3
4
 
4
5
  const TEMPLATE_URL = new URL('./templates/', import.meta.url);
5
6
  const SOURCE = 'app/src/generatedCode/SystemFlowSchemas/WebSocketMain/source';
@@ -126,7 +127,7 @@ export function hasSystemWebSocket(
126
127
  Array.isArray(flow?.event) &&
127
128
  (flow.event.includes('s3') ||
128
129
  (includeRelationshipOwnTopic &&
129
- flow.event.includes('ownTopic') &&
130
+ flowHasOwnTopic(flow) &&
130
131
  (flow.initiateBy === 'user' || flow.initiateBy === 'systemWithUser')))
131
132
  )
132
133
  );
@@ -36,7 +36,7 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
36
36
  const rootPath = await createProject(true);
37
37
  const result = await generateCreateTargetRoleHandler(rootPath);
38
38
 
39
- assert.equal(result.files.length, 59);
39
+ assert.equal(result.files.length, 60);
40
40
  assert.match(
41
41
  await readFile(join(rootPath, result.files[0].path), 'utf8'),
42
42
  /CreateTargetRole/
@@ -118,7 +118,55 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
118
118
  assert.match(validatorRbac, /return l2Hit;/);
119
119
  assert.match(validatorRbac, /return cacheResult;/);
120
120
  assert.doesNotMatch(validatorRbac, /L1 Cache hit:[^;]+;\s*return;/);
121
- assert.doesNotMatch(validatorRbac, /L2 Cache hit:[^;]+;\s*return;/);
121
+ const validatorHdrInv = await readFile(
122
+ join(
123
+ rootPath,
124
+ 'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_HdrInv.js'
125
+ ),
126
+ 'utf8'
127
+ );
128
+ assert.match(
129
+ validatorHdrInv,
130
+ /import ValidatorRbac from '\.\/ValidatorRbac_Main\.js';/
131
+ );
132
+ assert.match(
133
+ validatorHdrInv,
134
+ /middleware\.setValidatorSchema\(validatorSchema\);/
135
+ );
136
+ assert.match(
137
+ validatorHdrInv,
138
+ /await ValidatorRbac\(event\._izContext, \.\.\.passOnProperties\);/
139
+ );
140
+
141
+ const validatorHdrApi = await readFile(
142
+ join(
143
+ rootPath,
144
+ 'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_HdrApi.js'
145
+ ),
146
+ 'utf8'
147
+ );
148
+ assert.match(
149
+ validatorHdrApi,
150
+ /import ValidatorRbac from '\.\/ValidatorRbac_Main\.js';/
151
+ );
152
+ assert.match(
153
+ validatorHdrApi,
154
+ /middleware\.setValidatorSchema\(validatorSchema, { noAuthorizer: true }\);/
155
+ );
156
+ assert.match(
157
+ validatorHdrApi,
158
+ /await ValidatorRbac\(\s*event\._izContext,\s*\.\.\.passOnProperties\s*\);/
159
+ );
160
+
161
+ await assert.rejects(
162
+ readFile(
163
+ join(
164
+ rootPath,
165
+ 'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_HdrSqs.js'
166
+ )
167
+ )
168
+ );
169
+
122
170
  assert.match(
123
171
  await readFile(
124
172
  join(
@@ -133,6 +181,16 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
133
181
  join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'),
134
182
  'utf8'
135
183
  );
184
+ assert.match(
185
+ rbacYaml,
186
+ /ValidatorRbacHdrInv:\n\s+handler: src\/generatedCode\/FlowRbac\/ValidatorRbac\/source\/ValidatorRbac_HdrInv\.main/
187
+ );
188
+ assert.match(
189
+ rbacYaml,
190
+ /ValidatorRbacHdrApi:\n\s+handler: src\/generatedCode\/FlowRbac\/ValidatorRbac\/source\/ValidatorRbac_HdrApi\.main/
191
+ );
192
+ assert.doesNotMatch(rbacYaml, /ValidatorRbacHdrSqs:/);
193
+
136
194
  const limitYaml = await readFile(
137
195
  join(rootPath, 'app/sls_yaml/generatedCode/source/flow-limits.yml'),
138
196
  'utf8'
@@ -161,7 +219,7 @@ test('removes CreateTargetRole handler when RBAC is removed', async () => {
161
219
  await generateCreateTargetRoleHandler(rootPath);
162
220
  await writeFile(
163
221
  objectPath,
164
- "export default { objectType: 'user', addOnDataStructure: [] };\n"
222
+ "export default { objectType: 'user', generatedBy: 'user', fieldNames: { userId: { type: 'string', canUpdate: false, storageResourceTags: ['main'] } }, identifiers: [{ type: 'partitionKey', fieldName: 'userId' }], storageResources: { main: { storageType: 'dynamoDB', tableName: 'Users' } }, addOnDataStructure: [] };\n"
165
223
  );
166
224
 
167
225
  assert.deepEqual((await generateCreateTargetRoleHandler(rootPath)).files, []);
@@ -196,7 +254,7 @@ async function createProject(hasRbac) {
196
254
  await mkdir(directory, { recursive: true });
197
255
  await writeFile(
198
256
  join(directory, 'User.js'),
199
- `export default { objectType: 'user', fieldNames: [], identifiers: [], addOnDataStructure: ${addOns} };\n`
257
+ `export default { objectType: 'user', generatedBy: 'user', fieldNames: { userId: { type: 'string', canUpdate: false, storageResourceTags: ['main'] } }, identifiers: [{ type: 'partitionKey', fieldName: 'userId' }], storageResources: { main: { storageType: 'dynamoDB', tableName: 'Users' } }, addOnDataStructure: ${addOns} };\n`
200
258
  );
201
259
  return rootPath;
202
260
  }
@@ -44,7 +44,7 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
44
44
  assert.match(createMain, /CreateLogicalLib/);
45
45
  assert.match(
46
46
  createMain,
47
- /validateFieldNames\(_izContext, requestParams, objectSchemas, errorsFound\)/
47
+ /validateFieldNames\(_izContext, requestParams, objectSchemas, foundErrors\)/
48
48
  );
49
49
  assert.doesNotMatch(
50
50
  createMain,
@@ -59,12 +59,12 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
59
59
  );
60
60
  assert.match(
61
61
  createLogicalLib,
62
- /export async function resolveAndGenerateIdentifiers\(\s*_izContext,\s*requestParams,\s*objectSchema,\s*errorsFound\s*\)/
62
+ /export async function resolveAndGenerateIdentifiers\(\s*_izContext,\s*requestParams,\s*objectSchema,\s*foundErrors\s*\)/
63
63
  );
64
64
  assert.match(createLogicalLib, /return \{\s*objectSchemas\s*\};/);
65
65
  assert.match(
66
66
  createLogicalLib,
67
- /export function validateFieldNames\(\s*_izContext,\s*requestParams,\s*objectSchemas,\s*errorsFound\s*\)/
67
+ /export function validateFieldNames\(\s*_izContext,\s*requestParams,\s*objectSchemas,\s*foundErrors\s*\)/
68
68
  );
69
69
  assert.doesNotMatch(
70
70
  createLogicalLib,
@@ -1388,6 +1388,78 @@ test('emits autoProvisionOwnerTargetRole in CreateNodeCallback when service has
1388
1388
  );
1389
1389
  });
1390
1390
 
1391
+ test('emits synchronous before-hook invocation in Create_Main when event is generateCode and invocationType is lambdaSyncInv', async () => {
1392
+ const rootPath = await createProject(true);
1393
+ const schemas = join(rootPath, 'app/src/schemas');
1394
+ await writeFile(
1395
+ join(schemas, 'FlowSchemas/ValidateBeforeCreateSync.js'),
1396
+ "export default { flowTag: 'validateBeforeCreateSync', event: ['generateCode'], generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'before', invocationType: 'lambdaSyncInv' }, objType: { objectType: 'order' } };\n"
1397
+ );
1398
+ await generateCrudApplicationArtifacts(rootPath, {
1399
+ serviceTag: 'orders'
1400
+ });
1401
+
1402
+ const createMain = await readFile(
1403
+ join(
1404
+ rootPath,
1405
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
1406
+ ),
1407
+ 'utf8'
1408
+ );
1409
+ assert.match(
1410
+ createMain,
1411
+ /lambda\.invokeSync\(\s*_izContext,\s*await lambdaSharedLib\.lambdaFunctionName\(\s*_izContext,\s*'validateBeforeCreateSync'\s*\)/
1412
+ );
1413
+ assert.match(createMain, /Hook validateBeforeCreateSync failed/);
1414
+
1415
+ execFileSync(
1416
+ process.execPath,
1417
+ [
1418
+ '--check',
1419
+ join(
1420
+ rootPath,
1421
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
1422
+ )
1423
+ ],
1424
+ { stdio: 'inherit' }
1425
+ );
1426
+ });
1427
+
1428
+ test('emits async before-hook callback and SNS publish in Create_Main when event is generateCode and invocationType is ownTopic', async () => {
1429
+ const rootPath = await createProject(true);
1430
+ const schemas = join(rootPath, 'app/src/schemas');
1431
+ await writeFile(
1432
+ join(schemas, 'FlowSchemas/ValidateBeforeCreateAsync.js'),
1433
+ "export default { flowTag: 'validateBeforeCreateAsync', event: ['generateCode'], generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'before', invocationType: 'ownTopic' }, objType: { objectType: 'order' } };\n"
1434
+ );
1435
+ await generateCrudApplicationArtifacts(rootPath, {
1436
+ serviceTag: 'orders'
1437
+ });
1438
+
1439
+ const createMain = await readFile(
1440
+ join(
1441
+ rootPath,
1442
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
1443
+ ),
1444
+ 'utf8'
1445
+ );
1446
+ assert.match(createMain, /sns\.publishAsync\(_izContext,/);
1447
+ assert.match(
1448
+ createMain,
1449
+ /snsTopicArnByFlowSchema\(\s*_izContext,\s*'validateBeforeCreateAsync_In'\s*\)/
1450
+ );
1451
+ assert.match(createMain, /CreateBeforeHookCallbackHdrSqs/);
1452
+
1453
+ const callbackHandler = await readFile(
1454
+ join(
1455
+ rootPath,
1456
+ 'app/src/generatedCode/FlowObjects/Create/source/CreateBeforeHookCallback_HdrSqs.js'
1457
+ ),
1458
+ 'utf8'
1459
+ );
1460
+ assert.match(callbackHandler, /CreateBeforeHookCallback_Main/);
1461
+ });
1462
+
1391
1463
  async function createProject(withCrudObject) {
1392
1464
  const rootPath = await mkdtemp(
1393
1465
  join(tmpdir(), 'generate-service-code-v2-crud-application-')