@izara_project/izara-core-generate-service-code 1.0.83 → 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 (59) hide show
  1. package/package.json +4 -4
  2. package/src/compile/flowValidator.js +13 -2
  3. package/src/compile/loadSchemas.js +116 -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 +38 -5
  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 +50 -19
  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 +33 -8
  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 +1 -1
  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 +52 -3
  25. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  26. package/src/emit/custom/customFlows.js +47 -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 +4 -4
  45. package/src/emit/resource/emitResourceArtifacts.js +342 -108
  46. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +53 -2
  47. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -5
  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 +1 -1
  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 +20 -3
  55. package/test/customFlowApplicationArtifacts.test.js +5 -5
  56. package/test/dynamicCrudFlowSchemas.test.js +91 -4
  57. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  58. package/test/publicApi.test.js +6 -6
  59. package/test/resourceArtifacts.test.js +68 -27
@@ -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
+ }
223
256
  }
224
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`);
283
+ }
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,
@@ -396,35 +533,39 @@ async function renderS3(state) {
396
533
  return `Resources:\n${indentYaml(chunks.join('\n'), 2)}`;
397
534
  }
398
535
 
399
- 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
+
400
551
  const chunks = await Promise.all([
401
- ...state.ownTopics.map((queueName) =>
552
+ ...ownTopics.map((queueName) =>
402
553
  render('ResourceYaml.ejs', { queueName })
403
554
  ),
404
- ...state.sqsQueues.map((queueName) =>
555
+ ...sqsQueues.map((queueName) =>
405
556
  render('ResourceSqsYaml.ejs', { queueName })
406
557
  ),
407
- ...state.dsqQueues.map((queueName) =>
558
+ ...dsqQueues.map((queueName) =>
408
559
  render('ResourceSqsYaml.ejs', { queueName })
409
- ),
410
- ...(state.websocket
411
- ? [
412
- render('ResourceSqsYaml.ejs', {
413
- queueName: 'WebSocketCompleteHdrSqs'
414
- }),
415
- render('ResourceSqsYaml.ejs', {
416
- queueName: 'RegisterCompleteHdrSqs'
417
- })
418
- ]
419
- : [])
560
+ )
420
561
  ]);
421
- const webSocketSubscriptions = await Promise.all(
422
- state.webSocketSubscriptions.map((topicName) =>
562
+ const wsSubs = await Promise.all(
563
+ webSocketSubscriptions.map((topicName) =>
423
564
  renderWebSocketSubscription(topicName)
424
565
  )
425
566
  );
426
- const s3Subscriptions = await Promise.all(
427
- (state.s3Subscriptions ?? []).map(
567
+ const s3Subs = await Promise.all(
568
+ s3Subscriptions.map(
428
569
  ({ subscriptionName, topicName, queueName }) =>
429
570
  render('S3Subscription_Yaml.ejs', {
430
571
  subscriptionName,
@@ -434,16 +575,53 @@ async function renderInSqs(state) {
434
575
  )
435
576
  );
436
577
  const parts = [chunks.join('\n')];
437
- if (webSocketSubscriptions.length > 0) {
438
- parts.push(webSocketSubscriptions.join('\n'));
439
- }
440
- if (s3Subscriptions.length > 0) {
441
- parts.push(s3Subscriptions.join('\n'));
442
- }
443
- 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 '';
444
582
  return `Resources:\n${indentYaml(body, 2)}`;
445
583
  }
446
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
+
447
625
  async function renderOut(state) {
448
626
  const body = (
449
627
  await Promise.all(
@@ -517,7 +695,13 @@ function roleStatements(state, roleName) {
517
695
  statements.push({
518
696
  action: ['s3:GetObject'],
519
697
  resource: [
520
- '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}'
521
705
  ]
522
706
  });
523
707
  }
@@ -572,7 +756,17 @@ function roleTableNames(state, roleName) {
572
756
  ...state.objectTableNames,
573
757
  'AwaitingMultipleSteps',
574
758
  'AwaitingMultipleStepByPending',
575
- 'WebSocketTask'
759
+ 'WebSocketTask',
760
+ ...(state.rbac
761
+ ? [
762
+ 'TargetRoles',
763
+ 'RoleUsers',
764
+ 'UserRoles',
765
+ 'RoleLimit',
766
+ 'RunningTotalUsage',
767
+ 'RunningTotalReservedUsage'
768
+ ]
769
+ : [])
576
770
  ];
577
771
  if (roleName === 'FlowRelationshipRole')
578
772
  return [
@@ -611,7 +805,7 @@ function dynamoActionsForRole(roleName) {
611
805
  return ['dynamodb:GetItem', 'dynamodb:Query', 'dynamodb:UpdateItem'];
612
806
  if (roleName === 'WebSocketMainRole')
613
807
  return ['dynamodb:DeleteItem', 'dynamodb:PutItem', 'dynamodb:Query'];
614
- if (roleName === 'FlowRbacRole')
808
+ if (roleName === 'FlowRbacRole' || roleName === 'FlowObjectsRole')
615
809
  return [...DYNAMO_ACTIONS, 'dynamodb:TransactWriteItems'];
616
810
  return DYNAMO_ACTIONS;
617
811
  }
@@ -677,6 +871,9 @@ function roleQueueNames(state, roleName) {
677
871
  if (includesEvent(flow, 'dsq')) {
678
872
  queueList.push(`${tag}HdrDsq`);
679
873
  }
874
+ if (flowHasRbac(flow, state.objects)) {
875
+ queueList.push(`${tag}RbacCallbackHdrSqs`);
876
+ }
680
877
  queueList.push(
681
878
  ...flowExtTopicSteps([flow]).map(
682
879
  ({ flowTag, stepName }) => `${flowTag}${stepName}HdrSqs`
@@ -708,21 +905,58 @@ function roleDlqQueueNames(state, roleName) {
708
905
 
709
906
  function roleTopicNames(state, roleName) {
710
907
  if (roleName === 'ProcFindDataRole')
711
- return state.outTopics.filter((topic) => topic === 'FindData');
712
- if (roleName === 'FlowObjectsRole')
713
- return state.outTopics.filter((topic) => CRUD_ACTIONS.includes(topic));
714
- if (roleName === 'FlowRelationshipRole')
715
- return state.outTopics.filter((topic) =>
716
- RELATIONSHIP_ACTIONS.includes(topic)
717
- );
718
- if (roleName === 'FlowRbacRole')
719
- 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
+ }
720
945
  const flow = state.flows.find(
721
946
  (candidate) => customFlowRoleName(candidate?.flowTag ?? '') === roleName
722
947
  );
723
- return flow
724
- ? state.outTopics.filter((topic) => topic === upperFirst(flow.flowTag))
725
- : [];
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 [];
726
960
  }
727
961
 
728
962
  function dynamoArns(tableNames) {
@@ -794,7 +1028,7 @@ function topicArns(topicNames) {
794
1028
  .sort()
795
1029
  .map(
796
1030
  (topic) =>
797
- `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}`
798
1032
  );
799
1033
  }
800
1034
 
@@ -903,7 +1137,7 @@ function keyName(identifier) {
903
1137
  const VALID_CRUD_GENERATED_BY = new Set([
904
1138
  'user',
905
1139
  'systemWithUser',
906
- 'systemWithNoUser'
1140
+ 'systemNoUser'
907
1141
  ]);
908
1142
 
909
1143
  function isCrudObject(object) {
@@ -144,12 +144,12 @@ Resources:
144
144
  TableName: ${self:custom.iz_resourcePrefix}TargetRoles
145
145
  BillingMode: PAY_PER_REQUEST
146
146
  AttributeDefinitions:
147
- - AttributeName: targetId
147
+ - AttributeName: identifiersConcat
148
148
  AttributeType: S
149
149
  - AttributeName: roleIdKey
150
150
  AttributeType: S
151
151
  KeySchema:
152
- - AttributeName: targetId
152
+ - AttributeName: identifiersConcat
153
153
  KeyType: HASH
154
154
  - AttributeName: roleIdKey
155
155
  KeyType: RANGE
@@ -220,6 +220,57 @@ Resources:
220
220
  TimeToLiveSpecification:
221
221
  AttributeName: ttlTimestamp
222
222
  Enabled: true
223
+
224
+ RoleLimitTable:
225
+ Type: 'AWS::DynamoDB::Table'
226
+ Properties:
227
+ TableName: ${self:custom.iz_resourcePrefix}RoleLimit
228
+ BillingMode: PAY_PER_REQUEST
229
+ AttributeDefinitions:
230
+ - AttributeName: roleLimitId
231
+ AttributeType: S
232
+ - AttributeName: limitType
233
+ AttributeType: S
234
+ KeySchema:
235
+ - AttributeName: roleLimitId
236
+ KeyType: HASH
237
+ - AttributeName: limitType
238
+ KeyType: RANGE
239
+
240
+ RunningTotalUsageTable:
241
+ Type: 'AWS::DynamoDB::Table'
242
+ Properties:
243
+ TableName: ${self:custom.iz_resourcePrefix}RunningTotalUsage
244
+ BillingMode: PAY_PER_REQUEST
245
+ AttributeDefinitions:
246
+ - AttributeName: identifiersConcat
247
+ AttributeType: S
248
+ - AttributeName: limitTag
249
+ AttributeType: S
250
+ KeySchema:
251
+ - AttributeName: identifiersConcat
252
+ KeyType: HASH
253
+ - AttributeName: limitTag
254
+ KeyType: RANGE
255
+
256
+ RunningTotalReservedUsageTable:
257
+ Type: 'AWS::DynamoDB::Table'
258
+ Properties:
259
+ TableName: ${self:custom.iz_resourcePrefix}RunningTotalReservedUsage
260
+ BillingMode: PAY_PER_REQUEST
261
+ AttributeDefinitions:
262
+ - AttributeName: runningTotalReservedId
263
+ AttributeType: S
264
+ - AttributeName: usedTimestampId
265
+ AttributeType: S
266
+ KeySchema:
267
+ - AttributeName: runningTotalReservedId
268
+ KeyType: HASH
269
+ - AttributeName: usedTimestampId
270
+ KeyType: RANGE
271
+ TimeToLiveSpecification:
272
+ AttributeName: ttlTimestamp
273
+ Enabled: true
223
274
  <% } %>
224
275
  <% if (hasS3) { %>
225
276
  ReservedDataMainTable: