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

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 (31) 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 +1 -0
  5. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +15 -4
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +10 -4
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +10 -4
  8. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +36 -4
  9. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +35 -3
  10. package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
  11. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -2
  12. package/src/emit/rbac/emitRbacApplicationArtifacts.js +102 -6
  13. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +34 -4
  14. package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
  15. package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +106 -0
  16. package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
  17. package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
  18. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +12 -12
  19. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +12 -0
  20. package/src/emit/resource/emitResourceArtifacts.js +43 -2
  21. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +4 -2
  22. package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
  23. package/src/generate.js +18 -12
  24. package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
  25. package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
  26. package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
  27. package/test/createTargetRoleHandler.test.js +42 -1
  28. package/test/crudApplicationArtifacts.test.js +53 -0
  29. package/test/dynamicRbacFlowSchemas.test.js +5 -2
  30. package/test/publicApi.test.js +18 -14
  31. package/test/resourceArtifacts.test.js +3 -0
@@ -52,7 +52,7 @@ async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
52
52
  try {
53
53
  const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
54
54
  const result = await dynamodbSharedLib.getItem(_izContext, tableName, {
55
- submittedByUserId,
55
+ userId: submittedByUserId,
56
56
  cacheKey: cacheSubKey
57
57
  });
58
58
  if (result && result.ttlTimestamp && result.ttlTimestamp > Math.floor(Date.now() / 1000)) {
@@ -77,7 +77,7 @@ async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
77
77
  const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
78
78
  const ttlTimestamp = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
79
79
  await dynamodbSharedLib.putItem(_izContext, tableName, {
80
- submittedByUserId,
80
+ userId: submittedByUserId,
81
81
  cacheKey: cacheSubKey,
82
82
  permission: value.returnValue ? value.returnValue.permission : value.permission,
83
83
  roleIdKey: value.returnValue ? value.returnValue.roleIdKey : value.roleIdKey,
@@ -133,7 +133,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
133
133
  throw new Error('Missing flowTypeConcat in requestParams');
134
134
  }
135
135
 
136
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:', {
136
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:`, {
137
137
  submittedByUserId,
138
138
  targetId,
139
139
  flowTypeConcat,
@@ -175,7 +175,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
175
175
  // 1. Check L1 In-Memory Cache
176
176
  const l1Hit = _getL1Cache(fullCacheKey);
177
177
  if (l1Hit) {
178
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:', l1Hit);
178
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:`, l1Hit);
179
179
  await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
180
180
  return;
181
181
  }
@@ -183,19 +183,19 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
183
183
  // 2. Check L2 Shared DynamoDB Cache
184
184
  const l2Hit = await _getL2Cache(_izContext, submittedByUserId, cacheSubKey);
185
185
  if (l2Hit) {
186
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:', l2Hit);
186
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:`, l2Hit);
187
187
  _setL1Cache(fullCacheKey, l2Hit);
188
188
  await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
189
189
  return;
190
190
  }
191
191
 
192
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB');
192
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB`);
193
193
 
194
194
  // 3. Fallback to DB Query
195
195
  const userRolesResult = await dynamodbSharedLib.query(
196
196
  _izContext,
197
197
  dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
198
- { submittedByUserId }
198
+ { userId: submittedByUserId }
199
199
  );
200
200
 
201
201
  let userRoles = [];
@@ -205,7 +205,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
205
205
  userRoles = userRolesResult.Items;
206
206
  }
207
207
 
208
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:', { userRoles });
208
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:`, { userRoles });
209
209
 
210
210
  const targetIdPrefix = `${targetId}_`;
211
211
  const targetRoles = [];
@@ -215,13 +215,13 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
215
215
  }
216
216
  }
217
217
 
218
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:', {
218
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:`, {
219
219
  targetIdPrefix,
220
220
  targetRoles
221
221
  });
222
222
 
223
223
  if (targetRoles.length === 0) {
224
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied');
224
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied`);
225
225
  const result = {
226
226
  returnValue: {
227
227
  permission: false,
@@ -250,7 +250,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
250
250
  })
251
251
  );
252
252
 
253
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:', {
253
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:`, {
254
254
  rolePermissions
255
255
  });
256
256
 
@@ -278,7 +278,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
278
278
  status: 'Completed'
279
279
  };
280
280
 
