@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.85",
3
+ "version": "1.0.87",
4
4
  "description": "Code for locally generating per service files",
5
5
  "homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
6
6
  "repository": {
@@ -1,4 +1,3 @@
1
- import Logger from '@izara_project/izara-core-library-logger';
2
1
  import { join, resolve } from 'node:path';
3
2
  import { readFileSync, existsSync } from 'node:fs';
4
3
  import { parse as parseYaml } from 'yaml';
@@ -33,7 +32,7 @@ export function compileProject(request = {}) {
33
32
  }
34
33
  }
35
34
  } catch (err) {
36
- Logger?.debug?.('Could not read serverless.config.yml', err);
35
+ console.log('Could not read serverless.config.yml', err);
37
36
  }
38
37
  }
39
38
 
@@ -7,9 +7,15 @@ import {
7
7
  } from '@izara_project/izara-core-library-service-schemas';
8
8
  import { basicValidatorSchema } from '@izara_project/izara-shared-service-schemas';
9
9
  import { s3 } from '@izara_project/izara-core-library-s3';
10
- import Logger from '@izara_project/izara-core-library-logger';
11
10
  import { validateFlowSchema } from './flowValidator.js';
12
11
 
12
+ const consoleLogger = {
13
+ debug: console.log,
14
+ info: console.log,
15
+ warn: console.log,
16
+ error: console.error
17
+ };
18
+
13
19
  const VALID_INITIATE_BY_ENUM = [
14
20
  'user',
15
21
  'systemWithUser',
@@ -66,7 +72,7 @@ export async function loadLocalSchemaDirectory(
66
72
  options = {}
67
73
  ) {
68
74
  const directoryPath = join(project.schemaRoot, directoryName);
69
- const logger = options.logger || Logger;
75
+ const logger = options.logger || consoleLogger;
70
76
  let entries;
71
77
  try {
72
78
  entries = await readdir(directoryPath, { withFileTypes: true });
@@ -229,8 +235,9 @@ export async function resolveExtendedObjectSchemas(
229
235
  let currentSchema = schema;
230
236
  if (currentSchema.hasOwnProperty('extendObjType')) {
231
237
  try {
232
- Logger.debug(
233
- `[DEBUG] [resolveExtendedObjectSchemas] Merging extendObjType for objectType=${currentSchema.objectType}`,
238
+ console.log(
239
+ `[DEBUG] [resolveExtendedObjectSchemas] Merging extendObjType ` +
240
+ `for objectType=${currentSchema.objectType}`,
234
241
  { extendObjType: currentSchema.extendObjType }
235
242
  );
236
243
  const initialResources = currentSchema.storageResources || {};
@@ -238,7 +245,7 @@ export async function resolveExtendedObjectSchemas(
238
245
  const parentServiceTag = currentSchema.extendObjType.serviceTag;
239
246
 
240
247
  const mergedSchema = await getObjectSchema.mergeExtendObjSchema(
241
- { logger: Logger },
248
+ { logger: consoleLogger },
242
249
  currentSchema,
243
250
  bucketName
244
251
  );
@@ -270,8 +277,9 @@ export async function resolveExtendedObjectSchemas(
270
277
 
271
278
  currentSchema = mergedSchema;
272
279
  } catch (error) {
273
- Logger.error(
274
- `[ERROR] [resolveExtendedObjectSchemas] Failed to merge extendObjType for objectType=${currentSchema.objectType}`,
280
+ console.log(
281
+ `[ERROR] [resolveExtendedObjectSchemas] Failed to merge ` +
282
+ `extendObjType for objectType=${currentSchema.objectType}`,
275
283
  { error }
276
284
  );
277
285
  throw error;
@@ -289,8 +297,9 @@ export async function resolveExtendedObjectSchemas(
289
297
  belongToTarget.objectType
290
298
  ) {
291
299
  try {
292
- Logger.debug(
293
- `[DEBUG] [resolveExtendedObjectSchemas] Fetching belongTo schema for objectType=${currentSchema.objectType}`,
300
+ console.log(
301
+ `[DEBUG] [resolveExtendedObjectSchemas] Fetching belongTo ` +
302
+ `schema for objectType=${currentSchema.objectType}`,
294
303
  { belongTo: belongToTarget }
295
304
  );
296
305
  const belongToObjType = {
@@ -298,19 +307,21 @@ export async function resolveExtendedObjectSchemas(
298
307
  objectType: belongToTarget.objectType
299
308
  };
300
309
  const belongToSchema = await getObjectSchema.getObjSchemaS3(
301
- { logger: Logger },
310
+ { logger: consoleLogger },
302
311
  belongToObjType,
303
312
  false,
304
313
  bucketName
305
314
  );
306
315
  currentSchema.belongToSchema = belongToSchema;
307
- Logger.debug(
308
- `[DEBUG] [resolveExtendedObjectSchemas] Successfully fetched belongTo schema for objectType=${currentSchema.objectType}`,
316
+ console.log(
317
+ `[DEBUG] [resolveExtendedObjectSchemas] Successfully fetched ` +
318
+ `belongTo schema for objectType=${currentSchema.objectType}`,
309
319
  { belongToObjectType: belongToSchema?.objectType }
310
320
  );
311
321
  } catch (error) {
312
- Logger.warn(
313
- `[WARN] [resolveExtendedObjectSchemas] Failed to fetch belongTo schema for objectType=${currentSchema.objectType}`,
322
+ console.log(
323
+ `[WARN] [resolveExtendedObjectSchemas] Failed to fetch ` +
324
+ `belongTo schema for objectType=${currentSchema.objectType}`,
314
325
  { error }
315
326
  );
316
327
  }
@@ -361,7 +372,7 @@ export async function resolveGraphServiceTags(
361
372
  let graphServerTagsMap = {};
362
373
  if (bucketName) {
363
374
  try {
364
- const _izContext = { logger: Logger };
375
+ const _izContext = { logger: consoleLogger };
365
376
  const s3Data = await s3.getObjectS3(_izContext, {
366
377
  Bucket: bucketName,
367
378
  Key: 'serviceConfig/GraphServerTags.json'
@@ -370,8 +381,9 @@ export async function resolveGraphServiceTags(
370
381
  graphServerTagsMap = s3Data;
371
382
  }
372
383
  } catch (error) {
373
- Logger.debug?.(
374
- `[DEBUG] [resolveGraphServiceTags] Could not fetch GraphServerTags.json from bucket=${bucketName}`,
384
+ console.log(
385
+ `[DEBUG] [resolveGraphServiceTags] Could not fetch ` +
386
+ `GraphServerTags.json from bucket=${bucketName}`,
375
387
  error
376
388
  );
377
389
  }
@@ -95,12 +95,12 @@ function _extractDynamoTableNames({
95
95
  }
96
96
  }
97
97
  if (rbac) {
98
+ dynamoTableNames.add('Roles');
98
99
  dynamoTableNames.add('TargetRoles');
99
100
  dynamoTableNames.add('RolePermissions');
100
101
  dynamoTableNames.add('RoleUsers');
101
102
  dynamoTableNames.add('UserRoles');
102
103
  dynamoTableNames.add('RbacPermissionCaches');
103
- dynamoTableNames.add('RoleLimit');
104
104
  dynamoTableNames.add('RunningTotalUsage');
105
105
  dynamoTableNames.add('RunningTotalReservedUsage');
106
106
  }
@@ -244,6 +244,7 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
244
244
  const hasQuotaReservation = crudPlans?.Create?.hasQuotaReservation ?? false;
245
245
 
246
246
  const state = {
247
+ serviceTag: project?.serviceTag || '',
247
248
  project,
248
249
  objects,
249
250
  relationships,
@@ -66,12 +66,12 @@ const externalServices = {
66
66
  }
67
67
  });
68
68
  if (typeof rbac !== 'undefined' && rbac) {
69
+ dynamoTableNames.add('Roles');
69
70
  dynamoTableNames.add('TargetRoles');
70
71
  dynamoTableNames.add('RolePermissions');
71
72
  dynamoTableNames.add('RoleUsers');
72
73
  dynamoTableNames.add('UserRoles');
73
74
  dynamoTableNames.add('RbacPermissionCaches');
74
- dynamoTableNames.add('RoleLimit');
75
75
  dynamoTableNames.add('RunningTotalUsage');
76
76
  dynamoTableNames.add('RunningTotalReservedUsage');
77
77
  }
@@ -179,18 +179,9 @@ const tableConfigs = {
179
179
  });
180
180
  });
181
181
 
182
+ const currentServiceTag = (typeof serviceTag !== 'undefined' && serviceTag) ? serviceTag : ((typeof project !== 'undefined' && project?.serviceTag) ? project.serviceTag : '');
182
183
  const rbacSubs = [];
183
184
  if (crud && rbac) {
184
- const allRbacTags = Array.from(new Set(['Rbac', ...(typeof rbacValidatorServiceTags !== 'undefined' && rbacValidatorServiceTags ? rbacValidatorServiceTags : [])]));
185
- const hasGraph = typeof objectGraphServiceTags !== 'undefined' && objectGraphServiceTags && objectGraphServiceTags.length > 0;
186
- const createEndpoint = hasGraph ? 'CreateRbacCallbackHdrSqs' : 'CreateCompleteHdrSqs';
187
- const updateEndpoint = hasGraph ? 'UpdateRbacCallbackHdrSqs' : 'UpdateCompleteHdrSqs';
188
- const deleteEndpoint = hasGraph ? 'DeleteRbacCallbackHdrSqs' : 'DeleteCompleteHdrSqs';
189
- allRbacTags.forEach(function(tag) {
190
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: createEndpoint });
191
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: updateEndpoint });
192
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: deleteEndpoint });
193
- });
194
185
  if (typeof hasQuotaReservation !== 'undefined' && hasQuotaReservation) {
195
186
  rbacSubs.push({ serviceTag: 'Rbac', topicName: 'ConfirmUsage_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
196
187
  rbacSubs.push({ serviceTag: 'Rbac', topicName: 'CancelUsage_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
@@ -198,7 +189,11 @@ const tableConfigs = {
198
189
  }
199
190
 
200
191
  if (typeof customRbacFlowTags !== 'undefined' && customRbacFlowTags && customRbacFlowTags.length > 0) {
201
- const allRbacTags = Array.from(new Set(['Rbac', ...(typeof rbacValidatorServiceTags !== 'undefined' ? rbacValidatorServiceTags : [])]));
192
+ const allRbacTags = Array.from(new Set([
193
+ 'Rbac',
194
+ currentServiceTag,
195
+ ...(typeof rbacValidatorServiceTags !== 'undefined' ? rbacValidatorServiceTags : [])
196
+ ])).filter(Boolean);
202
197
  customRbacFlowTags.forEach(function(flowTag) {
203
198
  const tagUpper = flowTag[0].toUpperCase() + flowTag.slice(1);
204
199
  allRbacTags.forEach(function(tag) {
@@ -309,6 +304,17 @@ function roleConfigs() {
309
304
  <% }); -%>
310
305
  <% } -%>
311
306
  <% if (rbac || (rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0)) { -%>
307
+ {
308
+ Sid: 'FlowObjectsInvokeValidatorRbac',
309
+ Effect: 'Allow',
310
+ Action: ['lambda:InvokeFunction'],
311
+ Resource: [
312
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', 'Rbac')}`<% if (rbac) { %>,
313
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
314
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`<% }); %>
315
+ ]
316
+ },
317
+
312
318
  {
313
319
  Sid: 'FlowObjectsPublishValidatorRbacTopics',
314
320
  Effect: 'Allow',
@@ -316,7 +322,8 @@ function roleConfigs() {
316
322
  Resource: [
317
323
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`,
318
324
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', 'Rbac')}`,
319
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
325
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% if (rbac) { %>,
326
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
320
327
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
321
328
  ]
322
329
  },
@@ -330,12 +337,23 @@ function roleConfigs() {
330
337
  {
331
338
  roleName: 'FlowRbac',
332
339
  statement: [
340
+ {
341
+ Sid: 'FlowRbacInvokeValidatorRbac',
342
+ Effect: 'Allow',
343
+ Action: ['lambda:InvokeFunction'],
344
+ Resource: [
345
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', 'Rbac')}`<% if (rbac) { %>,
346
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
347
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`<% }); %>
348
+ ]
349
+ },
333
350
  {
334
351
  Sid: 'FlowRbacPublishValidatorRbacTopics',
335
352
  Effect: 'Allow',
336
353
  Action: ['sns:Publish'],
337
354
  Resource: [
338
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
355
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`,
356
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
339
357
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
340
358
  ]
341
359
  }
@@ -32,7 +32,7 @@ export function createCrudActionPlan(
32
32
  hasRbac: actionHasRbac,
33
33
  hasQuotaReservation,
34
34
  hasComplete: outputTopic && (actionHasRbac || hasGraph),
35
- hasRbacCallback: outputTopic && actionHasRbac && hasGraph,
35
+ hasRbacCallback: false,
36
36
  hasCreateNodeCallback:
37
37
  outputTopic && action === 'Create' && actionHasRbac && hasGraph && hasQuotaReservation
38
38
  };
@@ -35,11 +35,17 @@ export async function emitCrudApplicationArtifacts({
35
35
  objectType: obj?.objectType ?? '(undefined)',
36
36
  generatedBy: obj?.generatedBy ?? '(missing)'
37
37
  }));
38
- logger?.warn?.(
39
- `[WARN] [CRUD Application] Skipped FlowObjects application artifacts generation: No valid CRUD ObjectSchema found. ` +
38
+ const warnMsg =
39
+ `[WARN] [CRUD Application] Skipped FlowObjects application artifacts ` +
40
+ `generation: No valid CRUD ObjectSchema found. ` +
40
41
  `Checked ${objects.length} object(s): ${JSON.stringify(summary)}. ` +
41
- `Expected "generatedBy" to be one of: ${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`
42
- );
42
+ `Expected "generatedBy" to be one of: ` +
43
+ `${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`;
44
+ if (logger?.warn) {
45
+ logger.warn(warnMsg);
46
+ } else {
47
+ console.log(warnMsg);
48
+ }
43
49
  }
44
50
  return [];
45
51
  }
@@ -29,11 +29,17 @@ export function emitDynamicCrudFlowSchemas({
29
29
  objectType: obj?.objectType ?? '(undefined)',
30
30
  generatedBy: obj?.generatedBy ?? '(missing)'
31
31
  }));
32
- logger?.warn?.(
33
- `[WARN] [CRUD FlowSchemas] Skipped FlowObjects FlowSchemas generation: No valid CRUD ObjectSchema found. ` +
32
+ const warnMsg =
33
+ `[WARN] [CRUD FlowSchemas] Skipped FlowObjects FlowSchemas ` +
34
+ `generation: No valid CRUD ObjectSchema found. ` +
34
35
  `Checked ${objects.length} object(s): ${JSON.stringify(summary)}. ` +
35
- `Expected "generatedBy" to be one of: ${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`
36
- );
36
+ `Expected "generatedBy" to be one of: ` +
37
+ `${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`;
38
+ if (logger?.warn) {
39
+ logger.warn(warnMsg);
40
+ } else {
41
+ console.log(warnMsg);
42
+ }
37
43
  }
38
44
  return [];
39
45
  }
@@ -67,16 +73,17 @@ function _createDynamicFlow(action, plan, serviceTag = '', initiateBy = 'user')
67
73
  }
68
74
  );
69
75
  } else if (plan.hasComplete) {
76
+ const nextStepName = plan.hasCreateNodeCallback ? 'CreateNodeCallback' : 'Complete';
70
77
  startNextSteps.push({
71
78
  targetType: 'ownTopic',
72
- stepName: 'Complete'
79
+ stepName: nextStepName
73
80
  });
74
81
  if (plan.hasGraph) {
75
82
  startNextSteps.push({
76
83
  targetType: 'extTopic',
77
84
  serviceTag: 'GraphHandler',
78
85
  topicName: `${action}Node_In`,
79
- toStepNames: ['Complete']
86
+ toStepNames: [nextStepName]
80
87
  });
81
88
  }
82
89
  }
@@ -137,19 +144,20 @@ function _createDynamicFlow(action, plan, serviceTag = '', initiateBy = 'user')
137
144
  }
138
145
 
139
146
  if (plan.hasCreateNodeCallback) {
147
+ const prevStepName = plan.hasRbacCallback ? 'RbacCallback' : 'Start';
140
148
  flowSteps.push({
141
149
  stepName: 'CreateNodeCallback',
142
150
  event: ['extTopic'],
143
151
  previousSteps: [
144
152
  {
145
153
  sourceType: 'ownTopic',
146
- stepName: 'RbacCallback'
154
+ stepName: prevStepName
147
155
  },
148
156
  {
149
157
  sourceType: 'extTopic',
150
158
  serviceTag: 'GraphHandler',
151
159
  topicName: 'CreateNode_Out',
152
- fromStepNames: ['RbacCallback']
160
+ fromStepNames: [prevStepName]
153
161
  }
154
162
  ],
155
163
  nextSteps: [
@@ -381,11 +381,12 @@ export default async function <%- flowTag %>(
381
381
  },
382
382
  parameterGroups,
383
383
  {
384
+ targetIdentifiers,
384
385
  rbacObjType: {
385
386
  serviceTag: <%- rbacServiceTags.length === 1 ? rbacServiceTags[0] : `{ ${Object.entries(rbacServiceTagMap).map(([k, v]) => `'${k}': ${v}`).join(', ')} }[requestParams.objectType] || process.env.iz_serviceTag` %>
386
387
  },
387
388
  requestParams: requestParams,
388
- nextFlowStepName: '<%- plan?.hasRbacCallback ? `${flowTag}RbacCallback` : `${flowTag}Complete` %>'
389
+ sync: true
389
390
  }
390
391
  );
391
392
 
@@ -394,10 +395,18 @@ export default async function <%- flowTag %>(
394
395
  resultPermissionCheck
395
396
  );
396
397
 
398
+ if (resultPermissionCheck && resultPermissionCheck.permission !== true) {
399
+ errorsFound.push('Permission denied');
400
+ return await <%- flowTag %>LogicalLib(
401
+ _izContext,
402
+ requestParams,
403
+ callingFlowConfig,
404
+ errorsFound
405
+ );
406
+ }
407
+
397
408
  //(<rbacPublish_<%- flowTag %>)
398
409
  //(</rbacPublish_<%- flowTag %>)
399
-
400
- return resultPermissionCheck;
401
410
  }
402
411
  <%_ } _%>
403
412
 
@@ -385,21 +385,51 @@ export async function autoProvisionOwnerTargetRole(_izContext, { node, submitted
385
385
  }
386
386
 
387
387
  const identifiers = node.objInstanceFull.identifiers;
388
- const targetIdentKeys = Object.entries(identifiers)
389
- .filter(([k]) => k !== 'submittedByUserId')
390
- .map(([k, v]) => `${k}#${v}`)
391
- .join('|');
392
-
393
- const targetIdentifiersConcat =
394
- targetIdentKeys ||
395
- (identifiers[Object.keys(identifiers)[0]]
396
- ? `${Object.keys(identifiers)[0]}#${identifiers[Object.keys(identifiers)[0]]}`
397
- : '');
388
+ const sortedKeys = Object.keys(identifiers)
389
+ .filter(
390
+ (k) =>
391
+ k !== 'submittedByUserId' &&
392
+ k !== 'identifierConcat' &&
393
+ k !== 'identifiersConcat'
394
+ )
395
+ .sort();
396
+ const parts = [];
397
+ for (const key of sortedKeys) {
398
+ const value = identifiers[key];
399
+ if (value !== undefined && value !== null) {
400
+ parts.push(`${key}#${value}`);
401
+ }
402
+ }
403
+ const targetIdentifiersConcat = parts.join('|');
398
404
 
399
405
  if (!targetIdentifiersConcat) {
400
406
  return;
401
407
  }
402
408
 
409
+ const targetPlanTier =
410
+ node?.objInstanceFull?.planTier ||
411
+ node?.objInstanceFull?.targetIdentifiers?.planTier ||
412
+ 'tier-free';
413
+ let planTierName = 'Free Plan';
414
+ if (targetPlanTier === 'tier-pro') {
415
+ planTierName = 'Professional Plan';
416
+ } else if (targetPlanTier === 'tier-enterprise') {
417
+ planTierName = 'Enterprise Plan';
418
+ }
419
+
420
+ await dynamodbSharedLib.putItem(
421
+ _izContext,
422
+ dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
423
+ {
424
+ identifiersConcat: targetIdentifiersConcat,
425
+ roleIdKey: targetPlanTier,
426
+ roleType: 'plan_tier',
427
+ roleName: planTierName,
428
+ submittedByUserId,
429
+ createdAt: new Date().toISOString()
430
+ }
431
+ );
432
+
403
433
  const roleIdKey = `${targetIdentifiersConcat}_owner`;
404
434
  _izContext.logger.debug(
405
435
  '[CreateNodeCallback] Auto-provisioning owner TargetRole:',
@@ -417,8 +447,9 @@ export async function autoProvisionOwnerTargetRole(_izContext, { node, submitted
417
447
  {
418
448
  identifiersConcat: targetIdentifiersConcat,
419
449
  roleIdKey,
450
+ roleType: 'tenant_role',
451
+ roleName: 'Owner',
420
452
  submittedByUserId,
421
- roleName: 'owner',
422
453
  assignedAt: new Date().toISOString()
423
454
  }
424
455
  );
@@ -816,16 +847,49 @@ export async function autoProvisionOwnerTargetRoleForDynamoOnly(
816
847
  _izContext.correlationIds?.get(coreConsts.SUBMITTED_BY_USER_ID);
817
848
 
818
849
  if (submittedByUserId) {
819
- const targetIdentKeys = Object.entries(identifiersForCreateData)
820
- .map(([k, v]) => `${k}#${v}`)
821
- .join('|');
822
- const targetIdentifiersConcat =
823
- targetIdentKeys ||
824
- (identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]
825
- ? `${Object.keys(identifiersForCreateData)[0]}#${identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]}`
826
- : '');
850
+ const sortedKeys = Object.keys(identifiersForCreateData)
851
+ .filter(
852
+ (k) =>
853
+ k !== 'submittedByUserId' &&
854
+ k !== 'identifierConcat' &&
855
+ k !== 'identifiersConcat'
856
+ )
857
+ .sort();
858
+ const parts = [];
859
+ for (const key of sortedKeys) {
860
+ const value = identifiersForCreateData[key];
861
+ if (value !== undefined && value !== null) {
862
+ parts.push(`${key}#${value}`);
863
+ }
864
+ }
865
+ const targetIdentifiersConcat = parts.join('|');
827
866
 
828
867
  if (targetIdentifiersConcat) {
868
+ const targetPlanTier =
869
+ actionData?.planTier ||
870
+ requestParams?.planTier ||
871
+ requestParams?.passBackProperties?.planTier ||
872
+ 'tier-free';
873
+ let planTierName = 'Free Plan';
874
+ if (targetPlanTier === 'tier-pro') {
875
+ planTierName = 'Professional Plan';
876
+ } else if (targetPlanTier === 'tier-enterprise') {
877
+ planTierName = 'Enterprise Plan';
878
+ }
879
+
880
+ await dynamodbSharedLib.putItem(
881
+ _izContext,
882
+ dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
883
+ {
884
+ identifiersConcat: targetIdentifiersConcat,
885
+ roleIdKey: targetPlanTier,
886
+ roleType: 'plan_tier',
887
+ roleName: planTierName,
888
+ submittedByUserId,
889
+ createdAt: new Date().toISOString()
890
+ }
891
+ );
892
+
829
893
  const roleIdKey = `${targetIdentifiersConcat}_owner`;
830
894
  _izContext.logger.debug(
831
895
  '[<%- flowTag %>LogicalLib] Auto-provisioning owner TargetRole for DynamoDB-only:',
@@ -843,8 +907,9 @@ export async function autoProvisionOwnerTargetRoleForDynamoOnly(
843
907
  {
844
908
  identifiersConcat: targetIdentifiersConcat,
845
909
  roleIdKey,
910
+ roleType: 'tenant_role',
911
+ roleName: 'Owner',
846
912
  submittedByUserId,
847
- roleName: 'owner',
848
913
  assignedAt: new Date().toISOString()
849
914
  }
850
915
  );
@@ -85,6 +85,7 @@ export default async function <%- flowTag %>(
85
85
  _izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
86
86
 
87
87
  targetIdentifiers = requestParams.targetIdentifiers;
88
+ delete requestParams.targetIdentifiers;
88
89
 
89
90
  if (!targetIdentifiers && objectSchema.belongTo?.objectType === 'user' && submittedByUserId) {
90
91
  targetIdentifiers = { submittedByUserId };
@@ -99,12 +100,11 @@ export default async function <%- flowTag %>(
99
100
  if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
100
101
  targetIdentifiers.submittedByUserId = submittedByUserId;
101
102
  }
102
- requestParams.targetIdentifiers = targetIdentifiers;
103
103
  }
104
104
 
105
105
  _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
106
106
  submittedByUserId: requestParams.submittedByUserId,
107
- targetIdentifiers: requestParams.targetIdentifiers,
107
+ targetIdentifiers,
108
108
  belongTo: objectSchema.belongTo,
109
109
  generatedBy: objectSchema.generatedBy
110
110
  });
@@ -116,6 +116,37 @@ export default async function <%- flowTag %>(
116
116
  // ── Build identifiers used by asynchronous processing ────────────────────
117
117
  const identifiers = requestParams.identifiers;
118
118
 
119
+ if (!objectSchema.hasOwnProperty('belongTo')) {
120
+ if (!targetIdentifiers && identifiers) {
121
+ targetIdentifiers = { ...identifiers };
122
+ } else if (targetIdentifiers && identifiers) {
123
+ for (const [key, value] of Object.entries(identifiers)) {
124
+ if (targetIdentifiers[key] === undefined && value !== undefined) {
125
+ targetIdentifiers[key] = value;
126
+ }
127
+ }
128
+ }
129
+ if (targetIdentifiers) {
130
+ if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
131
+ targetIdentifiers.submittedByUserId = submittedByUserId;
132
+ }
133
+ }
134
+ }
135
+
136
+ if (targetIdentifiers && identifiers) {
137
+ for (const [key, value] of Object.entries(identifiers)) {
138
+ if (
139
+ targetIdentifiers[key] !== undefined &&
140
+ value !== undefined &&
141
+ targetIdentifiers[key] !== value
142
+ ) {
143
+ errorsFound.push(
144
+ `targetIdentifiers.${key} must match identifiers.${key}`
145
+ );
146
+ }
147
+ }
148
+ }
149
+
119
150
  if (errorsFound.length > 0)
120
151
  return await <%- flowTag %>LogicalLib(
121
152
  _izContext,
@@ -131,8 +162,8 @@ export default async function <%- flowTag %>(
131
162
  for (const o of rbacObjects) {
132
163
  const belongToTargets = o?.belongTo
133
164
  ? Array.isArray(o.belongTo)
134
- ? o.belongTo
135
- : [o.belongTo]
165
+ ? o.belongTo
166
+ : [o.belongTo]
136
167
  : [];
137
168
  if (belongToTargets.length > 0 && belongToTargets[0]?.serviceTag) {
138
169
  rbacServiceTagMap[o.objectType] = `'${belongToTargets[0].serviceTag}'`;
@@ -164,11 +195,13 @@ export default async function <%- flowTag %>(
164
195
  },
165
196
  parameterGroups,
166
197
  {
198
+ targetIdentifiers,
167
199
  rbacObjType: {
168
200
  serviceTag: <%- rbacServiceTags.length === 1 ? rbacServiceTags[0] : `{ ${Object.entries(rbacServiceTagMap).map(([k, v]) => `'${k}': ${v}`).join(', ')} }[requestParams.objectType] || process.env.iz_serviceTag` %>
169
201
  },
170
202
  requestParams: requestParams,
171
- nextFlowStepName: '<%- plan?.hasRbacCallback ? `${flowTag}RbacCallback` : `${flowTag}Complete` %>'
203
+ nextFlowStepName: '<%- plan?.hasRbacCallback ? `${flowTag}RbacCallback` : `${flowTag}Complete` %>',
204
+ sync: true
172
205
  }
173
206
  );
174
207
 
@@ -177,10 +210,18 @@ export default async function <%- flowTag %>(
177
210
  resultPermissionCheck
178
211
  );
179
212
 
213
+ if (resultPermissionCheck && resultPermissionCheck.permission !== true) {
214
+ errorsFound.push('Permission denied');
215
+ return await <%- flowTag %>LogicalLib(
216
+ _izContext,
217
+ requestParams,
218
+ callingFlowConfig,
219
+ errorsFound
220
+ );
221
+ }
222
+
180
223
  //(<rbacPublish_<%- flowTag %>)
181
224
  //(</rbacPublish_<%- flowTag %>)
182
-
183
- return resultPermissionCheck;
184
225
  }
185
226
  <%_ } _%>
186
227