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

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 (63) 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 +34 -1
  6. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +29 -7
  7. package/src/emit/crud/crudActionPlan.js +41 -6
  8. package/src/emit/crud/emitCrudApplicationArtifacts.js +56 -8
  9. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +153 -27
  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/CreateConfirmComplete_Main.ejs +54 -0
  15. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +252 -154
  16. package/src/emit/crud/templates/CreateLogicalLib.ejs +1163 -236
  17. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +221 -0
  18. package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
  19. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +33 -8
  20. package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
  21. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +20 -120
  22. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +79 -10
  23. package/src/emit/crud/templates/FlowMain_Wbs.ejs +31 -35
  24. package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
  25. package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
  26. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +52 -3
  27. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  28. package/src/emit/custom/customFlows.js +47 -0
  29. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
  30. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
  31. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
  32. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
  33. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  34. package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
  35. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
  36. package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
  37. package/src/emit/rbac/templates/CancelUsage_Main.ejs +163 -0
  38. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +187 -0
  39. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +68 -6
  40. package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
  41. package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
  42. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +166 -0
  43. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +84 -23
  44. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +11 -8
  45. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  46. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +4 -4
  47. package/src/emit/resource/emitResourceArtifacts.js +351 -108
  48. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +3 -1
  49. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +55 -2
  50. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +1 -1
  51. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -5
  52. package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
  53. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +6 -0
  54. package/src/emit/systemText/emitSystemTextSchemas.js +1 -1
  55. package/src/generate.js +41 -10
  56. package/test/attributeTreeSchemas.test.js +219 -0
  57. package/test/createTargetRoleHandler.test.js +59 -4
  58. package/test/crudApplicationArtifacts.test.js +20 -3
  59. package/test/customFlowApplicationArtifacts.test.js +5 -5
  60. package/test/dynamicCrudFlowSchemas.test.js +107 -6
  61. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  62. package/test/publicApi.test.js +6 -6
  63. package/test/resourceArtifacts.test.js +89 -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.85",
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,8 @@
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
+ import { createCrudActionPlans } from '../crud/crudActionPlan.js';
5
6
 
6
7
  const TEMPLATE_URL = new URL('./templates/', import.meta.url);
7
8
  const APP_ROOT = 'app/sls_yaml/generatedCode/source';
@@ -99,6 +100,9 @@ function _extractDynamoTableNames({
99
100
  dynamoTableNames.add('RoleUsers');
100
101
  dynamoTableNames.add('UserRoles');
101
102
  dynamoTableNames.add('RbacPermissionCaches');
103
+ dynamoTableNames.add('RoleLimit');
104
+ dynamoTableNames.add('RunningTotalUsage');
105
+ dynamoTableNames.add('RunningTotalReservedUsage');
102
106
  }
103
107
  if ((s3Flows || []).length > 0) {
104
108
  dynamoTableNames.add('ReservedDataMain');
@@ -213,17 +217,46 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
213
217
  (objects || []).filter(o => o.belongTo?.serviceTag && o.belongTo.serviceTag !== 'Rbac').map(o => o.belongTo.serviceTag)
214
218
  )).sort();
215
219
 
220
+ const customRbacFlows = (flows || []).filter((f) => flowHasRbac(f, objects));
221
+ const customRbacFlowTags = customRbacFlows.map((f) => f.flowTag);
222
+ const customRbacFlowsWithTags = customRbacFlows.map((f) => {
223
+ const roleName = upperFirst(f.flowTag);
224
+ const matched = objects.find((o) => o.objectType === f.objType?.objectType);
225
+ const targetServiceTags = [];
226
+ const belongTo = matched?.belongTo;
227
+ if (belongTo) {
228
+ if (Array.isArray(belongTo)) {
229
+ for (const b of belongTo) {
230
+ if (b?.serviceTag) targetServiceTags.push(b.serviceTag);
231
+ }
232
+ } else if (belongTo.serviceTag) {
233
+ targetServiceTags.push(belongTo.serviceTag);
234
+ }
235
+ }
236
+ return {
237
+ roleName,
238
+ flowTag: f.flowTag,
239
+ targetServiceTags: Array.from(new Set(targetServiceTags))
240
+ };
241
+ });
242
+
243
+ const crudPlans = createCrudActionPlans(objects);
244
+ const hasQuotaReservation = crudPlans?.Create?.hasQuotaReservation ?? false;
245
+
216
246
  const state = {
217
247
  project,
218
248
  objects,
219
249
  relationships,
220
250
  flows,
221
251
  customFlowNextSteps: customFlowNextSteps(flows),
252
+ customRbacFlowTags,
253
+ customRbacFlowsWithTags,
222
254
  objectGraphServiceTags,
223
255
  relationshipGraphServiceTags,
224
256
  crud,
225
257
  hasRelationships,
226
258
  rbac,
259
+ hasQuotaReservation,
227
260
  rbacValidatorServiceTags,
228
261
  s3Flows,
229
262
  hasGenerateGraph: objects.length > 0 || relationships.length > 0