@izara_project/izara-core-generate-service-code 1.0.55 → 1.0.57

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/codeGenerators/app/initial_setup/InitialSetupGenerator.js +223 -123
  3. package/src/codeGenerators/app/initial_setup/templates/InitialSetup_LambdaRole.ejs +6 -6
  4. package/src/codeGenerators/app/sls_yaml/FindDataYamlGenerator.js +1 -1
  5. package/src/codeGenerators/app/sls_yaml/FunctionYamlGenerator.js +299 -197
  6. package/src/codeGenerators/app/sls_yaml/RoleNameConfigGenerator.js +84 -60
  7. package/src/codeGenerators/app/sls_yaml/SharedResourceYamlGenerator.js +304 -271
  8. package/src/codeGenerators/app/sls_yaml/__tests__/SharedResourceYamlGenerator.test.js +91 -32
  9. package/src/codeGenerators/app/sls_yaml/templates/SharedResource_Yaml.ejs +2 -2
  10. package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/EndpointsGenerator.js +1 -1
  11. package/src/codeGenerators/app/src/generatedCode/Flow/FlowRbac/templates/rbac/FlowRbac_Main.ejs +252 -0
  12. package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/RelationshipFlowGenerator.js +19 -3
  13. package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessCreateRelationship_Main.ejs +1 -1
  14. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/WebSocketGenerator.js +4 -4
  15. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowClassifier.js +3 -2
  16. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowMainFunctionBase.js +12 -5
  17. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowValidator.js +1 -1
  18. package/src/codeGenerators/app/src/generatedCode/libs/templates/Consts.ejs +23 -23
  19. package/src/codeGenerators/resource/sls_yaml/DynamoDBGenerator.js +1 -1
  20. package/src/codeGenerators/resource/sls_yaml/FlowOutGenerator.js +3 -6
  21. package/src/codeGenerators/resource/sls_yaml/FlowResourceYamlGenerator.js +29 -10
  22. package/src/codeGenerators/resource/sls_yaml/templates/SystemDynamoDB_Yaml.ejs +64 -0
  23. package/src/generate.js +1 -1
  24. package/src/generateCode.js +14 -11
  25. package/src/generateSchema.js +1 -1
  26. package/src/schemaGenerators/app/src/schemas/FlowSchemas/FlowSchemaGenerator.js +0 -1
  27. package/src/schemaGenerators/app/src/schemas/FlowSchemas/RbacFlowSchemaGenerator.js +16 -2
  28. package/src/schemaGenerators/app/src/schemas/FlowSchemas/RelationshipFlowSchemaGenerator.js +2 -2
  29. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/DynamicFlowSchemaTemplate.ejs +12 -12
  30. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/DynamicRbacFlowSchemaTemplate.ejs +1 -0
  31. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/UserRbacFlowSchemaTemplate.ejs +22 -0
  32. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/.gitkeep +0 -0
  33. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/templates/FlowEndpointBeforeLogical_Main.ejs +0 -0
  34. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/templates/crud/CreateEndpoint_Main.ejs +0 -0
  35. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/templates/crud/DeleteEndpoint_Main.ejs +0 -0
  36. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/templates/crud/GetEndpoint_Main.ejs +0 -0
  37. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowEndpoints → FlowObjects}/templates/crud/UpdateEndpoint_Main.ejs +0 -0
  38. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessChangeRelationshipComplete_Main.ejs +0 -0
  39. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessChangeRelationship_Main.ejs +0 -0
  40. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessCreateRelationshipComplete_Main.ejs +0 -0
  41. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessDeleteRelationshipComplete_Main.ejs +0 -0
  42. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessDeleteRelationship_Main.ejs +0 -0
  43. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessGetRelationshipComplete_Main.ejs +0 -0
  44. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessGetRelationship_Main.ejs +0 -0
  45. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessMoveRelationshipComplete_Main.ejs +0 -0
  46. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessMoveRelationship_Main.ejs +0 -0
  47. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessUpdateRelationshipComplete_Main.ejs +0 -0
  48. /package/src/codeGenerators/app/src/generatedCode/Flow/{FlowRelationshipEndpoints → FlowRelationships}/templates/relationship/ProcessUpdateRelationship_Main.ejs +0 -0
