@izara_project/izara-core-generate-service-code 1.0.61 → 1.0.63

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 (44) hide show
  1. package/package.json +5 -6
  2. package/src/codeGenerators/app/initial_setup/InitialSetupGenerator.js +100 -150
  3. package/src/codeGenerators/app/initial_setup/templates/InitialSetup_LambdaRole.ejs +27 -20
  4. package/src/codeGenerators/app/sls_yaml/FunctionYamlGenerator.js +8 -17
  5. package/src/codeGenerators/app/sls_yaml/RoleNameConfigGenerator.js +13 -72
  6. package/src/codeGenerators/app/sls_yaml/_policy/PolicyEmitter.js +3 -2
  7. package/src/codeGenerators/app/sls_yaml/_policy/__tests__/PolicyEmitter.test.js +3 -3
  8. package/src/codeGenerators/app/sls_yaml/_policy/templates/ManagedPolicy_Yaml.ejs +2 -2
  9. package/src/codeGenerators/app/sls_yaml/templates/SharedResource_Yaml.ejs +1 -1
  10. package/src/codeGenerators/app/src/generatedCode/Flow/FlowObjects/EndpointsGenerator.js +1 -1
  11. package/src/codeGenerators/app/src/generatedCode/Flow/FlowObjects/templates/crud/CreateEndpoint_Main.ejs +6 -6
  12. package/src/codeGenerators/app/src/generatedCode/Flow/{FlowSchemas → FlowObjects}/templates/step/FlowEndpointComplete_Main.ejs +78 -69
  13. package/src/codeGenerators/app/src/generatedCode/Flow/FlowRelationships/templates/relationship/ProcessChangeRelationship_Main.ejs +2 -2
  14. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowEndpoint_HdrWbs.ejs +2 -2
  15. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowEndpoint_Main.ejs +8 -4
  16. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowMain_Wbs.ejs +4 -4
  17. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/s3/CreatePreSignUrl_Main.ejs +1 -1
  18. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/s3/ProcessS3File_Main.ejs +1 -1
  19. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/BaseDsqHandler.js +4 -13
  20. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/BaseSqsHandler.js +14 -15
  21. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/lambdaSyncApi.js +4 -13
  22. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/lambdaSyncInv.js +4 -13
  23. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/ownTopic.js +16 -4
  24. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrDsq.ejs +79 -63
  25. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrSqs.ejs +62 -50
  26. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowEntryPointBase.js +7 -4
  27. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowMainFunctionBase.js +23 -33
  28. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowNaming.js +16 -0
  29. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowStepBase.js +10 -5
  30. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowStepNormalizer.js +21 -20
  31. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowValidator.js +1 -13
  32. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/propertyNormalizer.js +20 -0
  33. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/roleNameDetection.js +49 -0
  34. package/src/codeGenerators/resource/sls_yaml/DynamoDBGenerator.js +1 -10
  35. package/src/codeGenerators/resource/sls_yaml/FlowOutGenerator.js +2 -10
  36. package/src/codeGenerators/resource/sls_yaml/FlowResourceYamlGenerator.js +2 -11
  37. package/src/core/validate.js +9 -5
  38. package/src/generate.js +2 -2
  39. package/src/generateCode.js +21 -0
  40. package/src/schemaGenerators/app/src/schemas/FlowSchemas/FlowSchemaGenerator.js +0 -43
  41. package/test_schema.js +2 -0
  42. package/test_generate.js +0 -7
  43. package/test_normalize.js +0 -17
  44. /package/src/codeGenerators/app/src/generatedCode/Flow/FlowObjects/templates/{FlowEndpointBeforeLogical_Main.ejs → step/FlowEndpointBeforeLogical_Main.ejs} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.61",
3
+ "version": "1.0.63",
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": {
@@ -18,15 +18,14 @@
18
18
  "testEnvironment": "node"
19
19
  },
20
20
  "dependencies": {
21
- "@izara_project/izara-attribute-tree": "^1.0.15",
22
21
  "@izara_project/izara-core-library-core": "^1.0.32",
23
22
  "@izara_project/izara-core-library-logger": "^1.0.9",
24
- "@izara_project/izara-core-library-s3": "^1.0.10",
25
- "@izara_project/izara-property-nodes": "^1.0.16",
26
23
  "@izara_project/izara-shared-core": "^1.0.12",
27
24
  "ejs": "^5.0.1",
28
- "js-beautify": "^1.15.4",
29
- "yaml": "^2.9.0"
25
+ "js-beautify": "^2.0.3",
26
+ "yaml": "^2.9.0",
27
+ "@izara_project/izara-property-nodes": "^1.0.16",
28
+ "@izara_project/izara-attribute-tree": "^1.0.15"
30
29
  },
