@izara_project/izara-core-generate-service-code 1.0.85 → 1.0.87

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 (43) hide show
  1. package/package.json +1 -1
  2. package/src/compile/compileProject.js +1 -2
  3. package/src/compile/loadSchemas.js +29 -17
  4. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +2 -1
  5. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +32 -14
  6. package/src/emit/crud/crudActionPlan.js +1 -1
  7. package/src/emit/crud/emitCrudApplicationArtifacts.js +10 -4
  8. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +16 -8
  9. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +12 -3
  10. package/src/emit/crud/templates/CreateLogicalLib.ejs +85 -20
  11. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +48 -7
  12. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +47 -6
  13. package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
  14. package/src/emit/function/functionYamlArtifact.js +4 -2
  15. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -5
  16. package/src/emit/rbac/emitRbacApplicationArtifacts.js +70 -89
  17. package/src/emit/rbac/templates/CancelUsage_Main.ejs +14 -10
  18. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +14 -10
  19. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +56 -16
  20. package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
  21. package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +110 -0
  22. package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
  23. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +76 -34
  24. package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
  25. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +24 -21
  26. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +12 -0
  27. package/src/emit/resource/emitResourceArtifacts.js +75 -8
  28. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +4 -2
  29. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +14 -18
  30. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +4 -2
  31. package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
  32. package/src/generate.js +18 -12
  33. package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
  34. package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
  35. package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
  36. package/test/createTargetRoleHandler.test.js +42 -15
  37. package/test/crudApplicationArtifacts.test.js +62 -3
  38. package/test/customFlowApplicationArtifacts.test.js +3 -5
  39. package/test/dynamicCrudFlowSchemas.test.js +5 -17
  40. package/test/dynamicRbacFlowSchemas.test.js +5 -5
  41. package/test/functionYamlArtifact.test.js +4 -2
  42. package/test/publicApi.test.js +18 -14
  43. package/test/resourceArtifacts.test.js +7 -3
@@ -138,6 +138,18 @@ Resources:
138
138
  KeyType: RANGE
139
139
 
