@izara_project/izara-core-generate-service-code 1.0.82 → 1.0.84

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 (60) hide show
  1. package/package.json +4 -4
  2. package/src/compile/flowValidator.js +34 -0
  3. package/src/compile/loadSchemas.js +121 -7
  4. package/src/emit/attributeTree/emitAttributeTreeSchemas.js +388 -0
  5. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +29 -1
  6. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +16 -3
  7. package/src/emit/crud/crudActionPlan.js +43 -7
  8. package/src/emit/crud/emitCrudApplicationArtifacts.js +19 -4
  9. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +27 -6
  10. package/src/emit/crud/generateCrudApplicationArtifacts.js +3 -2
  11. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +3 -2
  12. package/src/emit/crud/loadLocalSchemaArrays.js +3 -3
  13. package/src/emit/crud/templates/CreateComplete_Main.ejs +1 -1
  14. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +75 -21
  15. package/src/emit/crud/templates/CreateLogicalLib.ejs +73 -8
  16. package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
  17. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +39 -11
  18. package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
  19. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +2 -2
  20. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -0
  21. package/src/emit/crud/templates/FlowMain_Wbs.ejs +5 -2
  22. package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
  23. package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
  24. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +53 -4
  25. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  26. package/src/emit/custom/customFlows.js +49 -0
  27. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
  28. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
  29. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
  30. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
  31. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  32. package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
  33. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
  34. package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
  35. package/src/emit/rbac/templates/CancelUsage_Main.ejs +103 -0
  36. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +71 -0
  37. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +49 -6
  38. package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
  39. package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
  40. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +147 -0
  41. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +59 -23
  42. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +1 -1
  43. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  44. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +10 -3
  45. package/src/emit/resource/emitResourceArtifacts.js +350 -112
  46. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +53 -2
  47. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -4
  48. package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
  49. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +7 -0
  50. package/src/emit/systemText/emitSystemTextSchemas.js +3 -2
  51. package/src/generate.js +41 -10
  52. package/test/attributeTreeSchemas.test.js +219 -0
  53. package/test/createTargetRoleHandler.test.js +59 -4
  54. package/test/crudApplicationArtifacts.test.js +23 -6
  55. package/test/customFlowApplicationArtifacts.test.js +5 -5
  56. package/test/dynamicCrudFlowSchemas.test.js +110 -13
  57. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  58. package/test/publicApi.test.js +7 -7
  59. package/test/resourceArtifacts.test.js +70 -29
  60. package/test/systemTextSchemas.test.js +1 -1
@@ -7,7 +7,8 @@ import {
7
7
  flowExtTopicSteps,
8
8
  generatedDsqFlows,
9
9
  generatedOwnTopicFlows,
10
- supportedCustomFlows
10
+ supportedCustomFlows,
11
+ flowHasRbac
11
12
  } from '../custom/customFlows.js';
12
13
  import { hasSystemWebSocket } from '../system/emitSystemWebSocketArtifacts.js';
13
14
  import { rbacFlowTag } from '../rbac/rbacFlowTag.js';
@@ -38,7 +39,13 @@ const RBAC_ACTIONS = [
38
39
  'CreateUserRole',
39
40
  'ListUserInRoles',
40
41
  'DeleteUserFromRole',
41
- 'ValidatorRbac'
42
+ 'ValidatorRbac',
43
+ 'CreateRoleLimit',
44
+ 'ListRoleLimits',
45
+ 'DeleteRoleLimit',
46
+ 'ReservedDynamicUsage',
47
+ 'ConfirmUsage',
48
+ 'CancelUsage'
42
49
  ];