@@ -34,7 +34,10 @@ describe('generateSharedResourceYaml', () => {
34
34
  'Resources:',
35
35
  ' AwaitingStepTable:',
36
36
  ' Properties:',
37
- ' TableName: ${self:custom.iz_resourcePrefix}AwaitingStep'
37
+ ' TableName: ${self:custom.iz_resourcePrefix}AwaitingStep',
38
+ ' WebSocketTaskTable:',
39
+ ' Properties:',
40
+ ' TableName: ${self:custom.iz_resourcePrefix}WebSocketTask'
38
41
  ].join('\n'),
39
42
  'utf8'
40
43
  );
@@ -58,7 +61,13 @@ describe('generateSharedResourceYaml', () => {
58
61
  ' QueueName: ${self:custom.iz_resourcePrefix}ReservationsSqsDLQ',
59
62
  ' InReservations:',
60
63
  ' Properties:',
61
- ' TopicName: ${self:custom.iz_serviceTag}_${self:custom.iz_stage}_Reservations_In'
64
+ ' TopicName: ${self:custom.iz_serviceTag}_${self:custom.iz_stage}_ReservationsComplete_In',
65
+ ' InReservationsMain:',
66
+ ' Properties:',
67
+ ' TopicName: ${self:custom.iz_serviceTag}_${self:custom.iz_stage}_Reservations_In',
68
+ ' InCreateRolePermissions:',
69
+ ' Properties:',
70
+ ' TopicName: ${self:custom.iz_serviceTag}_${self:custom.iz_stage}_CreateRolePermissions_In'
62
71
  ].join('\n'),
63
72
  'utf8'
64
73
  );
@@ -92,85 +101,135 @@ describe('generateSharedResourceYaml', () => {
92
101
  ),
93
102
  [
94
103
  'ReservationsCompleteSqs:',
104
+ ' role: WebSocketMainRole',
95
105
  ' events:',
96
106
  ' - sqs:',
97
107
  ' arn: arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}ReservationsCompleteSqs'
98
108
  ].join('\n'),
99
109
  'utf8'
100
110
  );
111
+
112
+ await fs.writeFile(
113
+ path.join(
114
+ tmpDir,
115
+ 'app',
116
+ 'sls_yaml',
117
+ 'generatedCode',
118
+ 'source',
119
+ 'role-name-config.yml'
120
+ ),
121
+ [
122
+ '# for createIamRole',
123
+ '- arn:aws:iam::${self:custom.iz_accountId}:role/${self:custom.iz_resourcePrefix}FlowObjectsRole',
124
+ '- arn:aws:iam::${self:custom.iz_accountId}:role/${self:custom.iz_resourcePrefix}ProcFindDataRole',
125
+ '- arn:aws:iam::${self:custom.iz_accountId}:role/${self:custom.iz_resourcePrefix}RegisterRole',
126
+ '- arn:aws:iam::${self:custom.iz_accountId}:role/${self:custom.iz_resourcePrefix}ReservationsRole',
127
+ '- arn:aws:iam::${self:custom.iz_accountId}:role/${self:custom.iz_resourcePrefix}WebSocketMainRole'
128
+ ].join('\n'),
129
+ 'utf8'
130
+ );
101
131
  });
102
132
 