31
30
  "peerDependencies": {
32
31
  "@izara_project/izara-core-library-service-schemas": "^1.0.157",
@@ -2,146 +2,27 @@ import path from 'path';
2
2
  import fs from 'fs/promises';
3
3
  import ejs from 'ejs';
4
4
  import { fileURLToPath } from 'url';
5
- import { getFlowsForGeneration } from '../src/generatedCode/Flow/_shared/shared/flowSelection.js';
5
+ import {
6
+ detectRoleNames,
7
+ FLOW_OBJECTS_ROLE,
8
+ PROC_FIND_DATA_ROLE,
9
+ FLOW_RELATIONSHIP_ROLE
10
+ } from '../src/generatedCode/Flow/_shared/shared/roleNameDetection.js';
6
11
 
7
12
  const __filename = fileURLToPath(import.meta.url);
8
13
  const __dirname = path.dirname(__filename);
9
14
 
10
- const FLOW_OBJECTS_ROLE = 'FlowObjectsRole';
11
- const CRUD_FLOW_TAGS = new Set(['Create', 'Update', 'Delete', 'Get']);
12
- const RBAC_FLOW_TAGS = new Set([
13
- 'CreateTargetRole',
14
- 'ListTargetRole',
15
- 'DeleteTargetRole',
16
- 'CreateRolePermissions',
17
- 'ListRolePermissions',
18
- 'DeleteRolePermissions',
19
- 'CreateUserRole',
20
- 'ListUserInRoles',
21
- 'DeleteUserFromRole'
22
- ]);
23
- const REL_FLOW_TAGS = new Set([
24
- 'CreateRelationship',
25
- 'UpdateRelationship',
26
- 'DeleteRelationship',
27
- 'GetRelationship',
28
- 'ChangeRelationship',
29
- 'MoveRelationship'
30
- ]);
31
-
32
- function upperCase(str) {
33
- if (!str) return str;
34
- return str.charAt(0).toUpperCase() + str.slice(1);
35
- }
36
-
37
- function detectRoleNames(allSchemas) {
38
- const flows = getFlowsForGeneration(allSchemas);
39
- const roleNames = new Set(['ProcFindDataRole']);
40
-
41
- for (const flow of flows) {
42
- if (!flow.flowTag) continue;
43
-
44
- const upperFlowTag = upperCase(flow.flowTag);
45
-
46
- if (CRUD_FLOW_TAGS.has(upperFlowTag)) {
47
- roleNames.add(FLOW_OBJECTS_ROLE);
48
- continue;
49
- }
50
-
51
- if (RBAC_FLOW_TAGS.has(upperFlowTag) || upperFlowTag.endsWith('RbacFlow')) {
52
- roleNames.add('FlowRbacRole');
53
- continue;
54
- }
55
-
56
- if (REL_FLOW_TAGS.has(upperFlowTag)) {
57
- roleNames.add('FlowRelationshipRole');
58
- continue;
59
- }
60
-
61
- if ((flow.event || []).includes('ownTopic')) {
62
- roleNames.add('WebSocketMainRole');
63
- // ponytail: removed continue; so custom flows with ownTopic get their own specific role
64
- }
65
-
66
- roleNames.add(`${upperFlowTag}Role`);
67
- }
68
-
69
- return { flows, roleNames };
70
- }
71
-
72
- export async function generateInitialSetup(allSchemas, options) {
73
- console.log(' [InitialSetupGenerator] Generating Initial Setup Scripts...');
74
-
75
- const { flows, roleNames } = detectRoleNames(allSchemas);
76
- const setupDir = path.join(options.outputPath, 'app', 'initial_setup');
77
- const sourceDir = path.join(setupDir, 'generatedCode', 'source');
78
- const hookDir = path.join(setupDir, 'generatedCode', 'hook');
79
-
80
- await fs.mkdir(sourceDir, { recursive: true });
81
- await fs.mkdir(hookDir, { recursive: true });
82
-
83
- const tableNames = new Set();
84
- const tableNameTpl = await fs.readFile(
85
- path.join(__dirname, 'templates', 'InitialSetup_TableName.ejs'),
86
- 'utf-8'
87
- );
88
- const tableNameContent = ejs.render(tableNameTpl, {
89
- tableNames: Array.from(tableNames)
90
- });
91
- await fs.writeFile(
92
- path.join(sourceDir, 'generatedTableNameConfig.js'),
93
- tableNameContent,
94
- 'utf-8'
95
- );
96
-
97
- const hardcodedSubscriptions = [
98
- {
99
- serviceTag: 'GraphHandler',
100
- topicName: 'UpdateRelationship_Out',
101
- sqsEndpoint: 'UpdateRelationshipCompleteHdrSqs'
102
- },
103
- {
104
- serviceTag: 'GraphHandler',
105
- topicName: 'CreateRelationship_Out',
106
- sqsEndpoint: 'CreateRelationshipCompleteHdrSqs'
107
- },
108
- {
109
- serviceTag: 'GraphHandler',
110
- topicName: 'DeleteRelationship_Out',
111
- sqsEndpoint: 'DeleteRelationshipCompleteHdrSqs'
112
- },
113
- {
114
- serviceTag: 'GraphHandler',
115
- topicName: 'UpdateNode_Out',
116
- sqsEndpoint: 'UpdateCompleteHdrSqs'
117
- },
118
- {
119
- serviceTag: 'GraphHandler',
120
- topicName: 'CreateNode_Out',
121
- sqsEndpoint: 'CreateCompleteHdrSqs'
122
- },
123
- {
124
- serviceTag: 'GraphHandler',
125
- topicName: 'ChangeRelationshipType_Out',
126
- sqsEndpoint: 'ChangeRelationshipCompleteHdrSqs'
127
- },
128
- {
129
- serviceTag: 'GraphHandler',
130
- topicName: 'MoveRelationship_Out',
131
- sqsEndpoint: 'MoveRelationshipCompleteHdrSqs'
132
- }
133
- ];
134
- const snsTpl = await fs.readFile(
135
- path.join(__dirname, 'templates', 'InitialSetup_SnsService.ejs'),
136
- 'utf-8'
137
- );
138
- const snsContent = ejs.render(snsTpl, { subscriptions: hardcodedSubscriptions });
139
- await fs.writeFile(
140
- path.join(sourceDir, 'generatedSnsServiceConfig.js'),
141
- snsContent,
142
- 'utf-8'
143
- );
144
-
15
+ const HARDCODED_SUBSCRIPTIONS = [
16
+ { serviceTag: 'GraphHandler', topicName: 'UpdateRelationship_Out', sqsEndpoint: 'UpdateRelationshipCompleteHdrSqs' },
17
+ { serviceTag: 'GraphHandler', topicName: 'CreateRelationship_Out', sqsEndpoint: 'CreateRelationshipCompleteHdrSqs' },
18
+ { serviceTag: 'GraphHandler', topicName: 'DeleteRelationship_Out', sqsEndpoint: 'DeleteRelationshipCompleteHdrSqs' },
19
+ { serviceTag: 'GraphHandler', topicName: 'UpdateNode_Out', sqsEndpoint: 'UpdateCompleteHdrSqs' },
20
+ { serviceTag: 'GraphHandler', topicName: 'CreateNode_Out', sqsEndpoint: 'CreateCompleteHdrSqs' },
21
+ { serviceTag: 'GraphHandler', topicName: 'ChangeRelationshipType_Out', sqsEndpoint: 'ChangeRelationshipCompleteHdrSqs' },
22
+ { serviceTag: 'GraphHandler', topicName: 'MoveRelationship_Out', sqsEndpoint: 'MoveRelationshipCompleteHdrSqs' }
23
+ ];
24
+
25
+ function buildLambdaRoles(flows, roleNames) {
145
26
  const lambdaRoles = [
146
27
  {
147
28
  objectType: FLOW_OBJECTS_ROLE,
@@ -170,7 +51,7 @@ export async function generateInitialSetup(allSchemas, options) {
170
51
  ]
171
52
  },
172
53
  {
173
- objectType: 'ProcFindDataRole',
54
+ objectType: PROC_FIND_DATA_ROLE,
174
55
  additionalResourcePermission: [
175
56
  {
176
57
  effect: 'Allow',
@@ -222,18 +103,37 @@ export async function generateInitialSetup(allSchemas, options) {
222
103
  });
223
104
  }
224
105
 
225
- const lambdaTpl = await fs.readFile(
226
- path.join(__dirname, 'templates', 'InitialSetup_LambdaRole.ejs'),
227
- 'utf-8'
228
- );
229
- const lambdaContent = ejs.render(lambdaTpl, { datas: lambdaRoles });
230
- await fs.writeFile(
231
- path.join(sourceDir, 'generatedLambdaRoleServiceConfig.js'),
232
- lambdaContent,
233
- 'utf-8'
234
- );
106
+ if (roleNames && roleNames.has(FLOW_RELATIONSHIP_ROLE)) {
107
+ lambdaRoles.push({
108
+ objectType: FLOW_RELATIONSHIP_ROLE,
109
+ additionalResourcePermission: [
110
+ {
111
+ effect: 'Allow',
112
+ action: { lambda: ['InvokeFunction'] },
113
+ resource: [
114
+ 'arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, "GetRelationshipHdrInv" ,"GraphHandler")}'
115
+ ]
116
+ },
117
+ {
118
+ effect: 'Allow',
119
+ action: { sns: ['Publish'] },
120
+ resource: [
121
+ 'arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, "CreateRelationship_In", "GraphHandler")}',
122
+ 'arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, "UpdateRelationship_In", "GraphHandler")}',
123
+ 'arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, "DeleteRelationship_In", "GraphHandler")}',
124
+ 'arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, "ChangeRelationship_In", "GraphHandler")}',
125
+ 'arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, "MoveRelationship_In", "GraphHandler")}'
126
+ ]
127
+ }
128
+ ]
129
+ });
130
+ }
235
131
 
236
- const roleNameConfigContent = `export function generatedIntTestRoleNameConfig() {
132
+ return lambdaRoles;
133
+ }
134
+
135
+ function buildRoleNameConfigContent(roleNames) {
136
+ return `export function generatedIntTestRoleNameConfig() {
237
137
  let roleNameConfigIntTest = [
238
138
  ${Array.from(roleNames)
239
139
  .sort()
@@ -243,7 +143,57 @@ ${Array.from(roleNames)
243
143
  return roleNameConfigIntTest;
244
144
  }
245
145
  `;
246
- await fs.writeFile(path.join(sourceDir, 'RoleNameConfig.js'), roleNameConfigContent, 'utf-8');
146
+ }
147
+
148
+ export async function generateInitialSetup(allSchemas, options) {
149
+ console.log(' [InitialSetupGenerator] Generating Initial Setup Scripts...');
150
+
151
+ const { flows, roleNames } = detectRoleNames(allSchemas);
152
+ const setupDir = path.join(options.outputPath, 'app', 'initial_setup');
153
+ const sourceDir = path.join(setupDir, 'generatedCode', 'source');
154
+ const hookDir = path.join(setupDir, 'generatedCode', 'hook');
155
+
156
+ await fs.mkdir(sourceDir, { recursive: true });
157
+ await fs.mkdir(hookDir, { recursive: true });
158
+
159
+ const tableNameTpl = await fs.readFile(
160
+ path.join(__dirname, 'templates', 'InitialSetup_TableName.ejs'),
161
+ 'utf-8'
162
+ );
163
+ const tableNameContent = ejs.render(tableNameTpl, { tableNames: [] });
164
+ await fs.writeFile(
165
+ path.join(sourceDir, 'generatedTableNameConfig.js'),
166
+ tableNameContent,
167
+ 'utf-8'
168
+ );
169
+
170
+ const snsTpl = await fs.readFile(
171
+ path.join(__dirname, 'templates', 'InitialSetup_SnsService.ejs'),
172
+ 'utf-8'
173
+ );
174
+ const snsContent = ejs.render(snsTpl, { subscriptions: HARDCODED_SUBSCRIPTIONS });
175
+ await fs.writeFile(
176
+ path.join(sourceDir, 'generatedSnsServiceConfig.js'),
177
+ snsContent,
178
+ 'utf-8'
179
+ );
180
+
181
+ const lambdaTpl = await fs.readFile(
182
+ path.join(__dirname, 'templates', 'InitialSetup_LambdaRole.ejs'),
183
+ 'utf-8'
184
+ );
185
+ const lambdaContent = ejs.render(lambdaTpl, { datas: buildLambdaRoles(flows, roleNames) });
186
+ await fs.writeFile(
187
+ path.join(sourceDir, 'generatedLambdaRoleServiceConfig.js'),
188
+ lambdaContent,
189
+ 'utf-8'
190
+ );
191
+
192
+ await fs.writeFile(
193
+ path.join(sourceDir, 'RoleNameConfig.js'),
194
+ buildRoleNameConfigContent(roleNames),
195
+ 'utf-8'
196
+ );
247
197
 
248
- console.log(` [InitialSetupGenerator] Wrote initial_setup scripts to ${sourceDir}`);
198
+ console.log(` [InitialSetupGenerator] Wrote initial_setup scripts to ${sourceDir}`);
249
199
  }
@@ -8,33 +8,40 @@ const _izContext = { logger: Logger };
8
8
 
9
9
  export function generatedLambdaRole() {
10
10
  const externalLambdaRole = [
11
- <% datas.forEach((data) => { %>
11
+ <% datas.forEach((data, dataIdx) => { -%>
12
12
  {
13
- roleName: "<%- data.objectType %>",
13
+ roleName: '<%- data.objectType.replace(/Role$/, '') %>',
14
14
  statement: [
15
- <% data.additionalResourcePermission.forEach((resourcePermission, resourceIdx) => { %>
15
+ <% data.additionalResourcePermission.forEach((resourcePermission, resourceIdx) => { -%>
16
16
  {
17
- "Sid": "<%- data.objectType %>Test<%- resourceIdx %>",
18
- "Effect": "<%- resourcePermission.effect %>",
19
- "Action": [
20
- <% Object.keys(resourcePermission.action).forEach((resourcePerAction) => { %>
21
- <% resourcePermission.action[resourcePerAction].forEach((permission) => { %>
22
- "<%- resourcePerAction %>:<%- permission %>",
23
- <% }) %>
24
- <% }) %>
25
- ],
26
- "Resource": [
27
- <% resourcePermission.resource.forEach((resource) => { %>
28
- `<%- resource %>`,
29
- <% }) %>
17
+ Sid: '<%- data.objectType %>Test<%- resourceIdx %>',
18
+ Effect: '<%- resourcePermission.effect %>',
19
+ Action: [<%
20
+ const actions = [];
21
+ Object.keys(resourcePermission.action).forEach((service) => {
22
+ resourcePermission.action[service].forEach((act) => {
23
+ actions.push(`'${service}:${act}'`);
24
+ });
25
+ });
26
+ %><%- actions.join(', ') %>],
27
+ Resource: [
28
+ <%
29
+ const resources = [];
30
+ resourcePermission.resource.forEach((res) => {
31
+ const formattedRes = res.replace(/"/g, "'");
32
+ resources.push(` \`${formattedRes}\``);
33
+ });
34
+ %><%- resources.join(',\n') %>
30
35
  ]
31
- },
32
- <% }) %>
36
+ }<%= (resourceIdx === data.additionalResourcePermission.length - 1) ? '' : ',\n\n' -%>
37
+ <% }) -%>
38
+
33
39
  //(<create<%- data.objectType.replace(/Role$/, '') %>Role>)