43
50
  const SYSTEM_TABLES = [
44
51
  'WebSocketTask',
@@ -57,6 +64,9 @@ const RBAC_TABLES = [
57
64
  'RoleUsers',
58
65
  'UserRoles',
59
66
  'RbacPermissionCaches',
67
+ 'RoleLimit',
68
+ 'RunningTotalUsage',
69
+ 'RunningTotalReservedUsage',
60
70
  'WebSocketTask'
61
71
  ];
62
72
  const LEGACY_PROCESS_QUEUES = [
@@ -93,8 +103,23 @@ export async function emitResourceArtifacts({
93
103
  throw new TypeError('objects, relationships, and flows must be arrays');
94
104
  }
95
105
  const state = createResourceState({ objects, relationships, flows });
96
- const [inSqs, out, dynamo, iam, lambdaRoleConfig, s3] = await Promise.all([
97
- renderInSqs(state),
106
+ const [
107
+ crudInSqs,
108
+ relationshipInSqs,
109
+ rbacInSqs,
110
+ customFlowInSqs,
111
+ systemInSqs,
112
+ out,
113
+ dynamo,
114
+ iam,
115
+ lambdaRoleConfig,
116
+ s3
117
+ ] = await Promise.all([
118
+ renderCrudInSqs(state),
119
+ renderRelationshipInSqs(state),
120
+ renderRbacInSqs(state),
121
+ renderCustomFlowInSqs(state),
122
+ renderSystemInSqs(state),
98
123
  renderOut(state),
99
124
  renderDynamo(state),
100
125
  renderIam(state),
@@ -102,10 +127,6 @@ export async function emitResourceArtifacts({
102
127
  renderS3(state)
103
128
  ]);
104
129
  const artifacts = [
105
- {
106
- path: `${RESOURCE_ROOT}/generated-sns-in-sqs.yml`,
107
- content: normalizeYaml(inSqs)
108
- },
109
130
  {
110
131
  path: `${RESOURCE_ROOT}/generated-sns-out.yml`,
111
132
  content: normalizeYaml(out)
@@ -123,6 +144,36 @@ export async function emitResourceArtifacts({
123
144
  content: normalizeYaml(iam.roleNameConfigContent)
124
145
  }
125
146
  ];
147
+ if (crudInSqs) {
148
+ artifacts.push({
149
+ path: `${RESOURCE_ROOT}/generated-sns-sqs-crud.yml`,
150
+ content: normalizeYaml(crudInSqs)
151
+ });
152
+ }
153
+ if (relationshipInSqs) {
154
+ artifacts.push({
155
+ path: `${RESOURCE_ROOT}/generated-sns-sqs-relationship.yml`,
156
+ content: normalizeYaml(relationshipInSqs)
157
+ });
158
+ }
159
+ if (rbacInSqs) {
160
+ artifacts.push({
161
+ path: `${RESOURCE_ROOT}/generated-sns-sqs-rbac.yml`,
162
+ content: normalizeYaml(rbacInSqs)
163
+ });
164
+ }
165
+ if (customFlowInSqs) {
166
+ artifacts.push({
167
+ path: `${RESOURCE_ROOT}/generated-sns-sqs-custom-flows.yml`,
168
+ content: normalizeYaml(customFlowInSqs)
169
+ });
170
+ }
171
+ if (systemInSqs) {
172
+ artifacts.push({
173
+ path: `${RESOURCE_ROOT}/generated-sns-sqs-system.yml`,
174
+ content: normalizeYaml(systemInSqs)
175
+ });
176
+ }
126
177
  if (state.s3Buckets.length > 0) {
127
178
  artifacts.push({
128
179
  path: `${RESOURCE_ROOT}/generated-s3.yml`,
@@ -140,6 +191,11 @@ export async function emitResourceArtifacts({
140
191
 
141
192
  export const resourceArtifactPaths = [
142
193
  `${RESOURCE_ROOT}/generated-sns-in-sqs.yml`,
194
+ `${RESOURCE_ROOT}/generated-sns-sqs-crud.yml`,
195
+ `${RESOURCE_ROOT}/generated-sns-sqs-relationship.yml`,
196
+ `${RESOURCE_ROOT}/generated-sns-sqs-rbac.yml`,
197
+ `${RESOURCE_ROOT}/generated-sns-sqs-custom-flows.yml`,
198
+ `${RESOURCE_ROOT}/generated-sns-sqs-system.yml`,
143
199
  `${RESOURCE_ROOT}/generated-sns-out.yml`,
144
200
  `${RESOURCE_ROOT}/generated-dynamoDB-table.yml`,
145
201
  `${RESOURCE_ROOT}/generated-s3.yml`,
@@ -153,65 +209,34 @@ function createResourceState({ objects, relationships, flows }) {
153
209
  const selfRbac = objects.some(hasSelfRbacAddOn);
154
210
  const rbac = objects.some(hasSelfRbacAddOn);
155
211
  const crudActionPlans = createCrudActionPlans(objects);
156
- const ownTopics = new Set([
157
- 'ProcessLogical',
158
- 'FindData',
159
- ...flows
160
- .filter((flow) => includesEvent(flow, 'ownTopic'))
161
- .map((flow) => upperFirst(flow.flowTag))
162
- ]);
163
- const sqsQueues = new Set([
164
- 'PaginateProcessLogicalHdrDsq',
165
- 'PaginateProcessLogicalHdrSqs',
166
- ...flows
167
- .filter((flow) => includesEvent(flow, 'extTopic'))
168
- .map((flow) => `Process${upperFirst(flow.flowTag)}HdrSqs`),
169
- ...flows
170
- .filter((flow) => flow?.outputTopic && !flow?.generatedCodeConfig && upperFirst(flow.flowTag))
171
- .map((flow) => `${upperFirst(flow.flowTag)}CompleteHdrSqs`)
172
- ]);
173
- for (const { flowTag, stepName } of flowExtTopicSteps(flows)) {
174
- sqsQueues.add(`${flowTag}${stepName}HdrSqs`);
175
- }
176
- for (const flow of flows.filter((flow) => includesEvent(flow, 's3'))) {
177
- const tag = upperFirst(flow.flowTag);
178
- sqsQueues.add(`${tag}CreatePreSignUrlHdrSqs`);
179
- sqsQueues.add(`${tag}ReservedLimitHdrSqs`);
180
- sqsQueues.add(`${tag}ConfirmAfterUploadHdrSqs`);
181
- }
182
- const dsqQueues = new Set(
183
- flows
184
- .filter((flow) => includesEvent(flow, 'dsq'))
185
- .map((flow) => `${upperFirst(flow.flowTag)}HdrDsq`)
186
- );
187
- for (const { flowTag, stepName } of flowDsqSteps(flows)) {
188
- dsqQueues.add(`${flowTag}${stepName}HdrDsq`);
189
- }
212
+
213
+ const crudOwnTopics = crud ? [...CRUD_ACTIONS] : [];
214
+ const crudSqsQueues = [];
190
215
  if (crud) {
191
- for (const action of CRUD_ACTIONS) {
192
- ownTopics.add(action);
193
- }
194
216
  for (const action of ['Create', 'Update', 'Delete']) {
195
217
  const plan = crudActionPlans[action];
196
218
  if (plan.hasComplete) {
197
- sqsQueues.add(`${action}CompleteHdrSqs`);
198
- }
199
- if (plan.hasRbacCallback) {
200
- sqsQueues.add(`${action}RbacCallbackHdrSqs`);
219
+ crudSqsQueues.push(`${action}CompleteHdrSqs`);
201
220
  }
202
221
  }
203
222
  }
204
- if (relationships.length > 0) {
205
- for (const action of RELATIONSHIP_ACTIONS) {
206
- ownTopics.add(action);
207
- sqsQueues.add(`${action}CompleteHdrSqs`);
208
- }
209
- dsqQueues.add('GetRelationshipHdrDsq');
210
- }
223
+
224
+ const relationshipOwnTopics = relationships.length > 0
225
+ ? [...RELATIONSHIP_ACTIONS]
226
+ : [];
227
+ const relationshipSqsQueues = relationships.length > 0
228
+ ? RELATIONSHIP_ACTIONS.map((action) => `${action}CompleteHdrSqs`)
229
+ : [];
230
+ const relationshipDsqQueues = relationships.length > 0
231
+ ? ['GetRelationshipHdrDsq']
232
+ : [];
233
+
234
+ const rbacOwnTopics = [];
235
+ const rbacSqsQueues = [];
211
236
  if (selfRbac) {
212
237
  for (const action of RBAC_ACTIONS) {
213
- ownTopics.add(action);
214
- sqsQueues.add(`Process${action}HdrSqs`);
238
+ rbacOwnTopics.push(action);
239
+ rbacSqsQueues.push(`Process${action}HdrSqs`);
215
240
  }
216
241
  }
217
242
  if (rbac) {
@@ -219,9 +244,93 @@ function createResourceState({ objects, relationships, flows }) {
219
244
  ...new Set(objects.filter(hasRbacAddOn).map(rbacFlowTag))
220
245
  ];
221
246
  for (const flowTag of flowTags) {
222
- ownTopics.add(flowTag);
247
+ rbacOwnTopics.push(flowTag);
248
+ }
249
+ }
250
+ if (crud) {
251
+ for (const action of ['Create', 'Update', 'Delete']) {
252
+ const plan = crudActionPlans[action];
253
+ if (plan.hasRbacCallback) {
254
+ rbacSqsQueues.push(`${action}RbacCallbackHdrSqs`);
255
+ }
256
+ }
257
+ }
258
+
259
+ const customOwnTopics = flows
260
+ .filter((flow) => includesEvent(flow, 'ownTopic'))
261
+ .map((flow) => upperFirst(flow.flowTag));
262
+
263
+ const customSqsQueues = [
264
+ ...flows
265
+ .filter((flow) => includesEvent(flow, 'extTopic'))
266
+ .map((flow) => `Process${upperFirst(flow.flowTag)}HdrSqs`),
267
+ ...flows
268
+ .filter(
269
+ (flow) =>
270
+ flow?.outputTopic &&
271
+ !flow?.generatedCodeConfig &&
272
+ upperFirst(flow.flowTag)
273
+ )
274
+ .map((flow) => `${upperFirst(flow.flowTag)}CompleteHdrSqs`)
275
+ ];
276
+ for (const { flowTag, stepName } of flowExtTopicSteps(flows)) {
277
+ customSqsQueues.push(`${flowTag}${stepName}HdrSqs`);
278
+ }
279
+ for (const flow of flows) {
280
+ if (flowHasRbac(flow, objects)) {
281
+ const tag = upperFirst(flow.flowTag);
282
+ customSqsQueues.push(`${tag}RbacCallbackHdrSqs`);
223
283
  }
224
284
  }
285
+ for (const flow of flows.filter((flow) => includesEvent(flow, 's3'))) {
286
+ const tag = upperFirst(flow.flowTag);
287
+ customSqsQueues.push(`${tag}CreatePreSignUrlHdrSqs`);
288
+ customSqsQueues.push(`${tag}ReservedLimitHdrSqs`);
289
+ customSqsQueues.push(`${tag}ConfirmAfterUploadHdrSqs`);
290
+ }
291
+
292
+ const customDsqQueues = flows
293
+ .filter((flow) => includesEvent(flow, 'dsq'))
294
+ .map((flow) => `${upperFirst(flow.flowTag)}HdrDsq`);
295
+ for (const { flowTag, stepName } of flowDsqSteps(flows)) {
296
+ customDsqQueues.push(`${flowTag}${stepName}HdrDsq`);
297
+ }
298
+
299
+ const websocket = hasSystemWebSocket(flows, {
300
+ includeRelationshipOwnTopic: true,
301
+ objects,
302
+ relationships
303
+ });
304
+
305
+ const systemOwnTopics = ['ProcessLogical', 'FindData'];
306
+ const systemSqsQueues = [
307
+ 'PaginateProcessLogicalHdrSqs',
308
+ ...(websocket
309
+ ? ['WebSocketCompleteHdrSqs', 'RegisterCompleteHdrSqs']
310
+ : [])
311
+ ];
312
+ const systemDsqQueues = ['PaginateProcessLogicalHdrDsq'];
313
+
314
+ const ownTopics = new Set([
315
+ ...systemOwnTopics,
316
+ ...crudOwnTopics,
317
+ ...relationshipOwnTopics,
318
+ ...rbacOwnTopics,
319
+ ...customOwnTopics
320
+ ]);
321
+ const sqsQueues = new Set([
322
+ ...systemSqsQueues,
323
+ ...crudSqsQueues,
324
+ ...relationshipSqsQueues,
325
+ ...rbacSqsQueues,
326
+ ...customSqsQueues
327
+ ]);
328
+ const dsqQueues = new Set([
329
+ ...systemDsqQueues,
330
+ ...relationshipDsqQueues,
331
+ ...customDsqQueues
332
+ ]);
333
+
225
334
  const customRoles = [
226
335
  ...new Set([
227
336
  ...supportedCustomFlows(flows).map((flow) =>
@@ -236,12 +345,11 @@ function createResourceState({ objects, relationships, flows }) {
236
345
  ...flowExtTopicSteps(flows).map(({ flow }) =>
237
346
  customFlowRoleName(flow.flowTag)
238
347
  ),
239
- ...flowDsqSteps(flows).map(({ flow }) => customFlowRoleName(flow.flowTag))
348
+ ...flowDsqSteps(flows).map(({ flow }) =>
349
+ customFlowRoleName(flow.flowTag)
350
+ )
240
351
  ])
241
352
  ];
242
- const websocket = hasSystemWebSocket(flows, {
243
- includeRelationshipOwnTopic: true
244
- });
245
353
  const webSocketRoles = new Set();
246
354
  if (websocket) {
247
355
  webSocketRoles.add('WebSocketMainRole');
@@ -286,7 +394,8 @@ function createResourceState({ objects, relationships, flows }) {
286
394
  .filter((flow) => includesEvent(flow, 's3'))
287
395
  .map((flow) => {
288
396
  const tag = upperFirst(flow.flowTag);
289
- const outputTopicStr = typeof flow.outputTopic === 'string' ? flow.outputTopic : null;
397
+ const outputTopicStr =
398
+ typeof flow.outputTopic === 'string' ? flow.outputTopic : null;
290
399
  const topicName = outputTopicStr
291
400
  ? (outputTopicStr.startsWith('Out')
292
401
  ? outputTopicStr
@@ -301,7 +410,16 @@ function createResourceState({ objects, relationships, flows }) {
301
410
  });
302
411
  const systemTables = [...SYSTEM_TABLES];
303
412
  if (rbac) {
304
- systemTables.push('TargetRoles', 'RolePermissions', 'RoleUsers', 'UserRoles', 'RbacPermissionCaches');
413
+ systemTables.push(
414
+ 'TargetRoles',
415
+ 'RolePermissions',
416
+ 'RoleUsers',
417
+ 'UserRoles',
418
+ 'RbacPermissionCaches',
419
+ 'RoleLimit',
420
+ 'RunningTotalUsage',
421
+ 'RunningTotalReservedUsage'
422
+ );
305
423
  }
306
424
  if (s3FlowTags.length > 0) {
307
425
  systemTables.push('ReservedDataMain');
@@ -315,6 +433,25 @@ function createResourceState({ objects, relationships, flows }) {
315
433
  crud,
316
434
  rbac,
317
435
  crudActionPlans,
436
+ crudOwnTopics: [...new Set(crudOwnTopics)].filter(Boolean).sort(),
437
+ crudSqsQueues: [...new Set(crudSqsQueues)].filter(Boolean).sort(),
438
+ relationshipOwnTopics: [...new Set(relationshipOwnTopics)]
439
+ .filter(Boolean)
440
+ .sort(),
441
+ relationshipSqsQueues: [...new Set(relationshipSqsQueues)]
442
+ .filter(Boolean)
443
+ .sort(),
444
+ relationshipDsqQueues: [...new Set(relationshipDsqQueues)]
445
+ .filter(Boolean)
446
+ .sort(),
447
+ rbacOwnTopics: [...new Set(rbacOwnTopics)].filter(Boolean).sort(),
448
+ rbacSqsQueues: [...new Set(rbacSqsQueues)].filter(Boolean).sort(),
449
+ customOwnTopics: [...new Set(customOwnTopics)].filter(Boolean).sort(),
450
+ customSqsQueues: [...new Set(customSqsQueues)].filter(Boolean).sort(),
451
+ customDsqQueues: [...new Set(customDsqQueues)].filter(Boolean).sort(),
452
+ systemOwnTopics: [...new Set(systemOwnTopics)].filter(Boolean).sort(),
453
+ systemSqsQueues: [...new Set(systemSqsQueues)].filter(Boolean).sort(),
454
+ systemDsqQueues: [...new Set(systemDsqQueues)].filter(Boolean).sort(),
318
455
  s3Subscriptions,
319
456
  s3FlowTags,
320
457
  hasHttpApi,
@@ -333,7 +470,8 @@ function createResourceState({ objects, relationships, flows }) {
333
470
  (flow) =>
334
471
  (flow?.outputTopic ||
335
472
  (includesEvent(flow, 'ownTopic') &&
336
- flow.initiateBy === 'user')) &&
473
+ (flow.initiateBy === 'user' ||
474
+ flow.initiateBy === 'systemWithUser'))) &&
337
475
  upperFirst(flow.flowTag) === topic
338
476
  ) ||
339
477
  (relationships.length > 0 &&
@@ -395,35 +533,39 @@ async function renderS3(state) {
395
533
  return `Resources:\n${indentYaml(chunks.join('\n'), 2)}`;
396
534
  }
397
535
 
398
- async function renderInSqs(state) {
536
+ async function renderResourceGroup({
537
+ ownTopics = [],
538
+ sqsQueues = [],
539
+ dsqQueues = [],
540
+ webSocketSubscriptions = [],
541
+ s3Subscriptions = []
542
+ }) {
543
+ const hasResources =
544
+ ownTopics.length > 0 ||
545
+ sqsQueues.length > 0 ||
546
+ dsqQueues.length > 0 ||
547
+ webSocketSubscriptions.length > 0 ||
548
+ s3Subscriptions.length > 0;
549
+ if (!hasResources) return '';
550
+
399
551
  const chunks = await Promise.all([
400
- ...state.ownTopics.map((queueName) =>
552
+ ...ownTopics.map((queueName) =>
401
553
  render('ResourceYaml.ejs', { queueName })
402
554
  ),
403
- ...state.sqsQueues.map((queueName) =>
555
+ ...sqsQueues.map((queueName) =>
404
556
  render('ResourceSqsYaml.ejs', { queueName })
405
557
  ),
406
- ...state.dsqQueues.map((queueName) =>
558
+ ...dsqQueues.map((queueName) =>
407
559
  render('ResourceSqsYaml.ejs', { queueName })
408
- ),
409
- ...(state.websocket
410
- ? [
411
- render('ResourceSqsYaml.ejs', {
412
- queueName: 'WebSocketCompleteHdrSqs'
413
- }),
414
- render('ResourceSqsYaml.ejs', {
415
- queueName: 'RegisterCompleteHdrSqs'
416
- })
417
- ]
418
- : [])
560
+ )
419
561
  ]);
420
- const webSocketSubscriptions = await Promise.all(
421
- state.webSocketSubscriptions.map((topicName) =>
562
+ const wsSubs = await Promise.all(
563
+ webSocketSubscriptions.map((topicName) =>
422
564
  renderWebSocketSubscription(topicName)
423
565
  )
424
566
  );
425
- const s3Subscriptions = await Promise.all(
426
- (state.s3Subscriptions ?? []).map(
567
+ const s3Subs = await Promise.all(
568
+ s3Subscriptions.map(
427
569
  ({ subscriptionName, topicName, queueName }) =>
428
570
  render('S3Subscription_Yaml.ejs', {
429
571
  subscriptionName,
@@ -433,16 +575,53 @@ async function renderInSqs(state) {
433
575
  )
434
576
  );
435
577
  const parts = [chunks.join('\n')];
436
- if (webSocketSubscriptions.length > 0) {
437
- parts.push(webSocketSubscriptions.join('\n'));
438
- }
439
- if (s3Subscriptions.length > 0) {
440
- parts.push(s3Subscriptions.join('\n'));
441
- }
442
- const body = parts.join('\n');
578
+ if (wsSubs.length > 0) parts.push(wsSubs.join('\n'));
579
+ if (s3Subs.length > 0) parts.push(s3Subs.join('\n'));
580
+ const body = parts.filter(Boolean).join('\n');
581
+ if (!body.trim()) return '';
443
582
  return `Resources:\n${indentYaml(body, 2)}`;
444
583
  }
445
584
 
585
+ async function renderCrudInSqs(state) {
586
+ return renderResourceGroup({
587
+ ownTopics: state.crudOwnTopics,
588
+ sqsQueues: state.crudSqsQueues
589
+ });
590
+ }
591
+
592
+ async function renderRelationshipInSqs(state) {
593
+ return renderResourceGroup({
594
+ ownTopics: state.relationshipOwnTopics,
595
+ sqsQueues: state.relationshipSqsQueues,
596
+ dsqQueues: state.relationshipDsqQueues
597
+ });
598
+ }
599
+
600
+ async function renderRbacInSqs(state) {
601
+ return renderResourceGroup({
602
+ ownTopics: state.rbacOwnTopics,
603
+ sqsQueues: state.rbacSqsQueues
604
+ });
605
+ }
606
+
607
+ async function renderCustomFlowInSqs(state) {
608
+ return renderResourceGroup({
609
+ ownTopics: state.customOwnTopics,
610
+ sqsQueues: state.customSqsQueues,
611
+ dsqQueues: state.customDsqQueues,
612
+ s3Subscriptions: state.s3Subscriptions
613
+ });
614
+ }
615
+
616
+ async function renderSystemInSqs(state) {
617
+ return renderResourceGroup({
618
+ ownTopics: state.systemOwnTopics,
619
+ sqsQueues: state.systemSqsQueues,
620
+ dsqQueues: state.systemDsqQueues,
621
+ webSocketSubscriptions: state.webSocketSubscriptions
622
+ });
623
+ }
624
+
446
625
  async function renderOut(state) {
447
626
  const body = (
448
627
  await Promise.all(
@@ -516,7 +695,13 @@ function roleStatements(state, roleName) {
516
695
  statements.push({
517
696
  action: ['s3:GetObject'],
518
697
  resource: [
519
- 'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}/perServiceSchemas/*'
698
+ 'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}/*'
699
+ ]
700
+ });
701
+ statements.push({
702
+ action: ['s3:ListBucket'],
703
+ resource: [
704
+ 'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}'
520
705
  ]
521
706
  });
522
707
  }
@@ -571,7 +756,17 @@ function roleTableNames(state, roleName) {
571
756
  ...state.objectTableNames,
572
757
  'AwaitingMultipleSteps',
573
758
  'AwaitingMultipleStepByPending',
574
- 'WebSocketTask'
759
+ 'WebSocketTask',
760
+ ...(state.rbac
761
+ ? [
762
+ 'TargetRoles',
763
+ 'RoleUsers',
764
+ 'UserRoles',
765
+ 'RoleLimit',
766
+ 'RunningTotalUsage',
767
+ 'RunningTotalReservedUsage'
768
+ ]
769
+ : [])
575
770
  ];
576
771
  if (roleName === 'FlowRelationshipRole')
577
772
  return [
@@ -610,7 +805,7 @@ function dynamoActionsForRole(roleName) {
610
805
  return ['dynamodb:GetItem', 'dynamodb:Query', 'dynamodb:UpdateItem'];
611
806
  if (roleName === 'WebSocketMainRole')
612
807
  return ['dynamodb:DeleteItem', 'dynamodb:PutItem', 'dynamodb:Query'];
613
- if (roleName === 'FlowRbacRole')
808
+ if (roleName === 'FlowRbacRole' || roleName === 'FlowObjectsRole')
614
809
  return [...DYNAMO_ACTIONS, 'dynamodb:TransactWriteItems'];
615
810
  return DYNAMO_ACTIONS;
616
811
  }
@@ -676,6 +871,9 @@ function roleQueueNames(state, roleName) {
676
871
  if (includesEvent(flow, 'dsq')) {
677
872
  queueList.push(`${tag}HdrDsq`);
678
873
  }
874
+ if (flowHasRbac(flow, state.objects)) {
875
+ queueList.push(`${tag}RbacCallbackHdrSqs`);
876
+ }
679
877
  queueList.push(
680
878
  ...flowExtTopicSteps([flow]).map(
681
879
  ({ flowTag, stepName }) => `${flowTag}${stepName}HdrSqs`
@@ -707,21 +905,58 @@ function roleDlqQueueNames(state, roleName) {
707
905
 
708
906
  function roleTopicNames(state, roleName) {
709
907
  if (roleName === 'ProcFindDataRole')
710
- return state.outTopics.filter((topic) => topic === 'FindData');
711
- if (roleName === 'FlowObjectsRole')
712
- return state.outTopics.filter((topic) => CRUD_ACTIONS.includes(topic));
713
- if (roleName === 'FlowRelationshipRole')
714
- return state.outTopics.filter((topic) =>
715
- RELATIONSHIP_ACTIONS.includes(topic)
716
- );
717
- if (roleName === 'FlowRbacRole')
718
- return state.outTopics.filter((topic) => RBAC_ACTIONS.includes(topic));
908
+ return state.outTopics.filter((topic) => topic === 'FindData').map((t) => `${t}_Out`);
909
+ if (roleName === 'FlowObjectsRole') {
910
+ const actions = state.outTopics.filter((topic) => CRUD_ACTIONS.includes(topic));
911
+ const topics = [];
912
+ for (const action of actions) {
913
+ if (action === 'Get') {
914
+ topics.push(`${action}_Out`);
915
+ } else {
916
+ topics.push(`${action}_In`, `${action}_Out`);
917
+ }
918
+ }
919
+ return topics;
920
+ }
921
+ if (roleName === 'FlowRelationshipRole') {
922
+ const actions = state.outTopics.filter((topic) => RELATIONSHIP_ACTIONS.includes(topic));
923
+ const topics = [];
924
+ for (const action of actions) {
925
+ if (action === 'GetRelationship') {
926
+ topics.push(`${action}_Out`);
927
+ } else {
928
+ topics.push(`${action}_In`, `${action}_Out`);
929
+ }
930
+ }
931
+ return topics;
932
+ }
933
+ if (roleName === 'FlowRbacRole') {
934
+ const actions = state.outTopics.filter((topic) => RBAC_ACTIONS.includes(topic));
935
+ const topics = [];
936
+ for (const action of actions) {
937
+ if (action.startsWith('List')) {
938
+ topics.push(`${action}_Out`);
939
+ } else {
940
+ topics.push(`${action}_In`, `${action}_Out`);
941
+ }
942
+ }
943
+ return topics;
944
+ }
719
945
  const flow = state.flows.find(
720
946
  (candidate) => customFlowRoleName(candidate?.flowTag ?? '') === roleName
721
947
  );
722
- return flow
723
- ? state.outTopics.filter((topic) => topic === upperFirst(flow.flowTag))
724
- : [];
948
+ if (flow && flow.flowTag) {
949
+ const name = upperFirst(flow.flowTag);
950
+ const topics = [];
951
+ if (state.outTopics.includes(name)) {
952
+ topics.push(`${name}_Out`);
953
+ }
954
+ if (includesEvent(flow, 'ownTopic')) {
955
+ topics.push(`${name}_In`);
956
+ }
957
+ return topics;
958
+ }
959
+ return [];
725
960
  }
726
961
 
727
962
  function dynamoArns(tableNames) {
@@ -793,7 +1028,7 @@ function topicArns(topicNames) {
793
1028
  .sort()
794
1029
  .map(
795
1030
  (topic) =>
796
- `arn:aws:sns:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_serviceTag}'}_${'${self:custom.iz_stage}'}_${topic}_Out`
1031
+ `arn:aws:sns:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_serviceTag}'}_${'${self:custom.iz_stage}'}_${topic}`
797
1032
  );
798
1033
  }
799
1034
 
@@ -899,11 +1134,14 @@ function keyName(identifier) {
899
1134
  );
900
1135
  }
901
1136
 
1137
+ const VALID_CRUD_GENERATED_BY = new Set([
1138
+ 'user',
1139
+ 'systemWithUser',
1140
+ 'systemNoUser'
1141
+ ]);
1142
+
902
1143
  function isCrudObject(object) {
903
- return (
904
- object?.generatedBy === 'userGenerated' ||
905
- object?.generatedBy === 'systemGenerated'
906
- );
1144
+ return VALID_CRUD_GENERATED_BY.has(object?.generatedBy);
907
1145
  }
908
1146
  function hasSelfRbacAddOn(object) {
909
1147
  return (