281
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:', cacheResult);
281
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:`, cacheResult);
282
282
 
283
283
  // Store in L1 & L2 Cache
284
284
  _setL1Cache(fullCacheKey, cacheResult);
@@ -26,6 +26,18 @@ import { sns } from '@izara_project/izara-core-library-external-request';
26
26
  export default async function <%- flowTag %>(_izContext, requestParams) {
27
27
  try {
28
28
  const { roleIdKey, userId } = requestParams;
29
+ <% if (operation === 'Delete') { -%>
30
+ if (roleIdKey && roleIdKey.endsWith('_owner')) {
31
+ const remainingUsers = await dynamodbSharedLib.query(
32
+ _izContext,
33
+ dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
34
+ { roleIdKey }
35
+ );
36
+ if (remainingUsers?.Items?.length <= 1) {
37
+ throw new Error('Cannot remove the last remaining owner');
38
+ }
39
+ }
40
+ <% } -%>
29
41
  await dynamodbSharedLib.transactWriteItems(_izContext, [
30
42
  {
31
43
  action: '<%- action %>',
@@ -31,14 +31,17 @@ const RELATIONSHIP_ACTIONS = [
31
31
  ];
32
32
  const RBAC_ACTIONS = [
33
33
  'CreateTargetRole',
34
- 'ListTargetRole',
34
+ 'UpdateTargetRole',
35
35
  'DeleteTargetRole',
36
+ 'GetTargetRole',
37
+ 'ListTargetRole',
36
38
  'CreateRolePermissions',
37
39
  'ListRolePermissions',
38
40
  'DeleteRolePermissions',
39
41
  'CreateUserRole',
40
42
  'ListUserInRoles',
41
43
  'DeleteUserFromRole',
44
+ 'GetMyPermissions',
42
45
  'ValidatorRbac',
43
46
  'CreateRoleLimit',
44
47
  'ListRoleLimits',
@@ -728,7 +731,10 @@ function roleStatements(state, roleName) {
728
731
  addStatement(
729
732
  statements,
730
733
  ['sns:Publish'],
731
- topicArns(roleTopicNames(state, roleName))
734
+ [
735
+ ...topicArns(roleTopicNames(state, roleName)),
736
+ ...roleExternalTopicArns(state, roleName)
737
+ ]
732
738
  );
733
739
  if (state.webSocketRoles.has(roleName)) {
734
740
  statements.push({
@@ -766,6 +772,7 @@ function roleTableNames(state, roleName) {
766
772
  ...(state.rbac
767
773
  ? [
768
774
  'TargetRoles',
775
+ 'RolePermissions',
769
776
  'RoleUsers',
770
777
  'UserRoles',
771
778
  'RoleLimit',
@@ -925,6 +932,9 @@ function roleTopicNames(state, roleName) {
925
932
  topics.push(`${action}_In`, `${action}_Out`);
926
933
  }
927
934
  }
935
+ if (state.rbac) {
936
+ topics.push('ValidatorRbac_In');
937
+ }
928
938
  return topics;
929
939
  }
930
940
  if (roleName === 'FlowRelationshipRole') {
@@ -937,6 +947,9 @@ function roleTopicNames(state, roleName) {
937
947
  topics.push(`${action}_In`, `${action}_Out`);
938
948
  }
939
949
  }
950
+ if (state.rbac) {
951
+ topics.push('ValidatorRbac_In');
952
+ }
940
953
  return topics;
941
954
  }
942
955
  if (roleName === 'FlowRbacRole') {
@@ -1023,6 +1036,34 @@ function roleExternalDynamoTables(state, roleName) {
1023
1036
  return [];
1024
1037
  }
1025
1038
 
1039
+ function roleExternalTopicArns(state, roleName) {
1040
+ if (!state.rbac) {
1041
+ return [];
1042
+ }
1043
+ const extraTags = getRbacValidatorServiceTags(state).filter(
1044
+ (tag) => tag && tag !== 'Rbac'
1045
+ );
1046
+ const extraTopics = extraTags.map(
1047
+ (tag) =>
1048
+ `arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_ValidatorRbac_In`
1049
+ );
1050
+ if (roleName === 'FlowObjectsRole') {
1051
+ return [
1052
+ `arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ValidatorRbac_In`,
1053
+ `arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ConfirmUsage_In`,
1054
+ `arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_CancelUsage_In`,
1055
+ ...extraTopics
1056
+ ];
1057
+ }
1058
+ if (roleName === 'FlowRbacRole' || roleName === 'FlowRelationshipRole') {
1059
+ return [
1060
+ `arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ValidatorRbac_In`,
1061
+ ...extraTopics
1062
+ ];
1063
+ }
1064
+ return [];
1065
+ }
1066
+
1026
1067
  function queueArns(queueNames) {
1027
1068
  return [...new Set(queueNames)]
1028
1069
  .sort()
@@ -58,7 +58,8 @@ export function generatedLambdaRole() {
58
58
  Resource: [
59
59
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`,
60
60
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', 'Rbac')}`,
61
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
61
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% if (rbac) { %>,
62
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
62
63
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
63
64
  ]
64
65
  }<% } %>