34
40
  //(</create<%- data.objectType.replace(/Role$/, '') %>Role>)
35
41
  ]
36
- },
37
- <% }) %>
42
+ }<%= (dataIdx === datas.length - 1) ? '' : ',\n\n' -%>
43
+ <% }) -%>
44
+
38
45
  //(<additionalRole>)
39
46
  //(</additionalRole>)
40
47
  ];
@@ -3,13 +3,17 @@ import path from 'path';
3
3
  import ejs from 'ejs';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { getFlowsForGeneration } from '../src/generatedCode/Flow/_shared/shared/flowSelection.js';
6
- import { isRbacFlow } from '../src/generatedCode/Flow/_shared/shared/flowClassifier.js';
6
+ import { isCrudFlow, isRbacFlow, isRelationshipFlow } from '../src/generatedCode/Flow/_shared/shared/flowClassifier.js';
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
 
11
11
  export async function generateFunctionYaml(allSchemas, options) {
12
12
  console.log(' [FunctionYamlGenerator] Generating Function YAMLs...');
13
+ const serviceName = options.serviceName;
14
+ if (!serviceName) {
15
+ throw new Error('[Developer Error] serviceName is missing in FunctionYamlGenerator. Ensure serverless.config.yml has main_config.iz_serviceName.');
16
+ }
13
17
 
14
18
  const slsYamlDir = path.join(
15
19
  options.outputPath,
@@ -44,15 +48,6 @@ export async function generateFunctionYaml(allSchemas, options) {
44
48
  };
45
49
  const flows = getFlowsForGeneration(allSchemas);
46
50
 
47
- const CRUD_FLOW_TAGS = ['Create', 'Update', 'Delete', 'Get'];
48
- const REL_FLOW_TAGS = [
49
- 'createRelationship',
50
- 'updateRelationship',
51
- 'deleteRelationship',
52
- 'getRelationship',
53
- 'changeRelationship',
54
- 'moveRelationship'
55
- ];
56
51
 
57
52
  let perActionYaml = '';
58
53
  let relActionYaml = '';
@@ -123,12 +118,8 @@ export async function generateFunctionYaml(allSchemas, options) {
123
118
  const handlerBaseName = splitFileName[0]; // e.g. ProcessCreate or CreateComplete
124
119
  const handlerSuffix = splitFileName[1]; // e.g. HdrSqs, HdrApi, HdrInv
125
120
 
126
- const isCrud = CRUD_FLOW_TAGS.map(t => t.toLowerCase()).includes(
127
- flowTag.toLowerCase()
128
- );
129
- const isRel = REL_FLOW_TAGS.map(t => t.toLowerCase()).includes(
130
- flowTag.toLowerCase()
131
- );
121
+ const isCrud = isCrudFlow(flowTag);
122
+ const isRel = isRelationshipFlow(flowTag);
132
123
  const isRbac =
133
124
  isRbacFlow(flowTag) ||
134
125
  String(flowTag || '')
@@ -210,7 +201,7 @@ export async function generateFunctionYaml(allSchemas, options) {
210
201
  const routesList = isCrud ? crudApiRoutes : isRel ? relApiRoutes : [];
211
202
  const actionPath = flowTag.toLowerCase().replace('relationship', '');
212
203
  apiRoutes = routesList.map(r => ({
213
- path: `/TST/${r.objectName}/${actionPath}`,
204
+ path: `/${serviceName}/${r.objectName}/${actionPath}`,
214
205
  upperName: r.upperName
215
206
  }));
216
207
  }
@@ -2,40 +2,19 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import ejs from 'ejs';
4
4
  import { fileURLToPath } from 'url';
5
- import { getFlowsForGeneration } from '../src/generatedCode/Flow/_shared/shared/flowSelection.js';
5
+ import { detectRoleNames } from '../src/generatedCode/Flow/_shared/shared/roleNameDetection.js';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
9
9
 
10
- const FLOW_OBJECTS_ROLE = 'FlowObjectsRole';
11
- const CRUD_FLOW_TAGS = new Set(['Create', 'Update', 'Delete', 'Get']);
12
- const RBAC_FLOW_TAGS = new Set([
13
- 'CreateTargetRole',
14
- 'ListTargetRole',
15
- 'DeleteTargetRole',
16
- 'CreateRolePermissions',
17
- 'ListRolePermissions',
18
- 'DeleteRolePermissions',
19
- 'CreateUserRole',
20
- 'ListUserInRoles',
21
- 'DeleteUserFromRole'
22
- ]);
23
- const REL_FLOW_TAGS = new Set([
24
- 'CreateRelationship',
25
- 'UpdateRelationship',
26
- 'DeleteRelationship',
27
- 'GetRelationship',
28
- 'ChangeRelationship',
29
- 'MoveRelationship'
30
- ]);
31
-
32
- function upperCase(str) {
33
- if (!str) return str;
34
- return str.charAt(0).toUpperCase() + str.slice(1);
35
- }
10
+ const FALLBACK_TEMPLATE = `# createIamRole
11
+ <% roleNameConfigs.forEach(function(roleNameConfig){ _%>
12
+ - arn:aws:iam::\${self:custom.iz_accountId}:role/\${self:custom.iz_resourcePrefix}<%- roleNameConfig %>
13
+ <% }) _%>
14
+ `;
36
15
 
37
16
  export async function generateRoleNameConfig(allSchemas, options) {
38
- console.log(' [RoleNameConfigGenerator] Generating IAM Role Configurations...');
17
+ console.log(' [RoleNameConfigGenerator] Generating IAM Role Configurations...');
39
18
 
40
19
  const resourceYamlDir = path.join(
41
20
  options.outputPath,
@@ -46,62 +25,24 @@ export async function generateRoleNameConfig(allSchemas, options) {
46
25
  );
47
26
  await fs.mkdir(resourceYamlDir, { recursive: true });
48
27
 
49
- const roleNameConfigs = new Set();
50
- const flows = getFlowsForGeneration(allSchemas);
51
-
52
- for (const flow of flows) {
53
- if (!flow.flowTag) continue;
54
-
55
- const upperFlowTag = upperCase(flow.flowTag);
56
-
57
- if (CRUD_FLOW_TAGS.has(upperFlowTag)) {
58
- roleNameConfigs.add(FLOW_OBJECTS_ROLE);
59
- continue;
60
- }
61
-
62
- if (RBAC_FLOW_TAGS.has(upperFlowTag) || upperFlowTag.endsWith('RbacFlow')) {
63
- roleNameConfigs.add('FlowRbacRole');
64
- continue;
65
- }
66
-
67
- if (REL_FLOW_TAGS.has(upperFlowTag)) {
68
- roleNameConfigs.add('FlowRelationshipRole');
69
- continue;
70
- }
71
-
72
- if ((flow.event || []).includes('ownTopic')) {
73
- roleNameConfigs.add('WebSocketMainRole');
74
- // ponytail: removed continue; so custom flows with ownTopic get their own specific role
75
- }
76
-
77
- roleNameConfigs.add(`${upperFlowTag}Role`);
78
- }
79
-
80
- roleNameConfigs.add('ProcFindDataRole');
81
-
28
+ const { roleNames } = detectRoleNames(allSchemas);
29
+ const roleNameConfigs = Array.from(roleNames);
82
30
  const templatePath = path.join(__dirname, 'templates', 'RoleNameConfig_Yaml.ejs');
83
- let templateString;
84
31
 
32
+ let templateString;
85
33
  try {
86
34
  templateString = await fs.readFile(templatePath, 'utf-8');
87
35
  } catch {
88
- templateString = `# for createIamRole
89
- <% roleNameConfigs.forEach(function(roleNameConfig){ _%>
90
- - arn:aws:iam::\${self:custom.iz_accountId}:role/\${self:custom.iz_resourcePrefix}<%- roleNameConfig %>
91
- <% }) _%>
92
- `;
36
+ templateString = FALLBACK_TEMPLATE;
93
37
  await fs.mkdir(path.dirname(templatePath), { recursive: true });
94
38
  await fs.writeFile(templatePath, templateString, 'utf-8');
95
39
  }
96
40
 
97
- const fileContent = ejs.render(templateString, {
98
- roleNameConfigs: Array.from(roleNameConfigs)
99
- });
100
-
41
+ const fileContent = ejs.render(templateString, { roleNameConfigs });
101
42
  const outputPath = path.join(resourceYamlDir, 'role-name-config.yml');
102
43
  await fs.writeFile(outputPath, fileContent, 'utf-8');
103
44
 
104
45
  console.log(
105
- ` [RoleNameConfigGenerator] Wrote ${roleNameConfigs.size} role configs to ${outputPath}`
46
+ ` [RoleNameConfigGenerator] Wrote ${roleNameConfigs.length} role configs to ${outputPath}`
106
47
  );
107
48
  }
@@ -36,8 +36,9 @@ export async function emitManagedPolicies(registry, options) {
36
36
  const tpl = await fs.readFile(templatePath, 'utf8');
37
37
 
38
38
  for (const [roleName, { statements }] of Object.entries(grouped)) {
39
- const content = ejs.render(tpl, { roleName, statements });
40
- const outFile = path.join(outDir, `${roleName}Policy.yml`);
39
+ const policyName = roleName.replace(/role$/i, '');
40
+ const content = ejs.render(tpl, { roleName, policyName, statements });
41
+ const outFile = path.join(outDir, `${policyName}Policy.yml`);
41
42
  await fs.writeFile(outFile, content, 'utf8');
42
43
  }
43
44
 
@@ -18,7 +18,7 @@ describe('emitManagedPolicies', () => {
18
18
  reg.add('RegisterRole', 'dynamodb:GetItem', 'arn:aws:dynamodb:*:*:table/x');
19
19
  await emitManagedPolicies(reg, { outputPath: tmpDir });
20
20
  const files = await fs.readdir(path.join(tmpDir, 'app', 'sls_yaml', 'generatedCode', 'source', 'iam-managed-policies'));
21
- expect(files.sort()).toEqual(['FindDataRolePolicy.yml', 'RegisterRolePolicy.yml']);
21
+ expect(files.sort()).toEqual(['FindDataPolicy.yml', 'RegisterPolicy.yml']);
22
22
  });
23
23
 
24
24
  it('emits empty dir + no files when registry is empty', async () => {
@@ -34,7 +34,7 @@ describe('emitManagedPolicies', () => {
34
34
  reg.add('FindDataRole', 's3:GetObject', 'arn:aws:s3:::a/*');
35
35
  await emitManagedPolicies(reg, { outputPath: tmpDir });
36
36
  const content = await fs.readFile(
37
- path.join(tmpDir, 'app', 'sls_yaml', 'generatedCode', 'source', 'iam-managed-policies', 'FindDataRolePolicy.yml'),
37
+ path.join(tmpDir, 'app', 'sls_yaml', 'generatedCode', 'source', 'iam-managed-policies', 'FindDataPolicy.yml'),
38
38
  'utf8'
39
39
  );
40
40
  expect(content).toContain('Type: AWS::IAM::ManagedPolicy');
@@ -47,7 +47,7 @@ describe('emitManagedPolicies', () => {
47
47
  reg.add('FindDataRole', 's3:GetObject', 'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}/*');
48
48
  await emitManagedPolicies(reg, { outputPath: tmpDir });
49
49
  const content = await fs.readFile(
50
- path.join(tmpDir, 'app', 'sls_yaml', 'generatedCode', 'source', 'iam-managed-policies', 'FindDataRolePolicy.yml'),
50
+ path.join(tmpDir, 'app', 'sls_yaml', 'generatedCode', 'source', 'iam-managed-policies', 'FindDataPolicy.yml'),
51
51
  'utf8'
52
52
  );
53
53
  expect(content).toContain('${self:custom.iz_serviceSchemaBucketName}');
@@ -1,8 +1,8 @@
1
1
  Resources:
2
- <%- roleName %>Policy:
2
+ <%- policyName %>Policy:
3
3
  Type: AWS::IAM::ManagedPolicy
4
4
  Properties:
5
- ManagedPolicyName: ${self:custom.iz_resourcePrefix}<%- roleName %>Policy
5
+ ManagedPolicyName: ${self:custom.iz_resourcePrefix}<%- policyName %>Policy
6
6
  Description: "Managed policy for <%- roleName %> (auto-generated by PolicyEmitter)"
7
7
  PolicyDocument:
8
8
  Version: "2012-10-17"
@@ -18,7 +18,7 @@ Resources:
18
18
  #<#<%- roleName %>ManagedPolicyArns#>
19
19
  #<#/<%- roleName %>ManagedPolicyArns#>
20
20
  Policies:
21
- - PolicyName: ${self:custom.iz_resourcePrefix}<%- roleName %>Policy
21
+ - PolicyName: ${self:custom.iz_resourcePrefix}<%- roleName.replace(/role$/i, '') %>Policy
22
22
  PolicyDocument:
23
23
  Version: "2012-10-17"
24
24
  Statement:
@@ -80,7 +80,7 @@ export async function generateEndpointsFlow(allSchemas, options) {
80
80
  const beforeLogicalConfigs = generateCode.filter(c => c.codeHookTag === 'beforeLogical');
81
81
  if (beforeLogicalConfigs.length > 0) {
82
82
  const beforeLogicalTemplateStr = await fs.readFile(
83
- path.join(templateDir, 'FlowEndpointBeforeLogical_Main.ejs'),
83
+ path.join(templateDir, 'step', 'FlowEndpointBeforeLogical_Main.ejs'),
84
84
  'utf-8'
85
85
  );
86
86
  const beforeLogicalFileName = `${upperAction}BeforeLogical_Main`;
@@ -41,9 +41,9 @@ const { TOPIC_NAME_GENERATE_CODE, TOPIC_NAME_GRAPH_HANDLER } = consts;
41
41
  const PREFIX = {
42
42
  ONE: 'one',
43
43
  MANY: 'many',
44
- CREATE_OBJECT_ASYNC: 'createObjectAsyncEndpoint',
45
- CREATE_OBJECT_ASYNC_COMPLETE: 'createObjectAsyncEndpointComplete',
46
- CREATE_OBJECT_EXTERNAL_TOPIC: 'createObjectExternalTopicEndpoint'
44
+ CREATE_OBJECT_ASYNC: 'CreateAsyncEndpoint',
45
+ CREATE_OBJECT_ASYNC_COMPLETE: 'CreateAsyncEndpointComplete',
46
+ CREATE_OBJECT_EXTERNAL_TOPIC: 'CreateExternalTopicEndpoint'
47
47
  };
48
48
 
49
49
  /**
@@ -256,13 +256,13 @@ export async function <%- flowTag %>MainLogical(
256
256
 
257
257
  // ── Create awaiting multiple steps ────────────────────────────────────────
258
258
  _izContext.logger.debug('not api will create awaitingStep');
259
- await asyncFlowSharedLib.createAwaitingMultipleStepsWithAdditionalAttributes(
259
+ await asyncFlowSharedLib.createAwaitingMultipleSteps(
260
260
  _izContext,
261
- allAwaitingStepsId,
262
261
  asyncFlowSharedLib.createPendingStepId(
263
262
  _izContext.uniqueRequestId,
264
263
  PREFIX.CREATE_OBJECT_ASYNC_COMPLETE
265
- )
264
+ ),
265
+ allAwaitingStepsId,
266
266
  );
267
267
 
268
268
  // ── Process graph objects ─────────────────────────────────────────────────