@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.83",
3
+ "version": "1.0.84",
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": {
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@izara_project/izara-attribute-tree": "^1.0.15",
24
- "@izara_project/izara-core-library-core": "^1.0.32",
24
+ "@izara_project/izara-core-library-core": "^1.0.35",
25
25
  "@izara_project/izara-property-nodes": "^1.0.16",
26
26
  "@izara_project/izara-shared-core": "^1.0.13",
27
27
  "ejs": "^6.0.1",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@izara_project/izara-core-library-logger": "^1.0.11",
34
- "@izara_project/izara-core-library-service-schemas": "1.0.162",
35
- "@izara_project/izara-shared-service-schemas": "^1.0.71",
34
+ "@izara_project/izara-core-library-service-schemas": "1.0.166",
35
+ "@izara_project/izara-shared-service-schemas": "^1.0.74",
36
36
  "jest": "^30.4.2",
37
37
  "prettier": "^3.9.6",
38
38
  "prettier-plugin-ejs": "^1.0.3"
@@ -6,9 +6,8 @@ export function validateFlowSchema(flowSchema) {
6
6
  if (flowSchema.initiateBy !== undefined) {
7
7
  const validInitiateBy = [
8
8
  'user',
9
- 'systemWithNoUser',
10
9
  'systemWithUser',
11
- 'system'
10
+ 'systemNoUser'
12
11
  ];
13
12
  if (!validInitiateBy.includes(flowSchema.initiateBy)) {
14
13
  throw new Error(
@@ -68,6 +67,18 @@ export function validateFlowSchema(flowSchema) {
68
67
  );
69
68
  }
70
69
 
70
+ if (typeof step.stepName !== 'string' || step.stepName.trim() === '') {
71
+ throw new Error(
72
+ `FlowSchema validation error: flowSteps[${idx}] requires a non-empty 'stepName'`
73
+ );
74
+ }
75
+
76
+ if (step.stepName[0] !== step.stepName[0].toUpperCase()) {
77
+ throw new Error(
78
+ `FlowSchema validation error: flowSteps[${idx}].stepName '${step.stepName}' must start with an uppercase letter`
79
+ );
80
+ }
81
+
71
82
  if (Array.isArray(step.nextSteps) && step.nextSteps.length > 0) {
72
83
  for (let nIdx = 0; nIdx < step.nextSteps.length; nIdx++) {
73
84
  const item = step.nextSteps[nIdx];
@@ -5,12 +5,68 @@ import {
5
5
  getObjectSchema,
6
6
  utils
7
7
  } from '@izara_project/izara-core-library-service-schemas';
8
+ import { basicValidatorSchema } from '@izara_project/izara-shared-service-schemas';
8
9
  import { s3 } from '@izara_project/izara-core-library-s3';
9
10
  import Logger from '@izara_project/izara-core-library-logger';
10
11
  import { validateFlowSchema } from './flowValidator.js';
11
12
 
12
- export async function loadLocalSchemaDirectory(project, directoryName) {
13
+ const VALID_INITIATE_BY_ENUM = [
14
+ 'user',
15
+ 'systemWithUser',
16
+ 'systemNoUser'
17
+ ];
18
+
19
+ if (basicValidatorSchema?.basicFlowSchema?.properties?.initiateBy) {
20
+ basicValidatorSchema.basicFlowSchema.properties.initiateBy.enum =
21
+ VALID_INITIATE_BY_ENUM;
22
+ }
23
+
24
+ const VALID_GENERATED_BY_ENUM = [
25
+ 'user',
26
+ 'systemWithUser',
27
+ 'systemNoUser',
28
+ 'searchResultGenerated'
29
+ ];
30
+
31
+ if (basicValidatorSchema?.basicObjectSchema?.properties?.generatedBy) {
32
+ basicValidatorSchema.basicObjectSchema.properties.generatedBy.enum =
33
+ VALID_GENERATED_BY_ENUM;
34
+ }
35
+
36
+ if (basicValidatorSchema?.basicExtendObjectSchema?.properties?.generatedBy) {
37
+ basicValidatorSchema.basicExtendObjectSchema.properties.generatedBy.enum =
38
+ VALID_GENERATED_BY_ENUM;
39
+ }
40
+
41
+ const objectSchemasToPatch = [
42
+ basicValidatorSchema?.basicObjectSchema,
43
+ basicValidatorSchema?.basicExtendObjectSchema
44
+ ];
45
+
46
+ for (const objSchema of objectSchemasToPatch) {
47
+ const addOnItems = objSchema?.properties?.addOnDataStructure?.items?.anyOf;
48
+ if (Array.isArray(addOnItems)) {
49
+ for (const item of addOnItems) {
50
+ if (item?.properties?.type?.enum?.includes('rbac')) {
51
+ if (item.properties.billalble) {
52
+ delete item.properties.billalble;
53
+ }
54
+ item.properties.billable = { type: 'boolean' };
55
+ if (Array.isArray(item.required)) {
56
+ item.required = item.required.filter((r) => r !== 'billalble');
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ export async function loadLocalSchemaDirectory(
64
+ project,
65
+ directoryName,
66
+ options = {}
67
+ ) {
13
68
  const directoryPath = join(project.schemaRoot, directoryName);
69
+ const logger = options.logger || Logger;
14
70
  let entries;
15
71
  try {
16
72
  entries = await readdir(directoryPath, { withFileTypes: true });
@@ -58,41 +114,85 @@ export async function loadLocalSchemaDirectory(project, directoryName) {
58
114
  }
59
115
  }
60
116
 
61
- if (process.env.NODE_ENV && process.env.NODE_ENV !== 'test') {
117
+ const shouldValidate =
118
+ options.validateSchemas ??
119
+ (!options.skipValidation && !_isTestEnvironment());
120
+ if (shouldValidate) {
62
121
  for (const schema of loadedSchemas) {
63
122
  let status = true;
64
123
  let errors = [];
65
124
 
66
125
  if (directoryName === 'ObjectSchemas') {
67
126
  [status, errors] = utils.validateObjectSchema(
68
- { logger: Logger },
127
+ { logger },
69
128
  schema
70
129
  );
130
+ if (!status && Array.isArray(errors)) {
131
+ errors = errors.filter(
132
+ (err) => !err.includes('billalble')
133
+ );
134
+ if (VALID_GENERATED_BY_ENUM.includes(schema?.generatedBy)) {
135
+ errors = errors.filter(
136
+ (err) =>
137
+ !err.includes('generatedBy') &&
138
+ !err.includes('must be equal to one of the allowed values (enum)') &&
139
+ !(err.includes('should be one of enum') && err.includes(schema?.generatedBy))
140
+ );
141
+ }
142
+ if (errors.length === 0) {
143
+ status = true;
144
+ }
145
+ }
71
146
  } else if (directoryName === 'RelationshipSchemas') {
72
147
  if (entry.name.toLowerCase() === 'refobjectrelationships.js') {
73
148
  [status, errors] = utils.validateRefRelationshipSchema(
74
- { logger: Logger },
149
+ { logger },
75
150
  schema
76
151
  );
77
152
  } else {
78
153
  [status, errors] = utils.validateRelationshipSchema(
79
- { logger: Logger },
154
+ { logger },
80
155
  schema
81
156
  );
82
157
  }
158
+ if (!status && Array.isArray(errors) && VALID_GENERATED_BY_ENUM.includes(schema?.generatedBy)) {
159
+ errors = errors.filter(
160
+ (err) =>
161
+ !err.includes('generatedBy') &&
162
+ !err.includes('must be equal to one of the allowed values (enum)') &&
163
+ !(err.includes('should be one of enum') && err.includes(schema?.generatedBy))
164
+ );
165
+ if (errors.length === 0) {
166
+ status = true;
167
+ }
168
+ }
83
169
  } else if (directoryName === 'FlowSchemas') {
84
170
  [status, errors] = utils.validateBasicFlowSchema(
85
- { logger: Logger },
171
+ { logger },
86
172
  schema
87
173
  );
174
+ if (!status && Array.isArray(errors) && VALID_INITIATE_BY_ENUM.includes(schema?.initiateBy)) {
175
+ errors = errors.filter(
176
+ (err) =>
177
+ !err.includes('initiateBy') &&
178
+ !err.includes('must be equal to one of the allowed values (enum)') &&
179
+ !(err.includes('should be one of enum') && err.includes(schema?.initiateBy))
180
+ );
181
+ if (errors.length === 0) {
182
+ status = true;
183
+ }
184
+ }
88
185
  if (status) {
89
186
  validateFlowSchema(schema);
90
187
  }
91
188
  }
92
189
 
93
190
  if (!status) {
191
+ const formattedErrors = (errors || [])
192
+ .map((err) => ` - ${err}`)
193
+ .join('\n');
94
194
  throw new Error(
95
- `Schema validation failed for ${entry.name}:\n${errors.join('\n')}`
195
+ `[SCHEMA_VALIDATION_ERROR] Schema validation failed for ${directoryName}/${entry.name}:\n${formattedErrors}`
96
196
  );
97
197
  }
98
198
  }
@@ -285,3 +385,12 @@ export async function resolveGraphServiceTags(
285
385
 
286
386
  return Array.from(resolvedTags);
287
387
  }
388
+
389
+ function _isTestEnvironment() {
390
+ return (
391
+ process.env.NODE_ENV === 'test' ||
392
+ process.env.npm_lifecycle_event === 'test' ||
393
+ Boolean(process.execArgv?.includes('--test')) ||
394
+ Boolean(process.argv?.includes('--test'))
395
+ );
396
+ }
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Emits dynamic ObjectSchemas and RelationshipSchemas for attributeTree add-ons
3
+ */
4
+
5
+ function _capitalize(str) {
6
+ if (!str || typeof str !== 'string') {
7
+ return '';
8
+ }
9
+ return str.charAt(0).toUpperCase() + str.slice(1);
10
+ }
11
+
12
+ function _extractAttributeTrees(objects = []) {
13
+ if (!Array.isArray(objects)) {
14
+ return [];
15
+ }
16
+ const results = [];
17
+
18
+ for (const object of objects) {
19
+ if (!Array.isArray(object?.addOnDataStructure)) {
20
+ continue;
21
+ }
22
+ for (const addOn of object.addOnDataStructure) {
23
+ if (addOn?.type === 'attributeTree' && addOn?.attributeTreeTag) {
24
+ results.push({
25
+ parentObject: object,
26
+ attributeTreeTag: addOn.attributeTreeTag
27
+ });
28
+ }
29
+ }
30
+ }
31
+
32
+ return results;
33
+ }
34
+
35
+ function _extractGraphStorageResources(object) {
36
+ const result = {};
37
+ if (object?.storageResources) {
38
+ for (const [tag, res] of Object.entries(object.storageResources)) {
39
+ if (res?.storageType === 'graph') {
40
+ result[tag] = {
41
+ storageType: 'graph',
42
+ graphServerTag: res.graphServerTag || 'GraphHandler'
43
+ };
44
+ }
45
+ }
46
+ }
47
+ if (Object.keys(result).length === 0) {
48
+ result.myGraph = {
49
+ storageType: 'graph',
50
+ graphServerTag: 'GraphHandler'
51
+ };
52
+ }
53
+ return result;
54
+ }
55
+
56
+ function _renderObjectSchema(schema) {
57
+ return `export default [\n ${JSON.stringify(schema, null, 2).replace(/\n/g, '\n ')}\n];\n`;
58
+ }
59
+
60
+ function _renderRelationshipSchema(relationshipTag, content) {
61
+ const wrapper = [{ [relationshipTag]: content }];
62
+ return `export default ${JSON.stringify(wrapper, null, 2)};\n`;
63
+ }
64
+
65
+ export function attributeTreeObjectSchemaManagedPaths(objects = []) {
66
+ const trees = _extractAttributeTrees(objects);
67
+ const paths = [];
68
+
69
+ for (const { attributeTreeTag } of trees) {
70
+ const tag = attributeTreeTag;
71
+ const tagCap = _capitalize(tag);
72
+ paths.push(
73
+ `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Link.js`,
74
+ `app/src/schemas/ObjectSchemas/generatedAttributeTree/attribute${tagCap}Value.js`,
75
+ `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Label.js`,
76
+ `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}ValueStandard.js`
77
+ );
78
+ }
79
+
80
+ return paths;
81
+ }
82
+
83
+ export function attributeTreeRelationshipSchemaManagedPaths(objects = []) {
84
+ const trees = _extractAttributeTrees(objects);
85
+ const paths = [];
86
+
87
+ for (const { attributeTreeTag } of trees) {
88
+ const tag = attributeTreeTag;
89
+ const tagCap = _capitalize(tag);
90
+ paths.push(
91
+ `app/src/schemas/RelationshipSchemas/generatedAttributeTree/has${tagCap}Link.js`,
92
+ `app/src/schemas/RelationshipSchemas/generatedAttributeTree/disabled${tagCap}Link.js`,
93
+ `app/src/schemas/RelationshipSchemas/generatedAttributeTree/is${tagCap}.js`,
94
+ `app/src/schemas/RelationshipSchemas/generatedAttributeTree/is${tagCap}Label.js`
95
+ );
96
+ }
97
+
98
+ return paths;
99
+ }
100
+
101
+ export function emitAttributeTreeObjectSchemas({ objects = [] } = {}) {
102
+ const trees = _extractAttributeTrees(objects);
103
+ if (trees.length === 0) {
104
+ return [];
105
+ }
106
+
107
+ const artifacts = [];
108
+
109
+ for (const { parentObject, attributeTreeTag } of trees) {
110
+ const tag = attributeTreeTag;
111
+ const tagCap = _capitalize(tag);
112
+ const graphStorageResources = _extractGraphStorageResources(parentObject);
113
+ const storageResourceTags = Object.keys(graphStorageResources);
114
+
115
+ // 1. {tag}Link.js
116
+ const linkSchema = {
117
+ objectType: `${tag}Link`,
118
+ canDelete: true,
119
+ generatedBy: 'user',
120
+ storageResources: graphStorageResources,
121
+ fieldNames: {
122
+ [`${tag}LinkId`]: {
123
+ type: 'string',
124
+ randomOnCreate: true,
125
+ canUpdate: false,
126
+ storageResourceTags
127
+ },
128
+ order: {
129
+ type: 'integer',
130
+ optionalOnCreate: true,
131
+ canUpdate: true,
132
+ storageResourceTags
133
+ }
134
+ },
135
+ identifiers: [
136
+ {
137
+ type: 'identifier',
138
+ fieldName: `${tag}LinkId`
139
+ }
140
+ ]
141
+ };
142
+ artifacts.push({
143
+ path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Link.js`,
144
+ content: _renderObjectSchema(linkSchema)
145
+ });
146
+
147
+ // 2. attribute{Tag}Value.js
148
+ const valueSchema = {
149
+ objectType: `attribute${tagCap}Value`,
150
+ canDelete: true,
151
+ generatedBy: 'user',
152
+ storageResources: graphStorageResources,
153
+ fieldNames: {
154
+ [`attribute${tagCap}ValueId`]: {
155
+ type: 'string',
156
+ randomOnCreate: true,
157
+ canUpdate: false,
158
+ storageResourceTags
159
+ },
160
+ value: {
161
+ type: 'string',
162
+ requiredOnCreate: true,
163
+ canUpdate: true,
164
+ storageResourceTags
165
+ }
166
+ },
167
+ identifiers: [
168
+ {
169
+ type: 'identifier',
170
+ fieldName: `attribute${tagCap}ValueId`
171
+ }
172
+ ]
173
+ };
174
+ artifacts.push({
175
+ path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/attribute${tagCap}Value.js`,
176
+ content: _renderObjectSchema(valueSchema)
177
+ });
178
+
179
+ // 3. {tag}Label.js
180
+ const labelSchema = {
181
+ objectType: `${tag}Label`,
182
+ canDelete: true,
183
+ generatedBy: 'user',
184
+ storageResources: graphStorageResources,
185
+ fieldNames: {
186
+ [`${tag}LabelId`]: {
187
+ type: 'string',
188
+ randomOnCreate: true,
189
+ canUpdate: false,
190
+ storageResourceTags
191
+ },
192
+ label: {
193
+ type: 'string',
194
+ requiredOnCreate: true,
195
+ canUpdate: true,
196
+ storageResourceTags
197
+ }
198
+ },
199
+ identifiers: [
200
+ {
201
+ type: 'identifier',
202
+ fieldName: `${tag}LabelId`
203
+ }
204
+ ]
205
+ };
206
+ artifacts.push({
207
+ path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Label.js`,
208
+ content: _renderObjectSchema(labelSchema)
209
+ });
210
+
211
+ // 4. {tag}ValueStandard.js
212
+ const standardValueSchema = {
213
+ objectType: `${tag}ValueStandard`,
214
+ canDelete: true,
215
+ generatedBy: 'user',
216
+ storageResources: graphStorageResources,
217
+ fieldNames: {
218
+ [`${tag}ValueStandardId`]: {
219
+ type: 'string',
220
+ randomOnCreate: true,
221
+ canUpdate: false,
222
+ storageResourceTags
223
+ },
224
+ value: {
225
+ type: 'string',
226
+ requiredOnCreate: true,
227
+ canUpdate: true,
228
+ storageResourceTags
229
+ }
230
+ },
231
+ identifiers: [
232
+ {
233
+ type: 'identifier',
234
+ fieldName: `${tag}ValueStandardId`
235
+ }
236
+ ]
237
+ };
238
+ artifacts.push({
239
+ path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}ValueStandard.js`,
240
+ content: _renderObjectSchema(standardValueSchema)
241
+ });
242
+ }
243
+
244
+ return artifacts;
245
+ }
246
+
247
+ export function emitAttributeTreeRelationshipSchemas({
248
+ objects = [],
249
+ serviceTag = ''
250
+ } = {}) {
251
+ const trees = _extractAttributeTrees(objects);
252
+ if (trees.length === 0) {
253
+ return [];
254
+ }
255
+
256
+ const artifacts = [];
257
+
258
+ for (const { parentObject, attributeTreeTag } of trees) {
259
+ const tag = attributeTreeTag;
260
+ const tagCap = _capitalize(tag);
261
+ const parentObjectType = parentObject?.objectType;
262
+ const effectiveServiceTag =
263
+ parentObject?.serviceTag || serviceTag || 'CurrentService';
264
+ const graphStorageResources = _extractGraphStorageResources(parentObject);
265
+ const storageResourceTags = Object.keys(graphStorageResources);
266
+
267
+ // 1. has{Tag}Link.js
268
+ const hasLinkRel = {
269
+ fieldNames: {},
270
+ storageResources: graphStorageResources,
271
+ links: [
272
+ {
273
+ storageResourceTags,
274
+ from: {
275
+ objType: {
276
+ serviceTag: effectiveServiceTag,
277
+ objectType: parentObjectType
278
+ },
279
+ linkType: 'one'
280
+ },
281
+ to: {
282
+ objType: {
283
+ serviceTag: effectiveServiceTag,
284
+ objectType: `${tag}Link`
285
+ },
286
+ linkType: 'many'
287
+ }
288
+ }
289
+ ]
290
+ };
291
+ artifacts.push({
292
+ path: `app/src/schemas/RelationshipSchemas/generatedAttributeTree/has${tagCap}Link.js`,
293
+ content: _renderRelationshipSchema(`has${tagCap}Link`, hasLinkRel)
294
+ });
295
+
296
+ // 2. disabled{Tag}Link.js
297
+ const disabledLinkRel = {
298
+ fieldNames: {},
299
+ storageResources: graphStorageResources,
300
+ links: [
301
+ {
302
+ storageResourceTags,
303
+ from: {
304
+ objType: {
305
+ serviceTag: effectiveServiceTag,
306
+ objectType: parentObjectType
307
+ },
308
+ linkType: 'one'
309
+ },
310
+ to: {
311
+ objType: {
312
+ serviceTag: effectiveServiceTag,
313
+ objectType: `${tag}Link`
314
+ },
315
+ linkType: 'many'
316
+ }
317
+ }
318
+ ]
319
+ };
320
+ artifacts.push({
321
+ path: `app/src/schemas/RelationshipSchemas/generatedAttributeTree/disabled${tagCap}Link.js`,
322
+ content: _renderRelationshipSchema(
323
+ `disabled${tagCap}Link`,
324
+ disabledLinkRel
325
+ )
326
+ });
327
+
328
+ // 3. is{Tag}.js
329
+ const isRel = {
330
+ fieldNames: {},
331
+ storageResources: graphStorageResources,
332
+ links: [
333
+ {
334
+ storageResourceTags,
335
+ from: {
336
+ objType: {
337
+ serviceTag: effectiveServiceTag,
338
+ objectType: `${tag}Link`
339
+ },
340
+ linkType: 'one'
341
+ },
342
+ to: {
343
+ objType: {
344
+ serviceTag: effectiveServiceTag,
345
+ objectType: `attribute${tagCap}Value`
346
+ },
347
+ linkType: 'many'
348
+ }
349
+ }
350
+ ]
351
+ };
352
+ artifacts.push({
353
+ path: `app/src/schemas/RelationshipSchemas/generatedAttributeTree/is${tagCap}.js`,
354
+ content: _renderRelationshipSchema(`is${tagCap}`, isRel)
355
+ });
356
+
357
+ // 4. is{Tag}Label.js
358
+ const isLabelRel = {
359
+ fieldNames: {},
360
+ storageResources: graphStorageResources,
361
+ links: [
362
+ {
363
+ storageResourceTags,
364
+ from: {
365
+ objType: {
366
+ serviceTag: effectiveServiceTag,
367
+ objectType: `${tag}Link`
368
+ },
369
+ linkType: 'one'
370
+ },
371
+ to: {
372
+ objType: {
373
+ serviceTag: effectiveServiceTag,
374
+ objectType: `${tag}Label`
375
+ },
376
+ linkType: 'many'
377
+ }
378
+ }
379
+ ]
380
+ };
381
+ artifacts.push({
382
+ path: `app/src/schemas/RelationshipSchemas/generatedAttributeTree/is${tagCap}Label.js`,
383
+ content: _renderRelationshipSchema(`is${tagCap}Label`, isLabelRel)
384
+ });
385
+ }
386
+
387
+ return artifacts;
388
+ }
@@ -1,7 +1,7 @@
1
1
  import ejsLib from 'ejs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
 
4
- import { customFlowNextSteps } from '../custom/customFlows.js';
4
+ import { customFlowNextSteps, flowHasRbac, upperFirst } from '../custom/customFlows.js';
5
5
 
6
6
  const TEMPLATE_URL = new URL('./templates/', import.meta.url);
7
7
  const APP_ROOT = 'app/sls_yaml/generatedCode/source';
@@ -99,6 +99,9 @@ function _extractDynamoTableNames({
99
99
  dynamoTableNames.add('RoleUsers');
100
100
  dynamoTableNames.add('UserRoles');
101
101
  dynamoTableNames.add('RbacPermissionCaches');
102
+ dynamoTableNames.add('RoleLimit');
103
+ dynamoTableNames.add('RunningTotalUsage');
104
+ dynamoTableNames.add('RunningTotalReservedUsage');
102
105
  }
103
106
  if ((s3Flows || []).length > 0) {
104
107
  dynamoTableNames.add('ReservedDataMain');
@@ -213,12 +216,37 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
213
216
  (objects || []).filter(o => o.belongTo?.serviceTag && o.belongTo.serviceTag !== 'Rbac').map(o => o.belongTo.serviceTag)
214
217
  )).sort();
215
218
 
219
+ const customRbacFlows = (flows || []).filter((f) => flowHasRbac(f, objects));
220
+ const customRbacFlowTags = customRbacFlows.map((f) => f.flowTag);
221
+ const customRbacFlowsWithTags = customRbacFlows.map((f) => {
222
+ const roleName = upperFirst(f.flowTag);
223
+ const matched = objects.find((o) => o.objectType === f.objType?.objectType);
224
+ const targetServiceTags = [];
225
+ const belongTo = matched?.belongTo;
226
+ if (belongTo) {
227
+ if (Array.isArray(belongTo)) {
228
+ for (const b of belongTo) {
229
+ if (b?.serviceTag) targetServiceTags.push(b.serviceTag);
230
+ }
231
+ } else if (belongTo.serviceTag) {
232
+ targetServiceTags.push(belongTo.serviceTag);
233
+ }
234
+ }
235
+ return {
236
+ roleName,
237
+ flowTag: f.flowTag,
238
+ targetServiceTags: Array.from(new Set(targetServiceTags))
239
+ };
240
+ });
241
+
216
242
  const state = {
217
243
  project,
218
244
  objects,
219
245
  relationships,
220
246
  flows,
221
247
  customFlowNextSteps: customFlowNextSteps(flows),
248
+ customRbacFlowTags,
249
+ customRbacFlowsWithTags,
222
250
  objectGraphServiceTags,
223
251
  relationshipGraphServiceTags,
224
252
  crud,