@izara_project/izara-core-generate-service-code 1.0.78 → 1.0.80

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 (70) hide show
  1. package/package.json +2 -2
  2. package/src/compile/flowValidator.js +163 -0
  3. package/src/compile/loadSchemas.js +15 -5
  4. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +137 -7
  5. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +137 -17
  6. package/src/emit/crud/crudActionPlan.js +82 -0
  7. package/src/emit/crud/emitCrudApplicationArtifacts.js +169 -112
  8. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +43 -65
  9. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +4 -1
  10. package/src/emit/crud/templates/CreateComplete_Main.ejs +8 -3
  11. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +90 -513
  12. package/src/emit/crud/templates/CreateLogicalLib.ejs +541 -0
  13. package/src/emit/crud/templates/DeleteComplete_Main.ejs +8 -3
  14. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +93 -203
  15. package/src/emit/crud/templates/DeleteLogicalLib.ejs +213 -0
  16. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +217 -0
  17. package/src/emit/crud/templates/GetEndpoint_Main.ejs +1 -1
  18. package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +118 -37
  19. package/src/emit/crud/templates/UpdateComplete_Main.ejs +8 -3
  20. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +92 -444
  21. package/src/emit/crud/templates/UpdateLogicalLib.ejs +477 -0
  22. package/src/emit/custom/customFlows.js +27 -2
  23. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +1 -2
  24. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +10 -3
  25. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  26. package/src/emit/custom/templates/s3/CreateObject_Main.ejs +1 -1
  27. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +2 -2
  28. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +3 -9
  29. package/src/emit/rbac/emitRbacApplicationArtifacts.js +21 -16
  30. package/src/emit/rbac/rbacFlowTag.js +2 -7
  31. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +148 -21
  32. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +4 -2
  33. package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +7 -2
  34. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +6 -2
  35. package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +7 -2
  36. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +6 -2
  37. package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +7 -2
  38. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +6 -2
  39. package/src/emit/relationship/templates/GetRelationship_Main.ejs +1 -1
  40. package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +7 -2
  41. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +6 -2
  42. package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +7 -2
  43. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +6 -2
  44. package/src/emit/resource/emitResourceArtifacts.js +149 -79
  45. package/src/emit/resource/templates/DynamoDBTable_Yaml.ejs +4 -4
  46. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +31 -5
  47. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +21 -0
  48. package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
  49. package/src/emit/shared/templates/FlowHandler_HdrApi.ejs +1 -1
  50. package/src/emit/shared/templates/FlowHandler_HdrDsq.ejs +2 -2
  51. package/src/emit/shared/templates/FlowHandler_HdrInv.ejs +1 -1
  52. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +2 -2
  53. package/src/emit/systemText/emitSystemTextSchemas.js +246 -0
  54. package/src/emit/systemText/templates/SystemTextSchemas.ejs +10 -0
  55. package/src/generate.js +55 -8
  56. package/test/crudApplicationArtifacts.test.js +158 -19
  57. package/test/customFlowApplicationArtifacts.test.js +307 -16
  58. package/test/dynamicCrudFlowSchemas.test.js +150 -14
  59. package/test/dynamicRbacFlowSchemas.test.js +2 -2
  60. package/test/dynamicRelationshipFlowSchemas.test.js +1 -1
  61. package/test/graphDrivenFlowSchema.test.js +257 -0
  62. package/test/publicApi.test.js +14 -5
  63. package/test/resourceArtifacts.test.js +66 -19
  64. package/test/systemTextSchemas.test.js +154 -0
  65. package/src/emit/crud/templates/CreateComplete_HdrSqs.ejs +0 -99
  66. package/src/emit/crud/templates/DeleteComplete_HdrSqs.ejs +0 -99
  67. package/src/emit/crud/templates/FlowEndpointBeforeLogical_Main.ejs +0 -174
  68. package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +0 -187
  69. package/src/emit/crud/templates/UpdateComplete_HdrSqs.ejs +0 -99
  70. package/src/emit/resource/templates/ResourceBeforeLogicalYaml.ejs +0 -47
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
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": {
@@ -13,7 +13,7 @@
13
13
  "exports": "./index.js",