@@ -77,7 +78,8 @@ export function generatedLambdaRole() {
77
78
  Effect: 'Allow',
78
79
  Action: ['sns:Publish'],
79
80
  Resource: [
80
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
81
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`,
82
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
81
83
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
82
84
  ]
83
85
  }
@@ -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,6 +9,7 @@ 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',
@@ -39,7 +40,7 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
39
40
  const rootPath = await createProject(true);
40
41
  const result = await generateCreateTargetRoleHandler(rootPath);
41
42
 
42
- assert.equal(result.files.length, 60);
43
+ assert.equal(result.files.length, 70);
43
44
  assert.match(
44
45
  await readFile(join(rootPath, result.files[0].path), 'utf8'),
45
46
  /CreateTargetRole/
@@ -48,6 +49,46 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
48
49
  await readFile(join(rootPath, result.files[0].path), 'utf8'),
49
50
  /roleName = 'owner'/
50
51
  );
52
+ assert.match(
53
+ await readFile(
54
+ join(
55
+ rootPath,
56
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_Main.js'
57
+ ),
58
+ 'utf8'
59
+ ),
60
+ /Cannot modify system owner role/
61
+ );
62
+ assert.match(
63
+ await readFile(
64
+ join(
65
+ rootPath,
66
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_Main.js'
67
+ ),
68
+ 'utf8'
69
+ ),
70
+ /Cannot delete system owner role/
71
+ );
72
+ assert.match(
73
+ await readFile(
74
+ join(
75
+ rootPath,
76
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_Main.js'
77
+ ),
78
+ 'utf8'
79
+ ),
80
+ /GetTargetRole/
81
+ );
82
+ assert.match(
83
+ await readFile(
84
+ join(
85
+ rootPath,
86
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_Main.js'
87
+ ),
88
+ 'utf8'
89
+ ),
90
+ /GetMyPermissions/
91
+ );
51
92
  assert.match(
52
93
  await readFile(join(rootPath, result.files[1].path), 'utf8'),
53
94
  /CreateTargetRoleHdrSqs/
@@ -357,6 +357,59 @@ test('logs warning when ObjectSchema has invalid or non-CRUD generatedBy in crud
357
357
  );
358
358
  });
359
359
 
360
+ test('emits targetIdentifiers consistency check and fallback in Update_Main.js and Delete_Main.js', async () => {
361
+ const rootPath = await createProject(true);
362
+ await generateCrudApplicationArtifacts(rootPath, {
363
+ serviceTag: 'orders'
364
+ });
365
+
366
+ const updateMain = await readFile(
367
+ join(
368
+ rootPath,
369
+ 'app/src/generatedCode/FlowObjects/Update/source/Update_Main.js'
370
+ ),
371
+ 'utf8'
372
+ );
373
+ assert.match(updateMain, /targetIdentifiers\.\$\{key\} must match objInstanceFull\.identifiers\.\$\{key\}/);
374
+ assert.match(updateMain, /if \(!objectSchema\.hasOwnProperty\('belongTo'\)\)/);
375
+ assert.doesNotMatch(updateMain, /requestParams\.targetIdentifiers\s*=/);
376
+
377
+ const deleteMain = await readFile(
378
+ join(
379
+ rootPath,
380
+ 'app/src/generatedCode/FlowObjects/Delete/source/Delete_Main.js'
381
+ ),
382
+ 'utf8'
383
+ );
384
+ assert.match(deleteMain, /targetIdentifiers\.\$\{key\} must match identifiers\.\$\{key\}/);
385
+ assert.match(deleteMain, /if \(!objectSchema\.hasOwnProperty\('belongTo'\)\)/);
386
+ assert.doesNotMatch(deleteMain, /requestParams\.targetIdentifiers\s*=/);
387
+ });
388
+
389
+ test('emits targetIdentifiers passed directly to checkRbacPermission options in RBAC project', async () => {
390
+ const rootPath = await createRbacProject();
391
+ await generateCrudApplicationArtifacts(rootPath, {
392
+ serviceTag: 'orders'
393
+ });
394
+
395
+ const updateMain = await readFile(
396
+ join(
397
+ rootPath,
398
+ 'app/src/generatedCode/FlowObjects/Update/source/Update_Main.js'
399
+ ),
400
+ 'utf8'
401
+ );
402
+ assert.match(updateMain, /checkRbacPermission\([\s\S]*targetIdentifiers,[\s\S]*rbacObjType/);
403
+
404
+ const deleteMain = await readFile(
405
+ join(
406
+ rootPath,
407
+ 'app/src/generatedCode/FlowObjects/Delete/source/Delete_Main.js'
408
+ ),
409
+ 'utf8'
410
+ );
411
+ assert.match(deleteMain, /checkRbacPermission\([\s\S]*targetIdentifiers,[\s\S]*rbacObjType/);
412
+ });
360
413
 
361
414
  async function createProject(withCrudObject) {
362
415
  const rootPath = await mkdtemp(
@@ -10,14 +10,17 @@ 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 = [
@@ -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 () => {
@@ -365,6 +365,9 @@ test('emits CreateNodeCallbackHdrSqs in FlowObjectsRole when object has graph an
365
365
  const role = roleBlock(iam, 'FlowObjectsRole');
366
366
  assert.match(role, /CreateNodeCallbackHdrSqs/);
367
367
  assert.match(role, /CreateNodeCallbackHdrSqsDLQ/);
368
+ assert.match(role, /RolePermissions/);
369
+ assert.match(role, /ValidatorRbac_In/);
370
+ assert.match(role, /Rbac_\$\{self:custom\.iz_stage\}_ValidatorRbac_In/);
368
371
  });
369
372
 
370
373
  function roleBlock(iam, roleName) {