140
140
  <% if (hasRbac) { %>
141
+ RolesTable:
142
+ Type: 'AWS::DynamoDB::Table'
143
+ Properties:
144
+ TableName: ${self:custom.iz_resourcePrefix}Roles
145
+ BillingMode: PAY_PER_REQUEST
146
+ AttributeDefinitions:
147
+ - AttributeName: roleIdKey
148
+ AttributeType: S
149
+ KeySchema:
150
+ - AttributeName: roleIdKey
151
+ KeyType: HASH
152
+
141
153
  TargetRolesTable:
142
154
  Type: 'AWS::DynamoDB::Table'
143
155
  Properties:
@@ -221,22 +233,6 @@ Resources:
221
233
  AttributeName: ttlTimestamp
222
234
  Enabled: true
223
235
 
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
236
  RunningTotalUsageTable:
241
237
  Type: 'AWS::DynamoDB::Table'
242
238
  Properties:
@@ -245,12 +241,12 @@ Resources:
245
241
  AttributeDefinitions:
246
242
  - AttributeName: identifiersConcat
247
243
  AttributeType: S
248
- - AttributeName: limitTag
244
+ - AttributeName: flowTypeConcat
249
245
  AttributeType: S
250
246
  KeySchema:
251
247
  - AttributeName: identifiersConcat
252
248
  KeyType: HASH
253
- - AttributeName: limitTag
249
+ - AttributeName: flowTypeConcat
254
250
  KeyType: RANGE
255
251
 
256
252
  RunningTotalReservedUsageTable:
@@ -16,6 +16,8 @@ WebSocketCompleteHdrSqs:
16
16
  - body:
17
17
  MessageAttributes:
18
18
  callingFlow:
19
- Value:
20
- - exists: false
19
+ - exists: false
20
+ - body:
21
+ MessageAttributes:
22
+ - exists: false
21
23
  #<#/WebSocketCompleteHdrSqsFilterPatterns#>
@@ -17,14 +17,17 @@ const RELATIONSHIP_ACTIONS = [
17
17
  ];
18
18
  const RBAC_SHARED_FLOW_TAGS = [
19
19
  'CreateTargetRole',
20
- 'ListTargetRole',
20
+ 'UpdateTargetRole',
21
21
  'DeleteTargetRole',
22
+ 'GetTargetRole',
23
+ 'ListTargetRole',
22
24
  'CreateRolePermissions',
23
25
  'ListRolePermissions',
24
26
  'DeleteRolePermissions',
25
27
  'CreateUserRole',
26
28
  'ListUserInRoles',
27
- 'DeleteUserFromRole'
29
+ 'DeleteUserFromRole',
30
+ 'GetMyPermissions'
28
31
  ];
29
32
 
30
33
  export function systemTextSchemaManagedPaths() {
package/src/generate.js CHANGED
@@ -64,12 +64,10 @@ import {
64
64
 
65
65
  export async function generate(rootPath, options = {}) {
66
66
  const startedAt = Date.now();
67
- const logger = options.logger;
68
- logger?.info?.(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
67
+ console.log(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
69
68
  try {
70
69
  const project = compileProject({ ...options, rootPath });
71
70
  const schemaLoadOptions = {
72
- logger,
73
71
  skipValidation: options.skipValidation
74
72
  };
75
73
  const [rawObjects, relationships, flows, notifications] = await Promise.all([
@@ -92,8 +90,10 @@ export async function generate(rootPath, options = {}) {
92
90
  relationships,
93
91
  schemaBucketName
94
92
  );
95
- logger?.debug?.(
96
- `[DEBUG] [Generate] STATUS=SCHEMAS_LOADED | objects=${objects.length} relationships=${relationships.length} flows=${flows.length}`
93
+ console.log(
94
+ `[DEBUG] [Generate] STATUS=SCHEMAS_LOADED | ` +
95
+ `objects=${objects.length} ` +
96
+ `relationships=${relationships.length} flows=${flows.length}`
97
97
  );
98
98
  const groups = await _emitGroups({
99
99
  project,
@@ -103,10 +103,14 @@ export async function generate(rootPath, options = {}) {
103
103
  notifications,
104
104
  objectGraphServiceTags,
105
105
  relationshipGraphServiceTags,
106
- logger
106
+ logger: options.logger
107
+ });
108
+ const scopeCounts = groups.map((group) => {
109
+ return `${group.scope}=${group.artifacts.length}`;
107
110
  });
108
- logger?.debug?.(
109
- `[DEBUG] [Generate] STATUS=ARTIFACTS_EMITTED | ${groups.map((group) => `${group.scope}=${group.artifacts.length}`).join(' ')}`
111
+ const emittedScopeSummary = scopeCounts.join(' ');
112
+ console.log(
113
+ `[DEBUG] [Generate] STATUS=ARTIFACTS_EMITTED | ${emittedScopeSummary}`
110
114
  );
111
115
  let files = [];
112
116
  if (groups.length > 0) {
@@ -123,14 +127,16 @@ export async function generate(rootPath, options = {}) {
123
127
  );
124
128
  }
125
129
  const durationMs = Date.now() - startedAt;
126
- logger?.info?.(
127
- `[INFO] [Generate] STATUS=FINISHED | files=${files.length} durationMs=${durationMs}`
130
+ console.log(
131
+ `[INFO] [Generate] STATUS=FINISHED | ` +
132
+ `files=${files.length} durationMs=${durationMs}`
128
133
  );
129
134
 
130
135
  return { project, durationMs, files };
131
136
  } catch (error) {
132
- logger?.error?.(
133
- `[ERROR] [Generate] STATUS=FAILED | durationMs=${Date.now() - startedAt}`,
137
+ console.log(
138
+ `[ERROR] [Generate] STATUS=FAILED | ` +
139
+ `durationMs=${Date.now() - startedAt}`,
134
140
  error
135
141
  );
136
142
  throw error;
@@ -103,7 +103,7 @@ async function generateCodeWithTemplate(_izContext, savePath, settings) {
103
103
 
104
104
  return allCreateSource;
105
105
  } catch (error) {
106
- _izContext.logger.error(
106
+ console.log(
107
107
  'Error generating code with template propertyNode schema:',
108
108
  error
109
109
  );
@@ -71,7 +71,7 @@ async function generateCodeWithTemplate(_izContext, savePath, settings) {
71
71
 
72
72
  return allCreateSource;
73
73
  } catch (error) {
74
- _izContext.logger.error(
74
+ console.log(
75
75
  'Error generating code with template propertyNode schema:',
76
76
  error
77
77
  );
@@ -17,10 +17,16 @@ import fs from 'fs';
17
17
  import path from 'path';
18
18
 
19
19
  import { s3 } from '@izara_project/izara-core-library-s3';
20
- import Logger from '@izara_project/izara-core-library-logger';
20
+
21
+ const consoleLogger = {
22
+ debug: console.log,
23
+ info: console.log,
24
+ warn: console.log,
25
+ error: console.error
26
+ };
21
27
 
22
28
  const DEFAULT_BUCKET = 'integrationtest-config-us-east-2';
23
- const _izContext = { Logger };
29
+ const _izContext = { logger: consoleLogger, Logger: consoleLogger };
24
30
 
25
31
  function _getAllFiles(dirPath, filePaths = []) {
26
32
  if (!fs.existsSync(dirPath)) {
@@ -9,21 +9,18 @@ import { generateCreateTargetRoleHandler } from '../src/emit/rbac/generateCreate
9
9
  const roots = new Set();
10
10
  const OWN_TOPIC_FLOWS = [
11
11
  'CreateTargetRole',
12
+ 'UpdateTargetRole',
12
13
  'CreateRolePermissions',
13
14
  'DeleteRolePermissions',
14
15
  'DeleteTargetRole',
15
16
  'CreateUserRole',
16
17
  'DeleteUserFromRole',
17
- 'CreateRoleLimit',
18
- 'DeleteRoleLimit',
19
18
  'ReservedDynamicUsage',
20
19
  'ConfirmUsage',
21
20
  'CancelUsage'
22
21
  ];
23
22
 
24
23
  const LIMIT_FLOWS = [
25
- 'CreateRoleLimit',
26
- 'DeleteRoleLimit',
27
24
  'ReservedDynamicUsage',
28
25
  'ConfirmUsage',
29
26
  'CancelUsage'
@@ -39,7 +36,7 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
39
36
  const rootPath = await createProject(true);
40
37
  const result = await generateCreateTargetRoleHandler(rootPath);
41
38
 
42
- assert.equal(result.files.length, 60);
39
+ assert.equal(result.files.length, 59);
43
40
  assert.match(
44
41
  await readFile(join(rootPath, result.files[0].path), 'utf8'),
45
42
  /CreateTargetRole/
@@ -48,6 +45,46 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
48
45
  await readFile(join(rootPath, result.files[0].path), 'utf8'),
49
46
  /roleName = 'owner'/
50
47
  );
48
+ assert.match(
49
+ await readFile(
50
+ join(
51
+ rootPath,
52
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_Main.js'
53
+ ),
54
+ 'utf8'
55
+ ),
56
+ /Cannot modify system owner role/
57
+ );
58
+ assert.match(
59
+ await readFile(
60
+ join(
61
+ rootPath,
62
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_Main.js'
63
+ ),
64
+ 'utf8'
65
+ ),
66
+ /Cannot delete system owner role/
67
+ );
68
+ assert.match(
69
+ await readFile(
70
+ join(
71
+ rootPath,
72
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_Main.js'
73
+ ),
74
+ 'utf8'
75
+ ),
76
+ /GetTargetRole/
77
+ );
78
+ assert.match(
79
+ await readFile(
80
+ join(
81
+ rootPath,
82
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_Main.js'
83
+ ),
84
+ 'utf8'
85
+ ),
86
+ /GetMyPermissions/
87
+ );
51
88
  assert.match(
52
89
  await readFile(join(rootPath, result.files[1].path), 'utf8'),
53
90
  /CreateTargetRoleHdrSqs/
@@ -89,16 +126,6 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
89
126
  ),
90
127
  /Math\.min\(maxIncrease, minDecrease\)/
91
128
  );
92
- assert.match(
93
- await readFile(
94
- join(
95
- rootPath,
96
- 'app/src/generatedCode/FlowLimit/CreateRoleLimit/source/CreateRoleLimit_Main.js'
97
- ),
98
- 'utf8'
99
- ),
100
- /actionType/
101
- );
102
129
  const rbacYaml = await readFile(
103
130
  join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'),
104
131
  'utf8'
@@ -239,7 +239,7 @@ test('emits a single Complete RBAC handler for Update without Graph', async () =
239
239
  );
240
240
  });
241
241
 
242
- test('emits RbacCallback and Complete handlers for Update with Graph', async () => {
242
+ test('emits Complete handlers and inlines sync RBAC for Update with Graph', async () => {
243
243
  const rootPath = await createRbacProject(false, true);
244
244
  const result = await generateCrudApplicationArtifacts(rootPath, {
245
245
  serviceTag: 'orders'
@@ -248,19 +248,25 @@ test('emits RbacCallback and Complete handlers for Update with Graph', async ()
248
248
  .map((file) => file.path)
249
249
  .filter((path) => path.includes('/FlowObjects/Update/source/'));
250
250
 
251
- assert.ok(updatePaths.includes(
251
+ assert.ok(!updatePaths.includes(
252
252
  'app/src/generatedCode/FlowObjects/Update/source/UpdateRbacCallback_Main.js'
253
253
  ));
254
254
  assert.ok(updatePaths.includes(
255
255
  'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
256
256
  ));
257
+ const updateMainContent = await readFile(
258
+ join(rootPath, 'app/src/generatedCode/FlowObjects/Update/source/Update_Main.js'),
259
+ 'utf8'
260
+ );
261
+ assert.match(updateMainContent, /checkRbacPermission/);
262
+ assert.match(updateMainContent, /sync: true/);
257
263
  execFileSync(
258
264
  process.execPath,
259
265
  [
260
266
  '--check',
261
267
  join(
262
268
  rootPath,
263
- 'app/src/generatedCode/FlowObjects/Update/source/UpdateRbacCallback_Main.js'
269
+ 'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
264
270
  )
265
271
  ],
266
272
  { stdio: 'inherit' }
@@ -357,6 +363,59 @@ test('logs warning when ObjectSchema has invalid or non-CRUD generatedBy in crud
357
363
  );
358
364
  });
359
365
 
366
+ test('emits targetIdentifiers consistency check and fallback in Update_Main.js and Delete_Main.js', async () => {
367
+ const rootPath = await createProject(true);
368
+ await generateCrudApplicationArtifacts(rootPath, {
369
+ serviceTag: 'orders'
370
+ });
371
+
372
+ const updateMain = await readFile(
373
+ join(
374
+ rootPath,
375
+ 'app/src/generatedCode/FlowObjects/Update/source/Update_Main.js'
376
+ ),
377
+ 'utf8'
378
+ );
379
+ assert.match(updateMain, /targetIdentifiers\.\$\{key\} must match objInstanceFull\.identifiers\.\$\{key\}/);
380
+ assert.match(updateMain, /if \(!objectSchema\.hasOwnProperty\('belongTo'\)\)/);
381
+ assert.doesNotMatch(updateMain, /requestParams\.targetIdentifiers\s*=/);
382
+
383
+ const deleteMain = await readFile(
384
+ join(
385
+ rootPath,
386
+ 'app/src/generatedCode/FlowObjects/Delete/source/Delete_Main.js'
387
+ ),
388
+ 'utf8'
389
+ );
390
+ assert.match(deleteMain, /targetIdentifiers\.\$\{key\} must match identifiers\.\$\{key\}/);
391
+ assert.match(deleteMain, /if \(!objectSchema\.hasOwnProperty\('belongTo'\)\)/);
392
+ assert.doesNotMatch(deleteMain, /requestParams\.targetIdentifiers\s*=/);
393
+ });
394
+
395
+ test('emits targetIdentifiers passed directly to checkRbacPermission options in RBAC project', async () => {
396
+ const rootPath = await createRbacProject();
397
+ await generateCrudApplicationArtifacts(rootPath, {
398
+ serviceTag: 'orders'
399
+ });
400
+
401
+ const updateMain = await readFile(
402
+ join(
403
+ rootPath,
404
+ 'app/src/generatedCode/FlowObjects/Update/source/Update_Main.js'
405
+ ),
406
+ 'utf8'
407
+ );
408
+ assert.match(updateMain, /checkRbacPermission\([\s\S]*targetIdentifiers,[\s\S]*rbacObjType/);
409
+
410
+ const deleteMain = await readFile(
411
+ join(
412
+ rootPath,
413
+ 'app/src/generatedCode/FlowObjects/Delete/source/Delete_Main.js'
414
+ ),
415
+ 'utf8'
416
+ );
417
+ assert.match(deleteMain, /checkRbacPermission\([\s\S]*targetIdentifiers,[\s\S]*rbacObjType/);
418
+ });
360
419
 
361
420
  async function createProject(withCrudObject) {
362
421
  const rootPath = await mkdtemp(
@@ -895,7 +895,7 @@ test('registers SQS queue, SNS subscription, and SNS out topic for custom flowSc
895
895
  );
896
896
  });
897
897
 
898
- test('renders RBAC Validator subscriptions in InitialSetupConfig when object has belongTo to external RBAC service', async () => {
898
+ test('uses synchronous RBAC without obsolete callback SQS subscriptions in InitialSetupConfig', async () => {
899
899
  const rootPath = await createProject();
900
900
  await writeFile(
901
901
  join(rootPath, 'app/src/schemas/ObjectSchemas/Member.js'),
@@ -920,10 +920,8 @@ test('renders RBAC Validator subscriptions in InitialSetupConfig when object has
920
920
  ),
921
921
  'utf8'
922
922
  );
923
- assert.match(initialSetupConfigContent, /serviceTag: 'UserAccount'/);
924
- assert.match(initialSetupConfigContent, /topicName: 'ValidatorRbac_Out'/);
925
- assert.match(initialSetupConfigContent, /sqsEndpoint: 'UpdateRbacCallbackHdrSqs'/);
926
- assert.match(initialSetupConfigContent, /sqsEndpoint: 'DeleteRbacCallbackHdrSqs'/);
923
+ assert.doesNotMatch(initialSetupConfigContent, /sqsEndpoint: 'UpdateRbacCallbackHdrSqs'/);
924
+ assert.doesNotMatch(initialSetupConfigContent, /sqsEndpoint: 'DeleteRbacCallbackHdrSqs'/);
927
925
  });
928
926
 
929
927
  test('deduplicates duplicate nextSteps topics across flowSteps in InitialSetupConfig', async () => {
@@ -142,7 +142,7 @@ test('generates a direct Complete step for RBAC-only Update flows', async () =>
142
142
  assert.equal(update.flowSteps[1].properties.length, 0);
143
143
  });
144
144
 
145
- test('generates RbacCallback and Complete for Create when RBAC and Graph are present', async () => {
145
+ test('generates CreateNodeCallback and Complete for Create when RBAC and Graph are present', async () => {
146
146
  const rootPath = await createProject({
147
147
  generatedBy: 'user',
148
148
  });
@@ -160,13 +160,12 @@ test('generates RbacCallback and Complete for Create when RBAC and Graph are pre
160
160
  const create = await readGeneratedFlow(rootPath, 'Create');
161
161
  assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
162
162
  'Start',
163
- 'RbacCallback',
164
163
  'CreateNodeCallback',
165
164
  'Complete'
166
165
  ]);
167
166
  });
168
167
 
169
- test('uses the renamed RBAC callback step when Graph storage is present', async () => {
168
+ test('uses CreateNodeCallback step when Graph storage is present with sync RBAC', async () => {
170
169
  const rootPath = await mkdtemp(
171
170
  join(tmpdir(), 'generate-service-code-v2-crud-rbac-graph-')
172
171
  );
@@ -186,11 +185,10 @@ test('uses the renamed RBAC callback step when Graph storage is present', async
186
185
  const create = await readGeneratedFlow(rootPath, 'Create');
187
186
  assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
188
187
  'Start',
189
- 'RbacCallback',
190
188
  'CreateNodeCallback',
191
189
  'Complete'
192
190
  ]);
193
- assert.deepEqual(create.flowSteps[2].properties, [
191
+ assert.deepEqual(create.flowSteps[1].properties, [
194
192
  'returnValue',
195
193
  'parentFlowId',
196
194
  'status',
@@ -199,7 +197,7 @@ test('uses the renamed RBAC callback step when Graph storage is present', async
199
197
  ]);
200
198
  });
201
199
 
202
- test('chains RbacCallback and Complete for Update with RBAC and Graph', async () => {
200
+ test('chains Start and Complete for Update with RBAC and Graph', async () => {
203
201
  const rootPath = await mkdtemp(
204
202
  join(tmpdir(), 'generate-service-code-v2-crud-update-rbac-graph-')
205
203
  );
@@ -219,19 +217,9 @@ test('chains RbacCallback and Complete for Update with RBAC and Graph', async ()
219
217
  const update = await readGeneratedFlow(rootPath, 'Update');
220
218
  assert.deepEqual(update.flowSteps.map((step) => step.stepName), [
221
219
  'Start',
222
- 'RbacCallback',
223
220
  'Complete'
224
221
  ]);
225
222
  assert.deepEqual(update.flowSteps[0].nextSteps, [
226
- { targetType: 'ownTopic', stepName: 'RbacCallback' },
227
- {
228
- targetType: 'extTopic',
229
- serviceTag: 'Rbac',
230
- topicName: 'ValidatorRbac_In',
231
- toStepNames: ['RbacCallback']
232
- }
233
- ]);
234
- assert.deepEqual(update.flowSteps[1].nextSteps, [
235
223
  { targetType: 'ownTopic', stepName: 'Complete' },
236
224
  {
237
225
  targetType: 'extTopic',
@@ -240,7 +228,7 @@ test('chains RbacCallback and Complete for Update with RBAC and Graph', async ()
240
228
  toStepNames: ['Complete']
241
229
  }
242
230
  ]);
243
- assert.deepEqual(update.flowSteps[2].properties, [
231
+ assert.deepEqual(update.flowSteps[1].properties, [
244
232
  'returnValue',
245
233
  'parentFlowId',
246
234
  'status',
@@ -10,20 +10,20 @@ import { generateDynamicRbacFlowSchemas } from '../src/emit/rbac/generateDynamic
10
10
  const projectRoots = new Set();
11
11
  const STATIC_RBAC_ACTIONS = [
12
12
  'CreateTargetRole',
13
- 'ListTargetRole',
13
+ 'UpdateTargetRole',
14
14
  'DeleteTargetRole',
15
+ 'GetTargetRole',
16
+ 'ListTargetRole',
15
17
  'CreateRolePermissions',
16
18
  'ListRolePermissions',
17
19
  'DeleteRolePermissions',
18
20
  'CreateUserRole',
19
21
  'ListUserInRoles',
20
- 'DeleteUserFromRole'
22
+ 'DeleteUserFromRole',
23
+ 'GetMyPermissions'
21
24
  ];
22
25
 
23
26
  const STATIC_LIMIT_ACTIONS = [
24
- 'CreateRoleLimit',
25
- 'ListRoleLimits',
26
- 'DeleteRoleLimit',
27
27
  'ReservedDynamicUsage',
28
28
  'ConfirmUsage',
29
29
  'CancelUsage'
@@ -83,12 +83,14 @@ test('filters all SQS functions by their calling flow', () => {
83
83
  /#<#CreateCompleteHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateCompleteHdrSqs/
84
84
  );
85
85
  assert.match(completion, /#<#\/CreateCompleteHdrSqsFilterPatterns#>/);
86
- assert.match(completion, /exists: false/);
86
+ assert.match(completion, /callingFlow:\s*\n\s*- exists: false/);
87
+ assert.match(completion, /MessageAttributes:\s*\n\s*- exists: false/);
87
88
  assert.match(
88
89
  ordinary,
89
90
  /#<#CreateHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateHdrSqs/
90
91
  );
91
92
  assert.match(ordinary, /#<#\/CreateHdrSqsFilterPatterns#>/);
92
- assert.match(ordinary, /exists: false/);
93
+ assert.match(ordinary, /callingFlow:\s*\n\s*- exists: false/);
94
+ assert.match(ordinary, /MessageAttributes:\s*\n\s*- exists: false/);
93
95
  });
94
96
 
@@ -57,24 +57,28 @@ test('generate commits schemas, application artifacts, and resources in one publ
57
57
  );
58
58
  });
59
59
 
60
- test('generate logs its lifecycle through the supplied logger', async () => {
60
+ test('generate logs its lifecycle through console.log', async () => {
61
61
  const rootPath = await createCrudProject();
62
62
  const messages = [];
63
- const logger = Object.fromEntries(
64
- ['info', 'debug', 'error'].map((level) => [
65
- level,
66
- (message) => messages.push(message)
67
- ])
68
- );
63
+ const originalLog = console.log;
64
+ console.log = (...args) => {
65
+ messages.push(args.join(' '));
66
+ };
69
67
 
70
- await generate(rootPath, { logger });
68
+ try {
69
+ await generate(rootPath);
71
70
 
72
- assert.match(messages[0], /STATUS=STARTED/);
73
- assert.ok(messages.some((message) => /STATUS=SCHEMAS_LOADED/.test(message)));
74
- assert.ok(
75
- messages.some((message) => /STATUS=ARTIFACTS_EMITTED/.test(message))
76
- );
77
- assert.match(messages.at(-1), /STATUS=FINISHED/);
71
+ assert.match(messages[0], /STATUS=STARTED/);
72
+ assert.ok(messages.some((message) => {
73
+ return /STATUS=SCHEMAS_LOADED/.test(message);
74
+ }));
75
+ assert.ok(messages.some((message) => {
76
+ return /STATUS=ARTIFACTS_EMITTED/.test(message);
77
+ }));
78
+ assert.match(messages.at(-1), /STATUS=FINISHED/);
79
+ } finally {
80
+ console.log = originalLog;
81
+ }
78
82
  });
79
83
 
80
84
  test('generate commits RBAC WebSocket entries for own-topic mutation flows', async () => {
@@ -76,8 +76,9 @@ test('emits queues, topics, tables, and IAM roles from schema data', async () =>
76
76
  assert.match(dynamoYaml, /OrdersTable:/);
77
77
  assert.match(dynamoYaml, /#<#OrdersTableSetting#>/);
78
78
  assert.match(dynamoYaml, /#<#\/OrdersTableSetting#>/);
79
+ assert.match(dynamoYaml, /RolesTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}Roles/);
79
80
  assert.match(dynamoYaml, /TargetRolesTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+AttributeName: identifiersConcat/);
80
- assert.match(dynamoYaml, /RoleLimitTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}RoleLimit/);
81
+ assert.doesNotMatch(dynamoYaml, /RoleLimitTable:/);
81
82
  assert.match(dynamoYaml, /RunningTotalUsageTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}RunningTotalUsage/);
82
83
  assert.match(dynamoYaml, /RunningTotalReservedUsageTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}RunningTotalReservedUsage/);
83
84
  const iam = await readFile(
@@ -108,9 +109,9 @@ test('emits queues, topics, tables, and IAM roles from schema data', async () =>
108
109
  roleBlock(iam, 'FlowRbacRole'),
109
110
  /RolePermissions|ProcessCreateTargetRoleHdrSqs/
110
111
  );
111
- assert.match(roleBlock(iam, 'FlowRbacRole'), /RoleLimit/);
112
+ assert.doesNotMatch(roleBlock(iam, 'FlowRbacRole'), /RoleLimit/);
112
113
  assert.match(roleBlock(iam, 'FlowRbacRole'), /RunningTotalUsage/);
113
- assert.match(roleBlock(iam, 'FlowObjectsRole'), /RoleLimit/);
114
+ assert.doesNotMatch(roleBlock(iam, 'FlowObjectsRole'), /RoleLimit/);
114
115
  // Per-object RBAC flow queue (e.g. ValidatorRbacHdrSqs from Order with rbac add-on)
115
116
  assert.match(roleBlock(iam, 'FlowRbacRole'), /ValidatorRbacHdrSqs/);
116
117
 
@@ -365,6 +366,9 @@ test('emits CreateNodeCallbackHdrSqs in FlowObjectsRole when object has graph an
365
366
  const role = roleBlock(iam, 'FlowObjectsRole');
366
367
  assert.match(role, /CreateNodeCallbackHdrSqs/);
367
368
  assert.match(role, /CreateNodeCallbackHdrSqsDLQ/);
369
+ assert.match(role, /RolePermissions/);
370
+ assert.match(role, /ValidatorRbac_In/);
371
+ assert.match(role, /Rbac_\$\{self:custom\.iz_stage\}_ValidatorRbac_In/);
368
372
  });
369
373
 
370
374
  function roleBlock(iam, roleName) {