14
14
  "main": "index.js",
15
15
  "scripts": {
16
- "test": "jest",
16
+ "test": "node --test",
17
17
  "format:ejs": "node scripts/format-ejs.js"
18
18
  },
19
19
  "jest": {
@@ -0,0 +1,163 @@
1
+ export function validateFlowSchema(flowSchema) {
2
+ if (!flowSchema || typeof flowSchema !== 'object') {
3
+ throw new TypeError('flowSchema must be an object');
4
+ }
5
+
6
+ const steps = flowSchema.flowSteps;
7
+ if (!Array.isArray(steps) || steps.length === 0) {
8
+ throw new Error(
9
+ 'FlowSchema validation error: flowSteps must be a non-empty array'
10
+ );
11
+ }
12
+
13
+ const entryStep = steps[0];
14
+ if (
15
+ !entryStep ||
16
+ typeof entryStep !== 'object' ||
17
+ entryStep.stepName !== 'Start'
18
+ ) {
19
+ throw new Error(
20
+ "FlowSchema validation error: Entry step flowSteps[0] must have stepName fixed to 'Start'"
21
+ );
22
+ }
23
+
24
+ if (
25
+ entryStep.previousSteps &&
26
+ Array.isArray(entryStep.previousSteps) &&
27
+ entryStep.previousSteps.length > 0
28
+ ) {
29
+ throw new Error(
30
+ "FlowSchema validation error: Entry step flowSteps[0] must not define 'previousSteps'"
31
+ );
32
+ }
33
+
34
+ if (entryStep.event) {
35
+ throw new Error(
36
+ "FlowSchema validation error: Entry step flowSteps[0] must not define step-level 'event'"
37
+ );
38
+ }
39
+
40
+ for (let idx = 0; idx < steps.length; idx++) {
41
+ const step = steps[idx];
42
+ if (!step || typeof step !== 'object') {
43
+ throw new Error(
44
+ `FlowSchema validation error: flowSteps[${idx}] must be an object`
45
+ );
46
+ }
47
+
48
+ if (Array.isArray(step.nextSteps) && step.nextSteps.length > 0) {
49
+ for (let nIdx = 0; nIdx < step.nextSteps.length; nIdx++) {
50
+ const item = step.nextSteps[nIdx];
51
+ if (!item || typeof item !== 'object') {
52
+ throw new Error(
53
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] must be an object`
54
+ );
55
+ }
56
+ if (
57
+ item.targetType !== 'ownTopic' &&
58
+ item.targetType !== 'extTopic' &&
59
+ item.targetType !== 'lambdaSyncInv'
60
+ ) {
61
+ throw new Error(
62
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] must have targetType ('ownTopic' | 'extTopic' | 'lambdaSyncInv')`
63
+ );
64
+ }
65
+ if (item.targetType === 'ownTopic') {
66
+ if (
67
+ typeof item.stepName !== 'string' ||
68
+ item.stepName.trim() === ''
69
+ ) {
70
+ throw new Error(
71
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] ownTopic target requires non-empty 'stepName'`
72
+ );
73
+ }
74
+ } else if (item.targetType === 'extTopic') {
75
+ if (
76
+ typeof item.serviceTag !== 'string' ||
77
+ item.serviceTag.trim() === ''
78
+ ) {
79
+ throw new Error(
80
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] extTopic target requires non-empty 'serviceTag'`
81
+ );
82
+ }
83
+ if (
84
+ typeof item.topicName !== 'string' ||
85
+ item.topicName.trim() === ''
86
+ ) {
87
+ throw new Error(
88
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] extTopic target requires non-empty 'topicName'`
89
+ );
90
+ }
91
+ if (
92
+ !Array.isArray(item.toStepNames) ||
93
+ item.toStepNames.length === 0
94
+ ) {
95
+ throw new Error(
96
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] extTopic target requires non-empty 'toStepNames' array`
97
+ );
98
+ }
99
+ } else if (item.targetType === 'lambdaSyncInv') {
100
+ const fnName = item.functionName || item.topicName || item.topic;
101
+ if (typeof fnName !== 'string' || fnName.trim() === '') {
102
+ throw new Error(
103
+ `FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] lambdaSyncInv target requires non-empty 'functionName'`
104
+ );
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ if (Array.isArray(step.previousSteps) && step.previousSteps.length > 0) {
111
+ for (let pIdx = 0; pIdx < step.previousSteps.length; pIdx++) {
112
+ const item = step.previousSteps[pIdx];
113
+ if (!item || typeof item !== 'object') {
114
+ throw new Error(
115
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] must be an object`
116
+ );
117
+ }
118
+ if (item.sourceType !== 'ownTopic' && item.sourceType !== 'extTopic') {
119
+ throw new Error(
120
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] must have sourceType ('ownTopic' | 'extTopic')`
121
+ );
122
+ }
123
+ if (item.sourceType === 'ownTopic') {
124
+ if (
125
+ typeof item.stepName !== 'string' ||
126
+ item.stepName.trim() === ''
127
+ ) {
128
+ throw new Error(
129
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] ownTopic source requires non-empty 'stepName'`
130
+ );
131
+ }
132
+ } else if (item.sourceType === 'extTopic') {
133
+ if (
134
+ typeof item.serviceTag !== 'string' ||
135
+ item.serviceTag.trim() === ''
136
+ ) {
137
+ throw new Error(
138
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] extTopic source requires non-empty 'serviceTag'`
139
+ );
140
+ }
141
+ if (
142
+ typeof item.topicName !== 'string' ||
143
+ item.topicName.trim() === ''
144
+ ) {
145
+ throw new Error(
146
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] extTopic source requires non-empty 'topicName'`
147
+ );
148
+ }
149
+ if (
150
+ !Array.isArray(item.fromStepNames) ||
151
+ item.fromStepNames.length === 0
152
+ ) {
153
+ throw new Error(
154
+ `FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] extTopic source requires non-empty 'fromStepNames' array`
155
+ );
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ return true;
163
+ }
@@ -7,6 +7,7 @@ import {
7
7
  } from '@izara_project/izara-core-library-service-schemas';
8
8
  import { s3 } from '@izara_project/izara-core-library-s3';
9
9
  import Logger from '@izara_project/izara-core-library-logger';
10
+ import { validateFlowSchema } from './flowValidator.js';
10
11
 
11
12
  export async function loadLocalSchemaDirectory(project, directoryName) {
12
13
  const directoryPath = join(project.schemaRoot, directoryName);
@@ -42,6 +43,16 @@ export async function loadLocalSchemaDirectory(project, directoryName) {
42
43
  loadedSchemas = module.default;
43
44
  }
44
45
 
46
+ if (directoryName === 'FlowSchemas') {
47
+ for (const schema of loadedSchemas) {
48
+ if (Object.prototype.hasOwnProperty.call(schema?.generatedCodeConfig ?? {}, 'codeHookTag')) {
49
+ throw new Error(
50
+ `Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag is no longer supported`
51
+ );
52
+ }
53
+ }
54
+ }
55
+
45
56
  if (process.env.NODE_ENV && process.env.NODE_ENV !== 'test') {
46
57
  for (const schema of loadedSchemas) {
47
58
  let status = true;
@@ -69,6 +80,9 @@ export async function loadLocalSchemaDirectory(project, directoryName) {
69
80
  { logger: Logger },
70
81
  schema
71
82
  );
83
+ if (status) {
84
+ validateFlowSchema(schema);
85
+ }
72
86
  }
73
87
 
74
88
  if (!status) {
@@ -159,10 +173,7 @@ export async function resolveExtendedObjectSchemas(
159
173
  }
160
174
  }
161
175
 
162
- if (
163
- currentSchema.hasOwnProperty('belongTo') &&
164
- currentSchema.belongTo
165
- ) {
176
+ if (currentSchema.hasOwnProperty('belongTo') && currentSchema.belongTo) {
166
177
  const belongToTarget = Array.isArray(currentSchema.belongTo)
167
178
  ? currentSchema.belongTo[0]
168
179
  : currentSchema.belongTo;
@@ -269,4 +280,3 @@ export async function resolveGraphServiceTags(
269
280
 
270
281
  return Array.from(resolvedTags);
271
282
  }
272
-
@@ -9,7 +9,44 @@ const SRC_ROOT = 'app/src/generatedCode/CoreFunctions';
9
9
  const INITIAL_SETUP_ROOT = 'app/initial_setup/generatedCode/source';
10
10
  const INITIAL_SETUP_DATA_ROOT = 'app/initial_setup/generatedCode/data';
11
11
 
12
- function _extractDynamoTableNames(objects = []) {
12
+ function hasSelfRbacAddOn(object) {
13
+ return (
14
+ Array.isArray(object?.addOnDataStructure) &&
15
+ object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac')
16
+ );
17
+ }
18
+
19
+ function hasRbacAddOn(object, objects = []) {
20
+ const selfHasRbac = hasSelfRbacAddOn(object);
21
+ const belongToTargets = object?.belongTo
22
+ ? Array.isArray(object.belongTo)
23
+ ? object.belongTo
24
+ : [object.belongTo]
25
+ : [];
26
+ const belongToHasRbac =
27
+ (Array.isArray(object?.belongToSchema?.addOnDataStructure) &&
28
+ object.belongToSchema.addOnDataStructure.some(
29
+ (addOn) => addOn?.type === 'rbac'
30
+ )) ||
31
+ belongToTargets.some((target) => {
32
+ if (target?.serviceTag === 'UserAccount') return true;
33
+ const matched = objects.find(
34
+ (candidate) =>
35
+ candidate?.serviceTag === target?.serviceTag &&
36
+ candidate?.objectType === target?.objectType
37
+ );
38
+ return hasSelfRbacAddOn(matched);
39
+ });
40
+ return selfHasRbac || belongToHasRbac;
41
+ }
42
+
43
+ function _extractDynamoTableNames({
44
+ objects = [],
45
+ relationships = [],
46
+ flows = [],
47
+ rbac = false,
48
+ s3Flows = []
49
+ } = {}) {
13
50
  const dynamoTableNames = new Set();
14
51
  for (const obj of objects || []) {
15
52
  if (obj && obj.storageResources) {
@@ -26,18 +63,95 @@ function _extractDynamoTableNames(objects = []) {
26
63
  }
27
64
  }
28
65
  }
66
+ for (const rel of relationships || []) {
67
+ if (rel && rel.storageResources) {
68
+ for (const res of Object.values(rel.storageResources)) {
69
+ if (
70
+ res &&
71
+ res.tableName &&
72
+ (res.storageType === 'dynamo' ||
73
+ res.storageType === 'dynamoDB' ||
74
+ !res.storageType)
75
+ ) {
76
+ dynamoTableNames.add(res.tableName);
77
+ }
78
+ }
79
+ }
80
+ }
81
+ for (const flow of flows || []) {
82
+ if (flow && flow.storageResources) {
83
+ for (const res of Object.values(flow.storageResources)) {
84
+ if (
85
+ res &&
86
+ res.tableName &&
87
+ (res.storageType === 'dynamo' ||
88
+ res.storageType === 'dynamoDB' ||
89
+ !res.storageType)
90
+ ) {
91
+ dynamoTableNames.add(res.tableName);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if (rbac) {
97
+ dynamoTableNames.add('TargetRoles');
98
+ dynamoTableNames.add('RolePermissions');
99
+ dynamoTableNames.add('RoleUsers');
100
+ dynamoTableNames.add('UserRoles');
101
+ dynamoTableNames.add('RbacPermissionCaches');
102
+ }
103
+ if ((s3Flows || []).length > 0) {
104
+ dynamoTableNames.add('ReservedDataMain');
105
+ }
29
106
  return dynamoTableNames;
30
107
  }
31
108
 
32
109
  export function coreFunctionManagedPaths(
33
- objects = [],
110
+ firstArg = [],
34
111
  objectGraphServiceTags = [],
35
- relationshipGraphServiceTags = []
112
+ relationshipGraphServiceTags = [],
113
+ relationships = [],
114
+ flows = [],
115
+ rbac = false
36
116
  ) {
37
- const dynamoTableNames = _extractDynamoTableNames(objects);
117
+ let objects = [];
118
+ let objTags = objectGraphServiceTags;
119
+ let relTags = relationshipGraphServiceTags;
120
+ let rels = relationships;
121
+ let fls = flows;
122
+ let isRbac = rbac;
123
+
124
+ if (firstArg && typeof firstArg === 'object' && !Array.isArray(firstArg)) {
125
+ objects = firstArg.objects || [];
126
+ objTags = firstArg.objectGraphServiceTags || [];
127
+ relTags = firstArg.relationshipGraphServiceTags || [];
128
+ rels = firstArg.relationships || [];
129
+ fls = firstArg.flows || [];
130
+ isRbac =
131
+ firstArg.project?.rbac === true ||
132
+ (objects || []).some((o) => o.rbac === true) ||
133
+ (fls || []).some((f) => f.type === 'rbac');
134
+ } else {
135
+ objects = firstArg || [];
136
+ isRbac =
137
+ (objects || []).some((o) => o.rbac === true) ||
138
+ (fls || []).some((f) => f.type === 'rbac');
139
+ }
140
+
141
+ const s3Flows = (fls || []).filter(
142
+ (flow) => Array.isArray(flow?.event) && flow.event.includes('s3')
143
+ );
144
+
145
+ const dynamoTableNames = _extractDynamoTableNames({
146
+ objects,
147
+ relationships: rels,
148
+ flows: fls,
149
+ rbac: isRbac,
150
+ s3Flows
151
+ });
38
152
  const graphTags = new Set([
39
- ...(objectGraphServiceTags || []),
40
- ...(relationshipGraphServiceTags || [])
153
+ ...(objTags || []),
154
+ ...(relTags || [])
41
155
  ]);
42
156
 
43
157
  const paths = [
@@ -91,6 +205,14 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
91
205
  return { roleName };
92
206
  });
93
207
 
208
+ const rbac =
209
+ project?.rbac === true ||
210
+ (objects || []).some((o) => hasRbacAddOn(o, objects)) ||
211
+ (flows || []).some((f) => f.type === 'rbac');
212
+ const rbacValidatorServiceTags = Array.from(new Set(
213
+ (objects || []).filter(o => o.belongTo?.serviceTag && o.belongTo.serviceTag !== 'Rbac').map(o => o.belongTo.serviceTag)
214
+ )).sort();
215
+
94
216
  const state = {
95
217
  project,
96
218
  objects,
@@ -101,6 +223,8 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
101
223
  relationshipGraphServiceTags,
102
224
  crud,
103
225
  hasRelationships,
226
+ rbac,
227
+ rbacValidatorServiceTags,
104
228
  s3Flows,
105
229
  hasGenerateGraph: objects.length > 0 || relationships.length > 0
106
230
  };
@@ -250,7 +374,13 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
250
374
  writeWhenFileExist: false
251
375
  });
252
376
 
253
- const dynamoTableNames = _extractDynamoTableNames(objects);
377
+ const dynamoTableNames = _extractDynamoTableNames({
378
+ objects,
379
+ relationships,
380
+ flows,
381
+ rbac,
382
+ s3Flows
383
+ });
254
384
  for (const tableName of dynamoTableNames) {
255
385
  baseArtifacts.push({
256
386
  path: `${INITIAL_SETUP_DATA_ROOT}/dynamo/test/${tableName}.json`,
@@ -26,12 +26,12 @@ const _izContext = { logger: Logger };
26
26
  // ============================================================
27
27
  // External Service Config
28
28
  // ============================================================
29
- // <#InitialSetupConfig_ExternalServices#>
29
+ //(<InitialSetupConfig_ExternalServices>)
30
30
  const externalServices = {
31
31
  serviceTagGraphHandler: 'GraphHandler',
32
32
  serviceTagRbac: 'Rbac'
33
33
  };
34
- // <#/InitialSetupConfig_ExternalServices#>
34
+ //(</InitialSetupConfig_ExternalServices>)
35
35
 
36
36
  // ============================================================
37
37
  // Table Configs (generated + manual merge)
@@ -47,39 +47,68 @@ const externalServices = {
47
47
  });
48
48
  }
49
49
  });
50
+ (relationships || []).forEach(rel => {
51
+ if (rel && rel.storageResources) {
52
+ Object.values(rel.storageResources).forEach(res => {
53
+ if (res && res.tableName && (res.storageType === 'dynamo' || res.storageType === 'dynamoDB' || !res.storageType)) {
54
+ dynamoTableNames.add(res.tableName);
55
+ }
56
+ });
57
+ }
58
+ });
59
+ (flows || []).forEach(flow => {
60
+ if (flow && flow.storageResources) {
61
+ Object.values(flow.storageResources).forEach(res => {
62
+ if (res && res.tableName && (res.storageType === 'dynamo' || res.storageType === 'dynamoDB' || !res.storageType)) {
63
+ dynamoTableNames.add(res.tableName);
64
+ }
65
+ });
66
+ }
67
+ });
68
+ if (typeof rbac !== 'undefined' && rbac) {
69
+ dynamoTableNames.add('TargetRoles');
70
+ dynamoTableNames.add('RolePermissions');
71
+ dynamoTableNames.add('RoleUsers');
72
+ dynamoTableNames.add('UserRoles');
73
+ }
74
+ if (typeof s3Flows !== 'undefined' && (s3Flows || []).length > 0) {
75
+ dynamoTableNames.add('ReservedDataMain');
76
+ }
50
77
  -%>
51
78
  const tableConfigs = {
52
79
  prod: {
53
80
  Config: {
54
- // <#InitialSetupConfig_Prod_Config_TableConfig#>
55
- // <#/InitialSetupConfig_Prod_Config_TableConfig#>
81
+ //(<InitialSetupConfig_Prod_Config_TableConfig>)
82
+ //(</InitialSetupConfig_Prod_Config_TableConfig>)
56
83
  },
57
84
  <% dynamoTableNames.forEach(tableName => { -%>
58
85
  <%= tableName %>: {
59
- // <#InitialSetupConfig_Prod_<%= tableName %>_TableConfig#>
60
- // <#/InitialSetupConfig_Prod_<%= tableName %>_TableConfig#>
86
+ //(<InitialSetupConfig_Prod_<%= tableName %>_TableConfig>)
87
+ //(</InitialSetupConfig_Prod_<%= tableName %>_TableConfig>)
61
88
  },
62
89
  <% }); -%>
63
- // <#InitialSetupConfig_ExtraProdTableConfigs#>
64
- // <#/InitialSetupConfig_ExtraProdTableConfigs#>
90
+ //(<InitialSetupConfig_ExtraProdTableConfigs>)
91
+ //(</InitialSetupConfig_ExtraProdTableConfigs>)
65
92
  },
66
93
  test: {
67
94
  Config: {
68
- // <#InitialSetupConfig_Test_Config_TableConfig#>
95
+ //(<InitialSetupConfig_Test_Config_TableConfig>)
69
96
  clearDataWhenRefresh: true,
70
97
  rawSource: false
71
- // <#/InitialSetupConfig_Test_Config_TableConfig#>
98
+ //(</InitialSetupConfig_Test_Config_TableConfig>)
72
99
  },
73
100
  <% dynamoTableNames.forEach(tableName => { -%>
74
101
  <%= tableName %>: {
75
- // <#InitialSetupConfig_Test_<%= tableName %>_TableConfig#>
102
+ //(<InitialSetupConfig_Test_<%= tableName %>_TableConfig>)
76
103
  clearDataWhenRefresh: true
77
- // <#/InitialSetupConfig_Test_<%= tableName %>_TableConfig#>
104
+ //(</InitialSetupConfig_Test_<%= tableName %>_TableConfig>)
78
105
  },
79
106
  <% }); -%>
80
- // <#InitialSetupConfig_ExtraTestTableConfigs#>
81
- // <#/InitialSetupConfig_ExtraTestTableConfigs#>
82
- }
107
+ //(<InitialSetupConfig_ExtraTestTableConfigs>)
108
+ //(</InitialSetupConfig_ExtraTestTableConfigs>)
109
+ },
110
+ //(<InitialSetupConfig_ExtraTableConfigs>)
111
+ //(</InitialSetupConfig_ExtraTableConfigs>)
83
112
  };
84
113
 
85
114
  // ============================================================
@@ -100,6 +129,37 @@ const tableConfigs = {
100
129
  nodeSubs.push({ serviceTag: tag, topicName: 'UpdateNode_Out', sqsEndpoint: 'UpdateCompleteHdrSqs' });
101
130
  nodeSubs.push({ serviceTag: tag, topicName: 'CreateNode_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
102
131
  });
132
+
133
+ const customSubs = [];
134
+ (flows || []).forEach(function(flow) {
135
+ if (flow && flow.outputTopic && !flow.generatedCodeConfig && typeof flow.flowTag === 'string' && flow.flowTag) {
136
+ const tag = flow.flowTag[0].toUpperCase() + flow.flowTag.slice(1);
137
+ const serviceTag = flow.objType?.serviceTag || (project && project.serviceTag) || '';
138
+ const topicName = typeof flow.outputTopic === 'string'
139
+ ? (flow.outputTopic.endsWith('_Out') ? flow.outputTopic : `${flow.outputTopic}_Out`)
140
+ : `${tag}_Out`;
141
+ customSubs.push({
142
+ serviceTag: serviceTag,
143
+ topicName: topicName,
144
+ sqsEndpoint: `${tag}CompleteHdrSqs`
145
+ });
146
+ }
147
+ });
148
+
149
+ const rbacSubs = [];
150
+ if (crud && typeof rbacValidatorServiceTags !== 'undefined' && rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0) {
151
+ const allRbacTags = Array.from(new Set(['Rbac', ...rbacValidatorServiceTags]));
152
+ const hasGraph = typeof objectGraphServiceTags !== 'undefined' && objectGraphServiceTags && objectGraphServiceTags.length > 0;
153
+ const updateEndpoint = hasGraph ? 'UpdateRbacCallbackHdrSqs' : 'UpdateCompleteHdrSqs';
154
+ const deleteEndpoint = hasGraph ? 'DeleteRbacCallbackHdrSqs' : 'DeleteCompleteHdrSqs';
155
+ allRbacTags.forEach(function(tag) {
156
+ if (!hasGraph) {
157
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
158
+ }
159
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: updateEndpoint });
160
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: deleteEndpoint });
161
+ });
162
+ }
103
163
  -%>
104
164
  const snsTopicSubscriptions = [
105
165
  <% if (relSubs.length > 0) { -%>
@@ -126,6 +186,32 @@ const snsTopicSubscriptions = [
126
186
  sqsEndpoint: '<%= sub.sqsEndpoint %>'
127
187
  },
128
188
  <% }); -%>
189
+ <% } -%>
190
+ <% if (customSubs.length > 0) { -%>
191
+
192
+ // ------------------------------------------------------------
193
+ // Custom Flow Subscriptions
194
+ // ------------------------------------------------------------
195
+ <% customSubs.forEach(function(sub, idx) { -%>
196
+ {
197
+ serviceTag: '<%= sub.serviceTag %>',
198
+ topicName: '<%= sub.topicName %>',
199
+ sqsEndpoint: '<%= sub.sqsEndpoint %>'
200
+ },
201
+ <% }); -%>
202
+ <% } -%>
203
+ <% if (rbacSubs.length > 0) { -%>
204
+
205
+ // ------------------------------------------------------------
206
+ // RBAC Validator Subscriptions
207
+ // ------------------------------------------------------------
208
+ <% rbacSubs.forEach(function(sub, idx) { -%>
209
+ {
210
+ serviceTag: '<%= sub.serviceTag %>',
211
+ topicName: '<%= sub.topicName %>',
212
+ sqsEndpoint: '<%= sub.sqsEndpoint %>'
213
+ },
214
+ <% }); -%>
129
215
  <% } -%>
130
216
 
131
217
  //(<additionalSubscription>)
@@ -137,10 +223,11 @@ const snsTopicSubscriptions = [
137
223
  // ============================================================
138
224
  function roleConfigs() {
139
225
  const externalLambdaRole = [
140
- <% if (crud && objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
226
+ <% if (crud) { -%>
141
227
  {
142
228
  roleName: 'FlowObjects',
143
229
  statement: [
230
+ <% if (objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
144
231
  <% objectGraphServiceTags.forEach(function(tag, idx) { -%>
145
232
  {
146
233
  Sid: 'FlowObjectsInvokeGetNode<%= tag %>',
@@ -161,14 +248,46 @@ function roleConfigs() {
161
248
  ]
162
249
  },
163
250
  <% }); -%>
251
+ <% } -%>
252
+ <% if (rbac || (rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0)) { -%>
253
+ {
254
+ Sid: 'FlowObjectsPublishValidatorRbacTopics',
255
+ Effect: 'Allow',
256
+ Action: ['sns:Publish'],
257
+ Resource: [
258
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
259
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
260
+ ]
261
+ },
262
+ <% } -%>
164
263
  //(<createFlowObjectsRole>)
165
264
  //(</createFlowObjectsRole>)
166
265
  ]
167
266
  },
168
267
 
268
+ <% if (rbac) { -%>
269
+ {
270
+ roleName: 'FlowRbac',
271
+ statement: [
272
+ {
273
+ Sid: 'FlowRbacPublishValidatorRbacTopics',
274
+ Effect: 'Allow',
275
+ Action: ['sns:Publish'],
276
+ Resource: [
277
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
278
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
279
+ ]
280
+ }
281
+ //(<createFlowRbacRole>)
282
+ //(</createFlowRbacRole>)
283
+ ]
284
+ },
285
+ <% } -%>
286
+
169
287
  {
170
288
  roleName: 'ProcFindData',
171
289
  statement: [
290
+ <% if (objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
172
291
  <% objectGraphServiceTags.forEach(function(tag, idx) { -%>
173
292
  {
174
293
  Sid: 'ProcFindDataInvokeGetNode<%= tag %>',
@@ -179,6 +298,7 @@ function roleConfigs() {
179
298
  ]
180
299
  }
181
300
  <% }); -%>
301
+ <% } -%>
182
302
  //(<createProcFindDataRole>)
183
303
  //(</createProcFindDataRole>)
184
304
  ]
@@ -303,7 +423,7 @@ function roleConfigs() {
303
423
  comma = ',';
304
424
  }
305
425
  -%>
306
- `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, '<%- item.topic %>'<%- tagArg %>)}`<%= comma %>
426
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, '<%- item.functionName || item.topicName || item.topic %>'<%- tagArg %>)}`<%= comma %>
307
427
  <% } -%>
308
428
  ]
309
429
  }