@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
@@ -0,0 +1,82 @@
1
+ export const CRUD_ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
2
+
3
+ export function createCrudActionPlans(objects = []) {
4
+ const hasGraph = hasGraphStorageResource(objects);
5
+ const hasRbac = objects.some(
6
+ (object) => isCrudObject(object) && hasRbacObject(object, objects)
7
+ );
8
+
9
+ return Object.fromEntries(
10
+ CRUD_ACTIONS.map((action) => [
11
+ action,
12
+ createCrudActionPlan(action, { hasGraph, hasRbac })
13
+ ])
14
+ );
15
+ }
16
+
17
+ export function createCrudActionPlan(
18
+ action,
19
+ { hasGraph = false, hasRbac = false } = {}
20
+ ) {
21
+ const actionHasRbac = action !== 'Create' && action !== 'Get' && hasRbac;
22
+ const outputTopic = action !== 'Get';
23
+
24
+ return {
25
+ action,
26
+ outputTopic,
27
+ hasGraph: outputTopic && hasGraph,
28
+ hasRbac: actionHasRbac,
29
+ hasComplete: outputTopic && (actionHasRbac || hasGraph),
30
+ hasRbacCallback: outputTopic && actionHasRbac && hasGraph
31
+ };
32
+ }
33
+
34
+ export function isCrudObject(object) {
35
+ return (
36
+ object?.generatedBy === 'userGenerated' ||
37
+ object?.generatedBy === 'systemGenerated'
38
+ );
39
+ }
40
+
41
+ export function hasGraphStorageResource(objects = []) {
42
+ return objects.some((object) =>
43
+ Object.values(object?.storageResources ?? {}).some(
44
+ (resource) =>
45
+ resource?.storageType === 'graph' ||
46
+ resource?.resourceType === 'graph' ||
47
+ resource?.dbType === 'neo4j' ||
48
+ resource?.storageTag?.toLowerCase().includes('graph')
49
+ )
50
+ );
51
+ }
52
+
53
+ function hasRbacObject(object, objects = []) {
54
+ return (
55
+ hasSelfRbac(object) ||
56
+ Array.isArray(object?.belongToSchema?.addOnDataStructure) &&
57
+ object.belongToSchema.addOnDataStructure.some(
58
+ (addOn) => addOn?.type === 'rbac'
59
+ ) ||
60
+ normalizeBelongTo(object).some((target) => {
61
+ if (target?.serviceTag === 'UserAccount') return true;
62
+ const matched = objects.find(
63
+ (candidate) =>
64
+ candidate?.serviceTag === target?.serviceTag &&
65
+ candidate?.objectType === target?.objectType
66
+ );
67
+ return hasSelfRbac(matched);
68
+ })
69
+ );
70
+ }
71
+
72
+ function hasSelfRbac(object) {
73
+ return (
74
+ Array.isArray(object?.addOnDataStructure) &&
75
+ object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac')
76
+ );
77
+ }
78
+
79
+ function normalizeBelongTo(object) {
80
+ if (!object?.belongTo) return [];
81
+ return Array.isArray(object.belongTo) ? object.belongTo : [object.belongTo];
82
+ }
@@ -8,7 +8,13 @@ import {
8
8
  renderWbsFunctionYaml
9
9
  } from '../function/functionYamlArtifact.js';
10
10
 
11
- const ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
11
+ import {
12
+ CRUD_ACTIONS,
13
+ createCrudActionPlans,
14
+ isCrudObject
15
+ } from './crudActionPlan.js';
16
+
17
+ const ACTIONS = CRUD_ACTIONS;
12
18
  const YAML_PATH = 'app/sls_yaml/generatedCode/source/flow-objects.yml';
13
19
  const TEMPLATE_URL = new URL('./templates/', import.meta.url);
14
20
  const SHARED_TEMPLATE_URL = new URL('../shared/templates/', import.meta.url);
