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

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 (61) 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 +132 -17
  6. package/src/emit/crud/crudActionPlan.js +82 -0
  7. package/src/emit/crud/emitCrudApplicationArtifacts.js +162 -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/CreateEndpoint_Main.ejs +84 -512
  11. package/src/emit/crud/templates/CreateLogicalLib.ejs +536 -0
  12. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +87 -202
  13. package/src/emit/crud/templates/DeleteLogicalLib.ejs +213 -0
  14. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +217 -0
  15. package/src/emit/crud/templates/GetEndpoint_Main.ejs +1 -1
  16. package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +9 -2
  17. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +87 -444
  18. package/src/emit/crud/templates/UpdateLogicalLib.ejs +472 -0
  19. package/src/emit/custom/customFlows.js +27 -2
  20. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +1 -2
  21. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +10 -3
  22. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  23. package/src/emit/custom/templates/s3/CreateObject_Main.ejs +1 -1
  24. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +2 -2
  25. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +3 -9
  26. package/src/emit/rbac/emitRbacApplicationArtifacts.js +21 -16
  27. package/src/emit/rbac/rbacFlowTag.js +2 -7
  28. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +148 -21
  29. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +4 -2
  30. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +1 -1
  31. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +1 -1
  32. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +1 -1
  33. package/src/emit/relationship/templates/GetRelationship_Main.ejs +1 -1
  34. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +1 -1
  35. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +1 -1
  36. package/src/emit/resource/emitResourceArtifacts.js +83 -68
  37. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +31 -5
  38. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +21 -0
  39. package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
  40. package/src/emit/shared/templates/FlowHandler_HdrApi.ejs +1 -1
  41. package/src/emit/shared/templates/FlowHandler_HdrDsq.ejs +2 -2
  42. package/src/emit/shared/templates/FlowHandler_HdrInv.ejs +1 -1
  43. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +2 -2
  44. package/src/emit/systemText/emitSystemTextSchemas.js +246 -0
  45. package/src/emit/systemText/templates/SystemTextSchemas.ejs +10 -0
  46. package/src/generate.js +55 -8
  47. package/test/crudApplicationArtifacts.test.js +147 -19
  48. package/test/customFlowApplicationArtifacts.test.js +308 -16
  49. package/test/dynamicCrudFlowSchemas.test.js +150 -14
  50. package/test/dynamicRbacFlowSchemas.test.js +2 -2
  51. package/test/dynamicRelationshipFlowSchemas.test.js +1 -1
  52. package/test/graphDrivenFlowSchema.test.js +257 -0
  53. package/test/publicApi.test.js +14 -5
  54. package/test/resourceArtifacts.test.js +25 -19
  55. package/test/systemTextSchemas.test.js +154 -0
  56. package/src/emit/crud/templates/CreateComplete_HdrSqs.ejs +0 -99
  57. package/src/emit/crud/templates/DeleteComplete_HdrSqs.ejs +0 -99
  58. package/src/emit/crud/templates/FlowEndpointBeforeLogical_Main.ejs +0 -174
  59. package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +0 -187
  60. package/src/emit/crud/templates/UpdateComplete_HdrSqs.ejs +0 -99
  61. 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.79",
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,32 @@ 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
+ allRbacTags.forEach(function(tag) {
153
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
154
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: 'UpdateCompleteHdrSqs' });
155
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: 'DeleteCompleteHdrSqs' });
156
+ });
157
+ }
103
158
  -%>
104
159
  const snsTopicSubscriptions = [
105
160
  <% if (relSubs.length > 0) { -%>
@@ -126,6 +181,32 @@ const snsTopicSubscriptions = [
126
181
  sqsEndpoint: '<%= sub.sqsEndpoint %>'
127
182
  },
128
183
  <% }); -%>