103
- it('registers baseline IAM statements with explicit resources', async () => {
133
+ it('registers scoped IAM statements and avoids broad defaults', async () => {
104
134
  await generateSharedResourceYaml(
105
135
  { objects: [{ objectType: 'Foo' }], flows: [] },
106
136
  { outputPath: tmpDir }
107
137
  );
108
138
 
109
139
  const grouped = policyRegistry.toGroupedByRole();
140
+ const resources = grouped.FlowObjectsRole.statements.flatMap(
141
+ statement => statement.resource
142
+ );
143
+ const actions = grouped.FlowObjectsRole.statements.flatMap(
144
+ statement => statement.action
145
+ );
146
+
110
147
  expect(Object.keys(grouped).sort()).toEqual(
111
148
  expect.arrayContaining([
112
- 'PerActionEndpointRole',
149
+ 'FlowObjectsRole',
113
150
  'ProcFindDataRole',
114
- 'RegisterRole'
151
+ 'RegisterRole',
152
+ 'ReservationsRole',
153
+ 'WebSocketMainRole'
115
154
  ])
116
155
  );
117
-
118
- const resources = grouped.PerActionEndpointRole.statements.flatMap(
119
- statement => statement.resource
120
- );
121
-
122
156
  expect(resources).toContain(
123
157
  'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}/perServiceSchemas/*'
124
158
  );
125
159
  expect(resources).toContain(
126
160
  'arn:aws:dynamodb:${self:custom.iz_region}:${self:custom.iz_accountId}:table/${self:custom.iz_resourcePrefix}AwaitingStep'
127
161
  );
128
- expect(resources).toContain(
129
- 'arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}ReservationsSqs'
162
+ const reservationResources = grouped.ReservationsRole.statements.flatMap(
163
+ statement => statement.resource
130
164
  );
131
- expect(resources).toContain(
132
- 'arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}ReservationsCompleteSqs'
165
+ const reservationActions = grouped.ReservationsRole.statements.flatMap(
166
+ statement => statement.action
133
167
  );
134
- expect(resources).toContain(
168
+
169
+ expect(reservationResources).toContain(
135
170
  'arn:aws:sns:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_serviceTag}_${self:custom.iz_stage}_Reservations_Out'
136
171
  );
172
+ expect(reservationResources).not.toContain(
173
+ 'arn:aws:sns:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_serviceTag}_${self:custom.iz_stage}_ReservationsComplete_In'
174
+ );
137
175
  expect(resources).not.toContain(
138
- 'arn:aws:dynamodb:${self:custom.iz_region}:${self:custom.iz_accountId}:table/${self:custom.iz_resourcePrefix}*'
176
+ 'arn:aws:sns:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_serviceTag}_${self:custom.iz_stage}_CreateRolePermissions_In'
139
177
  );
178
+ expect(reservationActions).toContain('sns:Publish');
179
+ expect(reservationActions).not.toContain('sns:Subscribe');
140
180
  expect(resources).not.toContain(
141
- 'arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}*'
181
+ 'arn:aws:dynamodb:${self:custom.iz_region}:${self:custom.iz_accountId}:table/${self:custom.iz_resourcePrefix}*'
142
182
  );
143
183
  });
144
184
 
145
- it('adds WebSocket permissions only for WebSocketMainRole when ownTopic flow exists', async () => {
185
+ it('adds WebSocket permissions only for WebSocketMainRole', async () => {
146
186
  await generateSharedResourceYaml(
147
187
  { flows: [{ flowTag: 'dummy', event: ['ownTopic'] }] },
148
188
  { outputPath: tmpDir }
149
189
  );
150
190
 
151
191
  const grouped = policyRegistry.toGroupedByRole();
152
- expect(Object.keys(grouped)).toContain('WebSocketMainRole');
153
192
  const wsActions = grouped.WebSocketMainRole.statements.flatMap(
154
193
  statement => statement.action
155
194
  );
156
- expect(wsActions).toEqual(
157
- expect.arrayContaining(['execute-api:ManageConnections'])
158
- );
195
+
196
+ expect(wsActions).toContain('execute-api:ManageConnections');
197
+ expect(wsActions).not.toContain('execute-api:Invoke');
198
+
199
+ for (const [roleName, roleData] of Object.entries(grouped)) {
200
+ if (roleName === 'WebSocketMainRole') continue;
201
+ const actions = roleData.statements.flatMap(statement => statement.action);
202
+ expect(actions).not.toContain('execute-api:ManageConnections');
203
+ expect(actions).not.toContain('execute-api:Invoke');
204
+ }
159
205
  });
160
206
 
161
- it('does not add WebSocket permissions for roles other than WebSocketMainRole', async () => {
207
+ it('only emits roles present in role-name-config', async () => {
208
+ policyRegistry.add(
209
+ 'UnexpectedRole',
210
+ 'sns:Publish',
211
+ 'arn:aws:sns:${self:custom.iz_region}:${self:custom.iz_accountId}:unexpected'
212
+ );
213
+
162
214
  await generateSharedResourceYaml(
163
- { flows: [{ flowTag: 'dummy', event: ['ownTopic'] }] },
215
+ { objects: [{ objectType: 'Foo' }], flows: [] },
164
216
  { outputPath: tmpDir }
165
217
  );
166
218
 
167
- const grouped = policyRegistry.toGroupedByRole();
168
- for (const roleName of Object.keys(grouped)) {
169
- if (roleName === 'WebSocketMainRole') continue;
170
- const actions = grouped[roleName].statements.flatMap(
171
- statement => statement.action
172
- );
173
- expect(actions).not.toContain('execute-api:ManageConnections');
174
- }
219
+ const content = await fs.readFile(
220
+ path.join(
221
+ tmpDir,
222
+ 'app',
223
+ 'sls_yaml',
224
+ 'generatedCode',
225
+ 'source',
226
+ 'generate-shared-resource.yml'
227
+ ),
228
+ 'utf8'
229
+ );
230
+
231
+ expect(content).toContain('FlowObjectsRole:');
232
+ expect(content).toContain('WebSocketMainRole:');
233
+ expect(content).not.toContain('UnexpectedRole:');
175
234
  });
176
235
  });
@@ -33,7 +33,7 @@ Resources:
33
33
  - "<%- r %>"
34
34
  <% }) %>
35
35
  <% }) %>
36
- #<#<%- roleName %>StatementHookCode#>
37
- #<#/<%- roleName %>StatementHookCode#>
36
+ #<#<%- roleName %>StatementHookCode#>
37
+ #<#/<%- roleName %>StatementHookCode#>
38
38
 
39
39
  <% }) %>
@@ -40,7 +40,7 @@ export async function generateEndpointsFlow(allSchemas, options) {
40
40
  for (const action of CRUD_ACTIONS) {
41
41
  const isAsync = CRUD_ASYNC_ACTIONS.includes(action);
42
42
  const upperAction = upperFirst(action);
43
- const flowOutputDir = path.join(baseOutputDir, 'FlowEndpoints', upperAction, 'source');
43
+ const flowOutputDir = path.join(baseOutputDir, 'FlowObjects', upperAction, 'source');
44
44
  await fs.mkdir(flowOutputDir, { recursive: true });
45
45
 
46
46
  const mainFileName = processMainFileName(action);
@@ -0,0 +1,252 @@
1
+ /*
2
+ Copyright (C) 2020 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
16
+ <http: //www.gnu.org/licenses />.
17
+ */
18
+
19
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
20
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
21
+ import sqsSharedLib from '@izara_project/izara-core-library-sqs';
22
+ import asyncFlowSharedLib from '@izara_project/izara-core-library-asynchronous-flow';
23
+ import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
24
+ import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
25
+
26
+ //(<optionalImport>)
27
+ import { coreConsts } from '@izara_project/izara-core-library-core';
28
+ import { getObjectSchema } from '@izara_project/izara-core-library-service-schemas';
29
+ import {
30
+ lambda,
31
+ sns,
32
+ sqs
33
+ } from '@izara_project/izara-core-library-external-request';
34
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
35
+ import { utils } from '@izara_project/izara-core-library-service-schemas';
36
+
37
+ const { createFlowTypeConcat } = utils;
38
+ //(</optionalImport>)
39
+
40
+ /**
41
+ *
42
+ *
43
+ * description of function.
44
+ * @param {Object} _izContext
45
+ * @param {CorrelationIds} _izContext.correlationIds - property of _izContext
46
+ * @param {Logger} _izContext.logger - property of _izContext
47
+ * @param {Object} requestParams - request params
48
+ * @param {Object} requestParams.identifiers - identifiers for get data
49
+ * @param {Object} requestParams.additionalRequest - additionalRequest
50
+ *
51
+ *
52
+ * @returns {object} description of return value
53
+ */
54
+
55
+ export default async function ProcessUserRbacFlow(
56
+ _izContext,
57
+ requestParams,
58
+ callingFlowConfig = {}
59
+ //(<additionalParams>)
60
+ //(</additionalParams>)
61
+ ) {
62
+ try {
63
+ _izContext.logger.debug('ProcessUserRbacFlow _izContext', _izContext);
64
+ _izContext.logger.debug('ProcessUserRbacFlow requestParams', requestParams);
65
+ _izContext.logger.debug(
66
+ 'ProcessUserRbacFlow callingFlowConfig',
67
+ callingFlowConfig
68
+ );
69
+
70
+ const userId = _izContext.correlationIds.get(coreConsts.BASE_USER_ID);
71
+ const targetId = _izContext.correlationIds.get(coreConsts.TARGET_ID);
72
+
73
+ if (!userId && !targetId) {
74
+ throw new Error(
75
+ 'Missing required request parameters: userId or targetId'
76
+ );
77
+ }
78
+
79
+ const { flowTypeConcat, inputParameterGroups } = requestParams;
80
+ if (!flowTypeConcat) {
81
+ throw new Error('Missing flowTypeConcat in requestParams');
82
+ }
83
+
84
+ _izContext.logger.debug('userId', userId);
85
+ _izContext.logger.debug('targetId', targetId);
86
+ _izContext.logger.debug('flowTypeConcat', flowTypeConcat);
87
+
88
+ const tableUserRoles = dynamodbSharedLib.tableName(_izContext, 'UserRoles');
89
+
90
+ const resultUserRoles = await dynamodbSharedLib.query(
91
+ _izContext,
92
+ tableUserRoles,
93
+ {
94
+ userId
95
+ }
96
+ );
97
+
98
+ if (!resultUserRoles.Items) {
99
+ resultUserRoles.Items = [];
100
+ }
101
+
102
+ let targetUserRoles = resultUserRoles.Items;
103
+
104
+ targetUserRoles = targetUserRoles.filter(
105
+ r => r.roleIdKey && r.roleIdKey.startsWith(targetId + '_')
106
+ );
107
+
108
+ const tableRolePermission = dynamodbSharedLib.tableName(
109
+ _izContext,
110
+ 'RolePermissions'
111
+ );
112
+
113
+ const chunkSize = 10;
114
+ const rbacRolePermissions = [];
115
+
116
+ for (let i = 0; i < targetUserRoles.length; i += chunkSize) {
117
+ const chunk = targetUserRoles.slice(i, i + chunkSize);
118
+ const promises = chunk.map(userRole =>
119
+ dynamodbSharedLib
120
+ .getItem(_izContext, tableRolePermission, {
121
+ roleIdKey: userRole.roleIdKey,
122
+ flowTypeConcat: flowTypeConcat
123
+ })
124
+ .catch(err => {
125
+ _izContext.logger.error(
126
+ `Error fetching permission for role ${userRole.roleIdKey}:`,
127
+ err
128
+ );
129
+ return null;
130
+ })
131
+ );
132
+
133
+ const results = await Promise.all(promises);
134
+ rbacRolePermissions.push(...results);
135
+ }
136
+
137
+ let isPermission = false;
138
+ let roleIdKey = '';
139
+
140
+ for (let i = 0; i < rbacRolePermissions.length; i++) {
141
+ const rbacRolePermission = rbacRolePermissions[i];
142
+ const userRole = targetUserRoles[i];
143
+
144
+ if (rbacRolePermission && rbacRolePermission.permission === true) {
145
+ const dbParameterGroups = rbacRolePermission.parameterGroups;
146
+ let isParamValid = true;
147
+
148
+ if (
149
+ dbParameterGroups &&
150
+ typeof dbParameterGroups === 'object' &&
151
+ !Array.isArray(dbParameterGroups)
152
+ ) {
153
+ const entries = Object.entries(dbParameterGroups);
154
+ if (entries.length > 0) {
155
+ let any_parameterGroup_match = false;
156
+
157
+ for (const [groupName, parameterGroup] of entries) {
158
+ if (!Array.isArray(parameterGroup)) {
159
+ _izContext.logger.debug(
160
+ `Skipping non-array parameterGroup: ${groupName}`
161
+ );
162
+ continue;
163
+ }
164
+
165
+ _izContext.logger.debug(
166
+ `Checking parameterGroup [${groupName}]:`,
167
+ parameterGroup
168
+ );
169
+ let parameterGroupMatch = true;
170
+
171
+ for (const check_parameter of parameterGroup) {
172
+ let tempRequestParams = inputParameterGroups || {};
173
+
174
+ if (
175
+ !check_parameter.parameterNames ||
176
+ !Array.isArray(check_parameter.parameterNames)
177
+ ) {
178
+ parameterGroupMatch = false;
179
+ break;
180
+ }
181
+
182
+ for (const parameterName of check_parameter.parameterNames) {
183
+ if (
184
+ !tempRequestParams ||
185
+ typeof tempRequestParams !== 'object' ||
186
+ !(parameterName in tempRequestParams)
187
+ ) {
188
+ parameterGroupMatch = false;
189
+ break;
190
+ }
191
+ tempRequestParams = tempRequestParams[parameterName];
192
+ }
193
+
194
+ if (!parameterGroupMatch) break;
195
+
196
+ if (
197
+ !check_parameter.parameterValues ||
198
+ !Array.isArray(check_parameter.parameterValues)
199
+ ) {
200
+ parameterGroupMatch = false;
201
+ break;
202
+ }
203
+
204
+ const valueMatched =
205
+ check_parameter.parameterValues.includes(tempRequestParams);
206
+
207
+ if (!valueMatched) {
208
+ parameterGroupMatch = false;
209
+ break;
210
+ }
211
+ }
212
+
213
+ if (parameterGroupMatch) {
214
+ _izContext.logger.debug(
215
+ `✅ parameterGroup [${groupName}] matched`
216
+ );
217
+ any_parameterGroup_match = true;
218
+ break;
219
+ } else {
220
+ _izContext.logger.debug(
221
+ `❌ parameterGroup [${groupName}] did not match`
222
+ );
223
+ }
224
+ }
225
+
226
+ isParamValid = any_parameterGroup_match;
227
+ }
228
+ }
229
+
230
+ if (isParamValid) {
231
+ isPermission = true;
232
+ roleIdKey = rbacRolePermission.roleIdKey;
233
+ _izContext.logger.debug(
234
+ '### matched rbacRolePermission ###',
235
+ rbacRolePermission
236
+ );
237
+ break;
238
+ } else {
239
+ _izContext.logger.debug(
240
+ '### rbacRolePermission parameterGroups check failed ###',
241
+ { roleIdKey: userRole.roleIdKey }
242
+ );
243
+ }
244
+ }
245
+ }
246
+
247
+ return { permission: isPermission, roleIdKey };
248
+ } catch (err) {
249
+ _izContext.logger.error('error ProcessUserRbacFlow: ', err);
250
+ throw err;
251
+ }
252
+ }
@@ -37,7 +37,7 @@ export async function generateRelationshipFlows(allSchemas, options) {
37
37
  const upperFlowTag = upperFirst(flowTag);
38
38
  const processFunction = processFunctionName(flowTag);
39
39
 
40
- const flowOutputDir = path.join(baseOutputDir, 'FlowRelationshipEndpoints', upperFlowTag, 'source');
40
+ const flowOutputDir = path.join(baseOutputDir, 'FlowRelationships', upperFlowTag, 'source');
41
41
  await fs.mkdir(flowOutputDir, { recursive: true });
42
42
 
43
43
  // 1. Generate Action Step (In) Main Function
@@ -56,8 +56,13 @@ export async function generateRelationshipFlows(allSchemas, options) {
56
56
  console.warn(` [RelationshipFlowGenerator] Warning: Action template not found for ${flowTag}. Skipping.`);
57
57
  }
58
58
 
59
- // 2. Generate Action Step Handlers (based on fixed event ['ownTopic'])
60
- const events = ['ownTopic'];
59
+ // 2. Generate Action Step Handlers
60
+ const flowSchema = allSchemas.flows?.find(f => f.flowTag === flowTag);
61
+ let events = ['ownTopic'];
62
+ if (flowSchema && flowSchema.event) {
63
+ events = Array.isArray(flowSchema.event) ? flowSchema.event : [flowSchema.event];
64
+ }
65
+
61
66
  for (const event of events) {
62
67
  let handlerFileName, handlerContent, handlerFunctionName;
63
68
  if (event === 'ownTopic' || event === 'extTopic') {
@@ -91,6 +96,17 @@ export async function generateRelationshipFlows(allSchemas, options) {
91
96
  });
92
97
  await fs.writeFile(path.join(flowOutputDir, `${handlerFunctionName}.js`), handlerContent, 'utf-8');
93
98
  generatedCount++;
99
+ } else if (event === 'dsq') {
100
+ handlerFunctionName = `${processFunction}_HdrDsq`;
101
+ handlerContent = ejs.render(templates.sqs, {
102
+ flowTag: flowTag,
103
+ flowStepName: '',
104
+ mainFileName: actionFileName,
105
+ functionName: processFunction,
106
+ queueName: `${processFunction}HdrDsq`
107
+ });
108
+ await fs.writeFile(path.join(flowOutputDir, `${handlerFunctionName}.js`), handlerContent, 'utf-8');
109
+ generatedCount++;
94
110
  }
95
111
  }
96
112
 
@@ -81,7 +81,7 @@ export default async function createRelationship(
81
81
  relType,
82
82
  relationshipDirection,
83
83
  relationshipProperties,
84
- deepPathConditional
84
+ deepPathConditional,
85
85
  settings
86
86
  //(<requestParamCreateRel>)
87
87
  //(</requestParamCreateRel>)
@@ -22,7 +22,7 @@ export async function generateWebSocket(allLocalFlowSchemas, appPath, outputBase
22
22
  return;
23
23
  }
24
24
 
25
- console.log(' [WebSocketGenerator] Generated WebSocket files with ProcessWebSocketMain_Main convention.');
25
+ console.log(' [WebSocketGenerator] Generated WebSocket files with WebSocketComplete_Main convention.');
26
26
  const templatesDir = path.join(__dirname, 'templates', 'webSocket');
27
27
  const wsDir = path.join(outputBaseDir, 'app', 'src', 'generatedCode', 'SystemFlowSchemas', 'WebSocketMain', 'source');
28
28
  await fs.mkdir(wsDir, { recursive: true });
@@ -79,8 +79,8 @@ export async function generateWebSocket(allLocalFlowSchemas, appPath, outputBase
79
79
  upperFunctionName: upperCase(wsConnectName)
80
80
  });
81
81
 
82
- // 3. ProcessWebSocketMain
83
- const processFunctionName = 'ProcessWebSocketMain';
82
+ // 3. WebSocketComplete
83
+ const processFunctionName = 'WebSocketComplete';
84
84
  const processHandlerType = 'HdrSqs';
85
85
  const queueName = processFunctionName + shortNameHandler(processHandlerType);
86
86
 
@@ -114,5 +114,5 @@ export async function generateWebSocket(allLocalFlowSchemas, appPath, outputBase
114
114
  }
115
115
  }
116
116
 
117
- console.log(` [WebSocketGenerator] Generated WebSocket files with ProcessWebSocketMain_Main convention.`);
117
+ console.log(` [WebSocketGenerator] Generated WebSocket files with WebSocketComplete_Main convention.`);
118
118
  }
@@ -42,8 +42,9 @@ export function isRelationshipFlow(flowTag) {
42
42
  }
43
43
 
44
44
  export function getFlowOutputSubDir(flowTag) {
45
- if (isCrudFlow(flowTag)) return 'FlowEndpoints';
45
+ if (isCrudFlow(flowTag)) return 'FlowObjects';
46
46
  if (isRbacFlow(flowTag)) return 'FlowRbac';
47
- if (isRelationshipFlow(flowTag)) return 'FlowRelationshipEndpoints';
47
+ if (isRelationshipFlow(flowTag)) return 'FlowRelationships';
48
+ if (String(flowTag || '').toLowerCase().endsWith('rbacflow')) return 'FlowRbac';
48
49
  return 'FlowSchemas';
49
50
  }
@@ -53,16 +53,23 @@ export async function generateFlowMainFunction(allSchemas, options) {
53
53
  let templateName = 'FlowEndpoint_Main.ejs';
54
54
  if (['Create', 'Update', 'Delete', 'Get'].includes(flowTag)) {
55
55
  templateName = `${flowTag}Endpoint_Main.ejs`;
56
+ } else if (String(flowTag || '').toLowerCase().endsWith('rbacflow')) {
57
+ templateName = 'FlowRbac_Main.ejs';
56
58
  } else if (isOwnTopic) {
57
59
  // Non-CRUD flow with ownTopic: WebSocket entry that fetches flowSchema and publishes to SNS In topic
58
60
  templateName = 'FlowMain_Wbs.ejs';
59
61
  }
60
62
 
61
- // crud templates live under FlowEndpoints (the only output child that uses them);
63
+ // crud templates live under FlowObjects (the only output child that uses them);
62
64
  // shared endpoint templates (FlowEndpoint_Main, FlowMain_Wbs) live under _internal/_shared/endpoint
63
- const templateBaseDir = templateName.startsWith('crud/') || ['CreateEndpoint_Main.ejs', 'UpdateEndpoint_Main.ejs', 'DeleteEndpoint_Main.ejs', 'GetEndpoint_Main.ejs'].includes(templateName)
64
- ? path.join(__dirname, '..', '..', 'FlowEndpoints', 'templates', 'crud')
65
- : path.join(__dirname, '..', '..', 'FlowSchemas', 'templates', 'endpoint');
65
+ let templateBaseDir;
66
+ if (templateName === 'FlowRbac_Main.ejs') {
67
+ templateBaseDir = path.join(__dirname, '..', '..', 'FlowRbac', 'templates', 'rbac');
68
+ } else if (templateName.startsWith('crud/') || ['CreateEndpoint_Main.ejs', 'UpdateEndpoint_Main.ejs', 'DeleteEndpoint_Main.ejs', 'GetEndpoint_Main.ejs'].includes(templateName)) {
69
+ templateBaseDir = path.join(__dirname, '..', '..', 'FlowObjects', 'templates', 'crud');
70
+ } else {
71
+ templateBaseDir = path.join(__dirname, '..', '..', 'FlowSchemas', 'templates', 'endpoint');
72
+ }
66
73
  const mainTemplateStr = await fs.readFile(
67
74
  path.join(templateBaseDir, templateName),
68
75
  'utf-8'
@@ -130,7 +137,7 @@ export async function generateFlowMainFunction(allSchemas, options) {
130
137
  const beforeLogicalConfigs = generateCode.filter(c => c.codeHookTag === 'beforeLogical');
131
138
  if (isCrud && beforeLogicalConfigs.length > 0) {
132
139
  const beforeLogicalTemplateStr = await fs.readFile(
133
- path.join(__dirname, '..', '..', 'FlowEndpoints', 'templates', 'FlowEndpointBeforeLogical_Main.ejs'),
140
+ path.join(__dirname, '..', '..', 'FlowObjects', 'templates', 'FlowEndpointBeforeLogical_Main.ejs'),
134
141
  'utf-8'
135
142
  );
136
143
 
@@ -1,4 +1,4 @@
1
- const ALLOWED_MAIN_EVENTS = ['ownTopic', 'extTopic', 'lambdaSyncInv', 'lambdaSyncApi', 'eventBridge', 's3'];
1
+ const ALLOWED_MAIN_EVENTS = ['ownTopic', 'extTopic', 'lambdaSyncInv', 'lambdaSyncApi', 'eventBridge', 's3', 'dsq'];
2
2
  const MAIN_ASYNC_EVENTS = ['ownTopic', 'extTopic', 's3'];
3
3
  const ALLOWED_STEP_EVENTS = ['sqs', 'ownTopic', 'extTopic', 'dsq', 'lambdaSyncInv', 'lambdaSyncApi'];
4
4
  const RESERVED_SYSTEM_FLOW_TAGS = new Set([