@@ -24,7 +30,7 @@ export async function emitCrudApplicationArtifacts({
24
30
  if (!objects.some(isCrudObject)) return [];
25
31
 
26
32
  const handlers = await Promise.all(
27
- ACTIONS.map((action) => emitAction(action, objects, flows, serviceTag))
33
+ ACTIONS.map((action) => emitAction(action, objects, serviceTag))
28
34
  );
29
35
  return [
30
36
  ...handlers.flatMap((handler) => handler.artifacts),
@@ -43,6 +49,9 @@ export function crudApplicationManagedPaths() {
43
49
  ...ACTIONS.flatMap((action) => {
44
50
  const source = `app/src/generatedCode/FlowObjects/${action}/source`;
45
51
  const paths = [`${source}/${action}_Main.js`];
52
+ if (action !== 'Get') {
53
+ paths.unshift(`${source}/${action}LogicalLib.js`);
54
+ }
46
55
  if (action === 'Get') {
47
56
  paths.push(`${source}/Get_HdrInv.js`, `${source}/Get_HdrApi.js`);
48
57
  paths.push(
@@ -54,10 +63,8 @@ export function crudApplicationManagedPaths() {
54
63
  `${source}/${action}_HdrSqs.js`,
55
64
  `${source}/${action}_WbsMain.js`,
56
65
  `${source}/${action}_HdrWbs.js`,
57
- `${source}/${action}BeforeLogical_Main.js`,
58
- `${source}/${action}BeforeLogical_HdrSqs.js`
59
- );
60
- paths.push(
66
+ `${source}/${action}RbacCallback_Main.js`,
67
+ `${source}/${action}RbacCallback_HdrSqs.js`,
61
68
  `${source}/${action}Complete_Main.js`,
62
69
  `${source}/${action}Complete_HdrSqs.js`
63
70
  );
@@ -68,25 +75,22 @@ export function crudApplicationManagedPaths() {
68
75
  ];
69
76
  }
70
77
 
71
- async function emitAction(action, objects, flows, serviceTag) {
78
+ async function emitAction(action, objects, serviceTag) {
72
79
  const source = `app/src/generatedCode/FlowObjects/${action}/source`;
73
80
  const handlerRoot = `src/generatedCode/FlowObjects/${action}/source`;
74
- const generateCode = flows
75
- .filter(
76
- (flow) =>
77
- flow?.generatedCodeConfig?.generatedCodeTag?.toLowerCase() ===
78
- action.toLowerCase()
79
- )
80
- .map((flow) => ({ ...flow.generatedCodeConfig, flowTag: flow.flowTag }));
81
+ const plan = createCrudActionPlans(objects)[action];
81
82
  const mainFileName = `${action}_Main`;
82
83
  const shared = {
83
84
  flowTag: action,
84
85
  upperCaseFlowTag: action,
85
86
  topicArn: `${action}_In`,
86
- generateCode,
87
87
  flow: null,
88
88
  flowStepName: '',
89
- startConfig: null
89
+ startConfig: null,
90
+ objects: action === 'Create' ? [] : objects,
91
+ isCrudObject,
92
+ hasRbacAddOn: (object) => hasRbacAddOn(object, objects),
93
+ plan
90
94
  };
91
95
  const artifacts = [
92
96
  {
@@ -94,6 +98,12 @@ async function emitAction(action, objects, flows, serviceTag) {
94
98
  content: await render(`${action}Endpoint_Main.ejs`, shared)
95
99
  }
96
100
  ];
101
+ if (action !== 'Get') {
102
+ artifacts.unshift({
103
+ path: `${source}/${action}LogicalLib.js`,
104
+ content: await render(`${action}LogicalLib.ejs`, shared)
105
+ });
106
+ }
97
107
 
98
108
  let targetServiceTag = '${self:custom.iz_serviceName}';
99
109
  if (serviceTag) {
@@ -118,69 +128,75 @@ async function emitAction(action, objects, flows, serviceTag) {
118
128
  functionName: 'Get',
119
129
  queueName: 'GetHdrApi'
120
130
  })
121
- },
131
+ }
132
+ );
133
+ const functions = [
122
134
  {
123
- path: `${source}/GetRange_Main.js`,
124
- content: await render('GetRangeEndpoint_Main.ejs', {
125
- ...shared,
126
- mainFileName: 'GetRange_Main',
127
- functionName: 'GetRange'
128
- })
135
+ type: 'inv',
136
+ functionName: 'GetHdrInv',
137
+ handlerPath: `${handlerRoot}/Get_HdrInv.main`,
138
+ roleName: 'FlowObjectsRole',
139
+ flowTag: action,
140
+ flowStepName: ''
129
141
  },
130
142
  {
131
- path: `${source}/GetRange_HdrApi.js`,
132
- content: await renderShared('FlowHandler_HdrApi.ejs', {
133
- ...shared,
134
- mainFileName: 'GetRange_Main',
135
- functionName: 'GetRange',
136
- queueName: 'GetRangeHdrApi'
137
- })
143
+ type: 'api',
144
+ functionName: 'GetHdrApi',
145
+ handlerPath: `${handlerRoot}/Get_HdrApi.main`,
146
+ roleName: 'FlowObjectsRole',
147
+ flowTag: action,
148
+ flowStepName: '',
149
+ routes: objects.filter(isCrudObject).map((object) => ({
150
+ path: `/${targetServiceTag}/${object.objectType}/get`,
151
+ upperName: upperFirst(object.objectType)
152
+ }))
138
153
  }
139
- );
140
- return {
141
- artifacts,
142
- functions: [
143
- {
144
- type: 'inv',
145
- functionName: 'GetHdrInv',
146
- handlerPath: `${handlerRoot}/Get_HdrInv.main`,
147
- roleName: 'FlowObjectsRole',
148
- flowTag: action,
149
- flowStepName: ''
150
- },
154
+ ];
155
+
156
+ const getRangeRoutes = objects
157
+ .filter(isCrudObject)
158
+ .filter(
159
+ (object) =>
160
+ object.identifiers &&
161
+ object.identifiers.some((i) => i.type === 'sortKey')
162
+ )
163
+ .map((object) => ({
164
+ path: `/${targetServiceTag}/${object.objectType}/getRange`,
165
+ upperName: upperFirst(object.objectType)
166
+ }));
167
+
168
+ if (getRangeRoutes.length > 0) {
169
+ artifacts.push(
151
170
  {
152
- type: 'api',
153
- functionName: 'GetHdrApi',
154
- handlerPath: `${handlerRoot}/Get_HdrApi.main`,
155
- roleName: 'FlowObjectsRole',
156
- flowTag: action,
157
- flowStepName: '',
158
- routes: objects.filter(isCrudObject).map((object) => ({
159
- path: `/${targetServiceTag}/${object.objectType}/get`,
160
- upperName: upperFirst(object.objectType)
161
- }))
171
+ path: `${source}/GetRange_Main.js`,
172
+ content: await render('GetRangeEndpoint_Main.ejs', {
173
+ ...shared,
174
+ mainFileName: 'GetRange_Main',
175
+ functionName: 'GetRange'
176
+ })
162
177
  },
163
178
  {
164
- type: 'api',
165
- functionName: 'GetRangeHdrApi',
166
- handlerPath: `${handlerRoot}/GetRange_HdrApi.main`,
167
- roleName: 'FlowObjectsRole',
168
- flowTag: 'GetRange',
169
- flowStepName: '',
170
- routes: objects
171
- .filter(isCrudObject)
172
- .filter(
173
- (object) =>
174
- object.identifiers &&
175
- object.identifiers.some((i) => i.type === 'sortKey')
176
- )
177
- .map((object) => ({
178
- path: `/${targetServiceTag}/${object.objectType}/getRange`,
179
- upperName: upperFirst(object.objectType)
180
- }))
179
+ path: `${source}/GetRange_HdrApi.js`,
180
+ content: await renderShared('FlowHandler_HdrApi.ejs', {
181
+ ...shared,
182
+ mainFileName: 'GetRange_Main',
183
+ functionName: 'GetRange',
184
+ queueName: 'GetRangeHdrApi'
185
+ })
181
186
  }
182
- ]
183
- };
187
+ );
188
+ functions.push({
189
+ type: 'api',
190
+ functionName: 'GetRangeHdrApi',
191
+ handlerPath: `${handlerRoot}/GetRange_HdrApi.main`,
192
+ roleName: 'FlowObjectsRole',
193
+ flowTag: 'GetRange',
194
+ flowStepName: '',
195
+ routes: getRangeRoutes
196
+ });
197
+ }
198
+
199
+ return { artifacts, functions };
184
200
  }
185
201
 
186
202
  artifacts.push(
@@ -230,68 +246,106 @@ async function emitAction(action, objects, flows, serviceTag) {
230
246
  flowStepName: ''
231
247
  }
232
248
  ];
233
- const complete = `${action}Complete`;
234
- artifacts.push(
235
- {
236
- path: `${source}/${complete}_Main.js`,
237
- content: await render(`${action}Complete_Main.ejs`, { action })
238
- },
239
- {
240
- path: `${source}/${complete}_HdrSqs.js`,
241
- content: await render(`${action}Complete_HdrSqs.ejs`, {
242
- ...shared,
243
- mainFileName: `${complete}_Main`,
244
- functionName: complete,
245
- queueName: `${action}CompleteHdrSqs`
246
- })
247
- }
248
- );
249
- functions.push({
250
- type: 'sqs',
251
- functionName: `${action}CompleteHdrSqs`,
252
- handlerPath: `${handlerRoot}/${complete}_HdrSqs.main`,
253
- roleName: 'FlowObjectsRole',
254
- queueName: `${action}CompleteHdrSqs`,
255
- flowTag: action,
256
- flowStepName: 'Complete'
257
- });
258
- if (generateCode.some((config) => config.codeHookTag === 'beforeLogical')) {
259
- const beforeMain = `${action}BeforeLogical_Main`;
249
+
250
+ if (plan.hasRbacCallback) {
251
+ const rbacCallbackMain = `${action}RbacCallback_Main`;
260
252
  artifacts.push(
261
253
  {
262
- path: `${source}/${beforeMain}.js`,
263
- content: await render('FlowEndpointBeforeLogical_Main.ejs', shared)
254
+ path: `${source}/${rbacCallbackMain}.js`,
255
+ content: await render('FlowEndpointRbacCallback_Main.ejs', shared)
264
256
  },
265
257
  {
266
- path: `${source}/${action}BeforeLogical_HdrSqs.js`,
258
+ path: `${source}/${rbacCallbackMain.replace('_Main', '_HdrSqs')}.js`,
267
259
  content: await renderShared('FlowHandler_HdrSqs.ejs', {
268
260
  ...shared,
269
- mainFileName: beforeMain,
270
- functionName: `${action}BeforeLogicalMain`,
271
- queueName: `${action}BeforeLogicalHdrSqs`
261
+ mainFileName: rbacCallbackMain,
262
+ functionName: `${action}RbacCallbackMain`,
263
+ queueName: `${action}RbacCallbackHdrSqs`
272
264
  })
273
265
  }
274
266
  );
275
267
  functions.push({
276
268
  type: 'sqs',
277
- functionName: `${action}BeforeLogicalHdrSqs`,
278
- handlerPath: `${handlerRoot}/${action}BeforeLogical_HdrSqs.main`,
269
+ functionName: `${action}RbacCallbackHdrSqs`,
270
+ handlerPath: `${handlerRoot}/${action}RbacCallback_HdrSqs.main`,
279
271
  roleName: 'FlowObjectsRole',
280
- queueName: `${action}BeforeLogicalHdrSqs`,
272
+ queueName: `${action}RbacCallbackHdrSqs`,
281
273
  flowTag: action,
282
- flowStepName: 'BeforeLogical'
274
+ flowStepName: 'RbacCallback'
283
275
  });
284
276
  }
277
+
278
+ if (plan.hasComplete) {
279
+ const completeMain = `${action}Complete_Main`;
280
+ const templateName =
281
+ plan.hasRbac && !plan.hasGraph
282
+ ? 'FlowEndpointRbacCallback_Main.ejs'
283
+ : `${action}Complete_Main.ejs`;
284
+ const templateContext =
285
+ plan.hasRbac && !plan.hasGraph
286
+ ? shared
287
+ : { action, ...shared };
288
+
289
+ artifacts.push(
290
+ {
291
+ path: `${source}/${completeMain}.js`,
292
+ content: await render(templateName, templateContext)
293
+ },
294
+ {
295
+ path: `${source}/${action}Complete_HdrSqs.js`,
296
+ content: await renderShared('FlowHandler_HdrSqs.ejs', {
297
+ ...shared,
298
+ mainFileName: completeMain,
299
+ functionName: `${action}CompleteMain`,
300
+ queueName: `${action}CompleteHdrSqs`
301
+ })
302
+ }
303
+ );
304
+ functions.push({
305
+ type: 'sqs',
306
+ functionName: `${action}CompleteHdrSqs`,
307
+ handlerPath: `${handlerRoot}/${action}Complete_HdrSqs.main`,
308
+ roleName: 'FlowObjectsRole',
309
+ queueName: `${action}CompleteHdrSqs`,
310
+ flowTag: action,
311
+ flowStepName: 'Complete'
312
+ });
313
+ }
314
+
285
315
  return { artifacts, functions };
286
316
  }
287
317
 
288
- function isCrudObject(object) {
318
+ function hasSelfRbacAddOn(object) {
289
319
  return (
290
- object?.generatedBy === 'userGenerated' ||
291
- object?.generatedBy === 'systemGenerated'
320
+ Array.isArray(object?.addOnDataStructure) &&
321
+ object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac')
292
322
  );
293
323
  }
294
324
 
325
+ function hasRbacAddOn(object, objects = []) {
326
+ const selfHasRbac = hasSelfRbacAddOn(object);
327
+ const belongToTargets = object?.belongTo
328
+ ? Array.isArray(object.belongTo)
329
+ ? object.belongTo
330
+ : [object.belongTo]
331
+ : [];
332
+ const belongToHasRbac =
333
+ (Array.isArray(object?.belongToSchema?.addOnDataStructure) &&
334
+ object.belongToSchema.addOnDataStructure.some(
335
+ (addOn) => addOn?.type === 'rbac'
336
+ )) ||
337
+ belongToTargets.some((target) => {
338
+ if (target?.serviceTag === 'UserAccount') return true;
339
+ const matched = objects.find(
340
+ (candidate) =>
341
+ candidate?.serviceTag === target?.serviceTag &&
342
+ candidate?.objectType === target?.objectType
343
+ );
344
+ return hasSelfRbacAddOn(matched);
345
+ });
346
+ return selfHasRbac || belongToHasRbac;
347
+ }
348
+
295
349
  function upperFirst(value) {
296
350
  return value.charAt(0).toUpperCase() + value.slice(1);
297
351
  }
@@ -302,7 +356,10 @@ async function render(templateName, data) {
302
356
  }
303
357
 
304
358
  async function renderShared(templateName, data) {
305
- const template = await readFile(new URL(templateName, SHARED_TEMPLATE_URL), 'utf8');
359
+ const template = await readFile(
360
+ new URL(templateName, SHARED_TEMPLATE_URL),
361
+ 'utf8'
362
+ );
306
363
  return ejsLib.render(template, data);
307
364
  }
308
365
 
@@ -1,64 +1,52 @@
1
1
  import { renderDynamicCrudFlowSchema } from './dynamicCrudFlowSchemaTemplate.js';
2
+ import {
3
+ CRUD_ACTIONS,
4
+ createCrudActionPlans,
5
+ hasGraphStorageResource,
6
+ isCrudObject
7
+ } from './crudActionPlan.js';
2
8
 
3
- const ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
4
9
  const MESSAGE_ATTRIBUTES = ['userId', 'correlationId'];
5
10
 
6
- export const dynamicCrudFlowSchemaPaths = ACTIONS.map(
11
+ export const dynamicCrudFlowSchemaPaths = CRUD_ACTIONS.map(
7
12
  (action) =>
8
13
  `app/src/schemas/FlowSchemas/generateCode/FlowObjects/${action}.js`
9
14
  );
10
15
 
11
- export function emitDynamicCrudFlowSchemas({ objects, flows = [] }) {
16
+ export function emitDynamicCrudFlowSchemas({ objects, flows = [], serviceTag }) {
12
17
  if (!Array.isArray(objects) || !Array.isArray(flows)) {
13
18
  throw new TypeError('objects and flows must be arrays');
14
19
  }
15
- if (
16
- !objects.some(
17
- (object) =>
18
- object?.generatedBy === 'userGenerated' ||
19
- object?.generatedBy === 'systemGenerated'
20
- )
21
- ) {
22
- return [];
23
- }
20
+ if (!objects.some(isCrudObject)) return [];
24
21
 
25
- return ACTIONS.map((action, index) => {
26
- const flow = _createDynamicFlow(
22
+ const plans = createCrudActionPlans(objects);
23
+
24
+ return CRUD_ACTIONS.map((action, index) => ({
25
+ path: dynamicCrudFlowSchemaPaths[index],
26
+ content: renderDynamicCrudFlowSchema(
27
27
  action,
28
- _hasBeforeLogicalStep(flows, action)
29
- );
30
- return {
31
- path: dynamicCrudFlowSchemaPaths[index],
32
- content: renderDynamicCrudFlowSchema(action, flow)
33
- };
34
- });
28
+ _createDynamicFlow(action, plans[action], serviceTag)
29
+ )
30
+ }));
35
31
  }
36
32
 
37
- function _createDynamicFlow(action, hasBeforeLogicalStep) {
38
- const outputTopic = action !== 'Get';
33
+ function _createDynamicFlow(action, plan, serviceTag = '') {
39
34
  const flowSteps = [
40
35
  {
41
- stepName: 'start',
42
- ...(hasBeforeLogicalStep
43
- ? {
44
- nextSteps: [
45
- {
46
- stepName: 'BeforeLogical'
47
- }
48
- ]
49
- }
50
- : outputTopic
36
+ stepName: 'Start',
37
+ ...(plan.hasRbacCallback || plan.hasComplete
51
38
  ? {
52
39
  nextSteps: [
53
40
  {
54
- stepName: 'Complete'
41
+ targetType: 'ownTopic',
42
+ stepName: plan.hasRbacCallback ? 'RbacCallback' : 'Complete'
55
43
  }
56
44
  ]
57
45
  }
58
46
  : {}),
59
47
  properties: [],
60
48
  messageAttributes: MESSAGE_ATTRIBUTES,
61
- ...(outputTopic
49
+ ...(plan.outputTopic
62
50
  ? {
63
51
  returnValues: {
64
52
  properties: [],
@@ -69,17 +57,19 @@ function _createDynamicFlow(action, hasBeforeLogicalStep) {
69
57
  }
70
58
  ];
71
59
 
72
- if (hasBeforeLogicalStep) {
60
+ if (plan.hasRbacCallback) {
73
61
  flowSteps.push({
74
- stepName: 'BeforeLogical',
62
+ stepName: 'RbacCallback',
75
63
  event: ['extTopic'],
76
64
  previousSteps: [
77
65
  {
78
- stepName: 'start'
66
+ sourceType: 'ownTopic',
67
+ stepName: 'Start'
79
68
  }
80
69
  ],
81
70
  nextSteps: [
82
71
  {
72
+ targetType: 'ownTopic',
83
73
  stepName: 'Complete'
84
74
  }
85
75
  ],
@@ -88,38 +78,36 @@ function _createDynamicFlow(action, hasBeforeLogicalStep) {
88
78
  });
89
79
  }
90
80
 
91
- if (action !== 'Get') {
81
+ if (plan.hasComplete) {
92
82
  flowSteps.push({
93
83
  stepName: 'Complete',
94
84
  event: ['extTopic'],
95
85
  previousSteps: [
96
86
  {
97
- stepName: hasBeforeLogicalStep ? 'BeforeLogical' : 'start'
87
+ sourceType: 'ownTopic',
88
+ stepName: plan.hasRbacCallback ? 'RbacCallback' : 'Start'
98
89
  }
99
90
  ],
100
- properties: [
101
- 'returnValue',
102
- 'parentFlowId',
103
- 'status',
104
- 'errorsFound',
105
- 'graphServiceTag'
106
- ],
91
+ properties: plan.hasGraph
92
+ ? [
93
+ 'returnValue',
94
+ 'parentFlowId',
95
+ 'status',
96
+ 'errorsFound',
97
+ 'graphServiceTag'
98
+ ]
99
+ : [],
107
100
  messageAttributes: MESSAGE_ATTRIBUTES
108
101
  });
109
102
  }
110
103
 
111
- let eventTypes = ['lambdaSyncInv', 'lambdaSyncApi'];
112
- if (outputTopic) {
113
- eventTypes = ['ownTopic'];
114
- }
115
-
116
104
  return {
117
105
  flowTag: action,
118
106
  initiateBy: 'system',
119
107
  handleObj: 'one',
120
108
  statusType: 'none',
121
- event: eventTypes,
122
- outputTopic,
109
+ event: plan.outputTopic ? ['ownTopic'] : ['lambdaSyncInv', 'lambdaSyncApi'],
110
+ outputTopic: plan.outputTopic,
123
111
  stepMessageAttributes: {
124
112
  userId: { propertyName: 'x-correlation-base-user-id', type: 'string' },
125
113
  correlationId: { propertyName: 'x-correlation-id', type: 'string' }
@@ -128,14 +116,4 @@ function _createDynamicFlow(action, hasBeforeLogicalStep) {
128
116
  };
129
117
  }
130
118
 
131
- function _hasBeforeLogicalStep(flows, action) {
132
- return flows.some((flow) => {
133
- const config = flow?.generatedCodeConfig;
134
- return (
135
- config?.generatedCodeTag === action.toLowerCase() &&
136
- config.codeHookTag === 'beforeLogical' &&
137
- Array.isArray(flow.event) &&
138
- flow.event.includes(config.invocationType)
139
- );
140
- });
141
- }
119
+ export { hasGraphStorageResource };
@@ -12,7 +12,10 @@ export async function generateDynamicCrudFlowSchemas(rootPath, options = {}) {
12
12
  const schemaInputs = await loadLocalSchemaArrays(project);
13
13
  const plan = buildGenerationPlan(
14
14
  project,
15
- emitDynamicCrudFlowSchemas(schemaInputs),
15
+ emitDynamicCrudFlowSchemas({
16
+ ...schemaInputs,
17
+ serviceTag: project.serviceTag
18
+ }),
16
19
  {
17
20
  managedPaths: dynamicCrudFlowSchemaPaths,
18
21
  managedScope: 'crud-flow-schemas'
@@ -118,8 +118,10 @@ export default async function CreateComplete(
118
118
  }
119
119
  : null;
120
120
  if (status === 'complete') {
121
+ const listOfRecords =
122
+ result?.additionalAttributes?.listOfRecords ?? [];
121
123
  await Promise.all(
122
- (pending.listOfRecords ?? []).flatMap((records) =>
124
+ (listOfRecords ?? []).flatMap((records) =>
123
125
  Object.values(records).map(async (record) =>
124
126
  dynamodbSharedLib.putItem(
125
127
  _izContext,
@@ -137,9 +139,12 @@ export default async function CreateComplete(
137
139
  )
138
140
  );
139
141
  }
142
+ const callingFlowConfig =
143
+ result?.additionalAttributes?.callingFlowConfig ||
144
+ passBackProperties;
140
145
  const message =
141
146
  callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
142
- passBackProperties,
147
+ callingFlowConfig,
143
148
  {
144
149
  objType: returnValue.requestParams.objType,
145
150
  objInstanceFull: node.objInstanceFull,
@@ -152,7 +157,7 @@ export default async function CreateComplete(
152
157
  );
153
158
  const messageAttributes =
154
159
  callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
155
- passBackProperties,
160
+ callingFlowConfig,
156
161
  {}
157
162
  );
158
163
  const topicArn = snsSharedLib.snsTopicArnByFlowSchema(