184
+ <% } -%>
185
+ <% if (customSubs.length > 0) { -%>
186
+
187
+ // ------------------------------------------------------------
188
+ // Custom Flow Subscriptions
189
+ // ------------------------------------------------------------
190
+ <% customSubs.forEach(function(sub, idx) { -%>
191
+ {
192
+ serviceTag: '<%= sub.serviceTag %>',
193
+ topicName: '<%= sub.topicName %>',
194
+ sqsEndpoint: '<%= sub.sqsEndpoint %>'
195
+ },
196
+ <% }); -%>
197
+ <% } -%>
198
+ <% if (rbacSubs.length > 0) { -%>
199
+
200
+ // ------------------------------------------------------------
201
+ // RBAC Validator Subscriptions
202
+ // ------------------------------------------------------------
203
+ <% rbacSubs.forEach(function(sub, idx) { -%>
204
+ {
205
+ serviceTag: '<%= sub.serviceTag %>',
206
+ topicName: '<%= sub.topicName %>',
207
+ sqsEndpoint: '<%= sub.sqsEndpoint %>'
208
+ },
209
+ <% }); -%>
129
210
  <% } -%>
130
211
 
131
212
  //(<additionalSubscription>)
@@ -137,10 +218,11 @@ const snsTopicSubscriptions = [
137
218
  // ============================================================
138
219
  function roleConfigs() {
139
220
  const externalLambdaRole = [
140
- <% if (crud && objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
221
+ <% if (crud) { -%>
141
222
  {
142
223
  roleName: 'FlowObjects',
143
224
  statement: [
225
+ <% if (objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
144
226
  <% objectGraphServiceTags.forEach(function(tag, idx) { -%>
145
227
  {
146
228
  Sid: 'FlowObjectsInvokeGetNode<%= tag %>',
@@ -161,14 +243,46 @@ function roleConfigs() {
161
243
  ]
162
244
  },
163
245
  <% }); -%>
246
+ <% } -%>
247
+ <% if (rbac || (rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0)) { -%>
248
+ {
249
+ Sid: 'FlowObjectsPublishValidatorRbacTopics',
250
+ Effect: 'Allow',
251
+ Action: ['sns:Publish'],
252
+ Resource: [
253
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
254
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
255
+ ]
256
+ },
257
+ <% } -%>
164
258
  //(<createFlowObjectsRole>)
165
259
  //(</createFlowObjectsRole>)
166
260
  ]
167
261
  },
168
262
 
263
+ <% if (rbac) { -%>
264
+ {
265
+ roleName: 'FlowRbac',
266
+ statement: [
267
+ {
268
+ Sid: 'FlowRbacPublishValidatorRbacTopics',
269
+ Effect: 'Allow',
270
+ Action: ['sns:Publish'],
271
+ Resource: [
272
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
273
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
274
+ ]
275
+ }
276
+ //(<createFlowRbacRole>)
277
+ //(</createFlowRbacRole>)
278
+ ]
279
+ },
280
+ <% } -%>
281
+
169
282
  {
170
283
  roleName: 'ProcFindData',
171
284
  statement: [
285
+ <% if (objectGraphServiceTags && objectGraphServiceTags.length > 0) { -%>
172
286
  <% objectGraphServiceTags.forEach(function(tag, idx) { -%>
173
287
  {
174
288
  Sid: 'ProcFindDataInvokeGetNode<%= tag %>',
@@ -179,6 +293,7 @@ function roleConfigs() {
179
293
  ]
180
294
  }
181
295
  <% }); -%>
296
+ <% } -%>
182
297
  //(<createProcFindDataRole>)
183
298
  //(</createProcFindDataRole>)
184
299
  ]
@@ -303,7 +418,7 @@ function roleConfigs() {
303
418
  comma = ',';
304
419
  }
305
420
  -%>
306
- `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, '<%- item.topic %>'<%- tagArg %>)}`<%= comma %>
421
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, '<%- item.functionName || item.topicName || item.topic %>'<%- tagArg %>)}`<%= comma %>
307
422
  <% } -%>
308
423
  ]
309
424
  }