@izara_project/izara-core-generate-service-code 1.0.90 → 1.0.92

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 (82) hide show
  1. package/package.json +7 -10
  2. package/src/compile/flowValidator.js +6 -0
  3. package/src/compile/loadSchemas.js +177 -69
  4. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +2 -2
  5. package/src/emit/crud/generateCrudApplicationArtifacts.js +15 -17
  6. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +16 -21
  7. package/src/emit/crud/loadLocalSchemaArrays.js +4 -8
  8. package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +6 -6
  9. package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +3 -3
  10. package/src/emit/crud/templates/CreateComplete_Main.ejs +22 -22
  11. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +18 -18
  12. package/src/emit/crud/templates/CreateLogicalLib.ejs +47 -47
  13. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +24 -24
  14. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +5 -5
  15. package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +3 -3
  16. package/src/emit/crud/templates/DeleteComplete_Main.ejs +23 -23
  17. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +15 -15
  18. package/src/emit/crud/templates/DeleteLogicalLib.ejs +19 -19
  19. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +3 -3
  20. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +5 -5
  21. package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +3 -3
  22. package/src/emit/crud/templates/UpdateComplete_Main.ejs +22 -22
  23. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +15 -15
  24. package/src/emit/crud/templates/UpdateLogicalLib.ejs +33 -33
  25. package/src/emit/custom/customFlowPaths.js +149 -0
  26. package/src/emit/custom/customFlowS3.js +156 -0
  27. package/src/emit/custom/customFlowSteps.js +385 -0
  28. package/src/emit/custom/customFlowTemplates.js +28 -0
  29. package/src/emit/custom/customFlows.js +48 -19
  30. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +57 -692
  31. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +27 -25
  32. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +7 -7
  33. package/src/emit/custom/templates/s3/ConfirmReserved_HdrSqs.ejs +3 -3
  34. package/src/emit/custom/templates/s3/ConfirmReserved_Main.ejs +3 -3
  35. package/src/emit/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +3 -3
  36. package/src/emit/custom/templates/s3/CreatePreSignUrl_Main.ejs +4 -4
  37. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +26 -6
  38. package/src/emit/rbac/emitRbacApplicationArtifacts.js +40 -13
  39. package/src/emit/rbac/generateCreateTargetRoleHandler.js +11 -13
  40. package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +14 -18
  41. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +13 -7
  42. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  43. package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +16 -19
  44. package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +19 -19
  45. package/src/emit/relationship/templates/ChangeRelationshipComplete_HdrSqs.ejs +2 -2
  46. package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +6 -6
  47. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +17 -17
  48. package/src/emit/relationship/templates/CreateRelationshipComplete_HdrSqs.ejs +2 -2
  49. package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +6 -6
  50. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +14 -14
  51. package/src/emit/relationship/templates/DeleteRelationshipComplete_HdrSqs.ejs +2 -2
  52. package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +6 -6
  53. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +10 -10
  54. package/src/emit/relationship/templates/GetRelationshipComplete_HdrSqs.ejs +2 -2
  55. package/src/emit/relationship/templates/GetRelationshipComplete_Main.ejs +5 -5
  56. package/src/emit/relationship/templates/GetRelationship_Main.ejs +9 -9
  57. package/src/emit/relationship/templates/MoveRelationshipComplete_HdrSqs.ejs +2 -2
  58. package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +6 -6
  59. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +15 -15
  60. package/src/emit/relationship/templates/UpdateRelationshipComplete_HdrSqs.ejs +2 -2
  61. package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +6 -6
  62. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +11 -11
  63. package/src/emit/resource/dynamoResourceArtifacts.js +18 -0
  64. package/src/emit/resource/emitResourceArtifacts.js +13 -1251
  65. package/src/emit/resource/generateResourceArtifacts.js +20 -20
  66. package/src/emit/resource/iamResourceArtifacts.js +612 -0
  67. package/src/emit/resource/resourceState.js +564 -0
  68. package/src/emit/resource/resourceTemplate.js +30 -0
  69. package/src/emit/resource/s3ResourceArtifacts.js +14 -0
  70. package/src/emit/resource/sqsResourceArtifacts.js +113 -0
  71. package/src/emit/shared/runSubsystemGeneration.js +55 -0
  72. package/src/emit/shared/templates/FindData_Main.ejs +19 -19
  73. package/src/emit/shared/templates/PaginateProcessLogical_Main.ejs +3 -3
  74. package/src/emit/shared/templates/ProcessLogical_Main.ejs +15 -15
  75. package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
  76. package/test/createTargetRoleHandler.test.js +62 -4
  77. package/test/crudApplicationArtifacts.test.js +75 -3
  78. package/test/customFlowApplicationArtifacts.test.js +98 -0
  79. package/test/dynamicCrudFlowSchemas.test.js +2 -2
  80. package/test/dynamicRbacFlowSchemas.test.js +32 -4
  81. package/test/relationshipApplicationArtifacts.test.js +1 -1
  82. package/test/resourceArtifacts.test.js +99 -0
@@ -0,0 +1,564 @@
1
+ import {
2
+ customFlowRoleName,
3
+ flowDsqSteps,
4
+ flowExtTopicSteps,
5
+ generatedDsqFlows,
6
+ generatedOwnTopicFlows,
7
+ supportedCustomFlows,
8
+ flowHasRbac
9
+ } from '../custom/customFlows.js';
10
+ import { hasSystemWebSocket } from '../system/emitSystemWebSocketArtifacts.js';
11
+ import { rbacFlowTag } from '../rbac/rbacFlowTag.js';
12
+ import { CRUD_ACTIONS, createCrudActionPlans } from '../crud/crudActionPlan.js';
13
+
14
+ export const RELATIONSHIP_ACTIONS = [
15
+ 'CreateRelationship',
16
+ 'UpdateRelationship',
17
+ 'DeleteRelationship',
18
+ 'GetRelationship',
19
+ 'ChangeRelationship',
20
+ 'MoveRelationship'
21
+ ];
22
+
23
+ export const RBAC_ACTIONS = [
24
+ 'CreateTargetRole',
25
+ 'UpdateTargetRole',
26
+ 'DeleteTargetRole',
27
+ 'GetTargetRole',
28
+ 'ListTargetRole',
29
+ 'CreateRolePermissions',
30
+ 'ListRolePermissions',
31
+ 'DeleteRolePermissions',
32
+ 'CreateUserRole',
33
+ 'ListUserInRoles',
34
+ 'DeleteUserFromRole',
35
+ 'GetMyPermissions',
36
+ 'ValidatorRbac',
37
+ 'ReservedDynamicUsage',
38
+ 'ConfirmUsage',
39
+ 'CancelUsage'
40
+ ];
41
+
42
+ export const SYSTEM_TABLES = [
43
+ 'WebSocketTask',
44
+ 'UserUploadRecords',
45
+ 'RegisterRecords',
46
+ 'AwaitingMultipleSteps',
47
+ 'AwaitingMultipleStepByPending',
48
+ 'FindDataMain',
49
+ 'LogicalResultsMain',
50
+ 'LogicalResultsData',
51
+ 'AwaitingStep'
52
+ ];
53
+
54
+ export const RBAC_TABLES = [
55
+ 'Roles',
56
+ 'TargetRoles',
57
+ 'RolePermissions',
58
+ 'RoleUsers',
59
+ 'UserRoles',
60
+ 'RbacPermissionCaches',
61
+ 'RunningTotalUsage',
62
+ 'RunningTotalReservedUsage',
63
+ 'WebSocketTask'
64
+ ];
65
+
66
+ const VALID_CRUD_GENERATED_BY = new Set([
67
+ 'user',
68
+ 'systemWithUser',
69
+ 'systemNoUser'
70
+ ]);
71
+
72
+ export function createResourceState({ objects, relationships, flows }) {
73
+ const crud = objects.some(isCrudObject);
74
+ const selfRbac = objects.some(hasSelfRbacAddOn);
75
+ const rbac = objects.some((obj) => hasRbacAddOn(obj, objects));
76
+ const crudActionPlans = createCrudActionPlans(objects, flows);
77
+
78
+ let crudOwnTopics = [];
79
+ if (crud) {
80
+ crudOwnTopics = [...CRUD_ACTIONS];
81
+ }
82
+
83
+ const crudSqsQueues = [];
84
+ if (crud) {
85
+ for (const action of ['Create', 'Update', 'Delete']) {
86
+ const plan = crudActionPlans[action];
87
+ if (plan.hasCreateNodeCallback) {
88
+ crudSqsQueues.push(`${action}NodeCallbackHdrSqs`);
89
+ }
90
+ if (plan.hasBeforeHookCallback) {
91
+ crudSqsQueues.push(`${action}BeforeHookCallbackHdrSqs`);
92
+ }
93
+ if (plan.hasAfterHookCallback) {
94
+ crudSqsQueues.push(`${action}AfterHookCallbackHdrSqs`);
95
+ }
96
+ if (plan.hasComplete) {
97
+ crudSqsQueues.push(`${action}CompleteHdrSqs`);
98
+ }
99
+ }
100
+ }
101
+
102
+ let relationshipOwnTopics = [];
103
+ if (relationships.length > 0) {
104
+ relationshipOwnTopics = [...RELATIONSHIP_ACTIONS];
105
+ }
106
+
107
+ let relationshipSqsQueues = [];
108
+ if (relationships.length > 0) {
109
+ relationshipSqsQueues = RELATIONSHIP_ACTIONS.map(
110
+ (action) => `${action}CompleteHdrSqs`
111
+ );
112
+ }
113
+
114
+ let relationshipDsqQueues = [];
115
+ if (relationships.length > 0) {
116
+ relationshipDsqQueues = ['GetRelationshipHdrDsq'];
117
+ }
118
+
119
+ const rbacOwnTopics = [];
120
+ const rbacSqsQueues = [];
121
+ if (selfRbac) {
122
+ for (const action of RBAC_ACTIONS) {
123
+ rbacOwnTopics.push(action);
124
+ rbacSqsQueues.push(`Process${action}HdrSqs`);
125
+ }
126
+ const flowTags = [
127
+ ...new Set(objects.filter(hasSelfRbacAddOn).map(rbacFlowTag))
128
+ ];
129
+ for (const flowTag of flowTags) {
130
+ rbacOwnTopics.push(flowTag);
131
+ }
132
+ }
133
+ if (crud) {
134
+ for (const action of ['Create', 'Update', 'Delete']) {
135
+ const plan = crudActionPlans[action];
136
+ if (plan.hasRbacCallback) {
137
+ rbacSqsQueues.push(`${action}RbacCallbackHdrSqs`);
138
+ }
139
+ }
140
+ }
141
+
142
+ const customOwnTopics = flows
143
+ .filter((flow) => flowHasOwnTopic(flow))
144
+ .map((flow) => upperFirst(flow.flowTag));
145
+
146
+ const customSqsQueues = [
147
+ ...flows
148
+ .filter((flow) => includesEvent(flow, 'extTopic'))
149
+ .map((flow) => `Process${upperFirst(flow.flowTag)}HdrSqs`),
150
+ ...flows
151
+ .filter(
152
+ (flow) =>
153
+ flow?.outputTopic &&
154
+ !flow?.generatedCodeConfig &&
155
+ upperFirst(flow.flowTag)
156
+ )
157
+ .map((flow) => `${upperFirst(flow.flowTag)}CompleteHdrSqs`)
158
+ ];
159
+ for (const { flowTag, stepName } of flowExtTopicSteps(flows)) {
160
+ customSqsQueues.push(`${flowTag}${stepName}HdrSqs`);
161
+ }
162
+ for (const flow of flows) {
163
+ if (flowHasRbac(flow, objects)) {
164
+ const tag = upperFirst(flow.flowTag);
165
+ customSqsQueues.push(`${tag}RbacCallbackHdrSqs`);
166
+ }
167
+ }
168
+ for (const flow of flows.filter((flow) => includesEvent(flow, 's3'))) {
169
+ const tag = upperFirst(flow.flowTag);
170
+ customSqsQueues.push(`${tag}CreatePreSignUrlHdrSqs`);
171
+ customSqsQueues.push(`${tag}ReservedLimitHdrSqs`);
172
+ customSqsQueues.push(`${tag}ConfirmAfterUploadHdrSqs`);
173
+ }
174
+
175
+ const customDsqQueues = flows
176
+ .filter((flow) => includesEvent(flow, 'dsq'))
177
+ .map((flow) => `${upperFirst(flow.flowTag)}HdrDsq`);
178
+ for (const { flowTag, stepName } of flowDsqSteps(flows)) {
179
+ customDsqQueues.push(`${flowTag}${stepName}HdrDsq`);
180
+ }
181
+
182
+ const websocket = hasSystemWebSocket(flows, {
183
+ includeRelationshipOwnTopic: true,
184
+ objects,
185
+ relationships
186
+ });
187
+
188
+ const systemOwnTopics = ['ProcessLogical', 'FindData'];
189
+ let systemSqsQueues = ['PaginateProcessLogicalHdrSqs'];
190
+ if (websocket) {
191
+ systemSqsQueues.push('WebSocketCompleteHdrSqs', 'RegisterCompleteHdrSqs');
192
+ }
193
+ const systemDsqQueues = ['PaginateProcessLogicalHdrDsq'];
194
+
195
+ const ownTopics = new Set([
196
+ ...systemOwnTopics,
197
+ ...crudOwnTopics,
198
+ ...relationshipOwnTopics,
199
+ ...rbacOwnTopics,
200
+ ...customOwnTopics
201
+ ]);
202
+ const sqsQueues = new Set([
203
+ ...systemSqsQueues,
204
+ ...crudSqsQueues,
205
+ ...relationshipSqsQueues,
206
+ ...rbacSqsQueues,
207
+ ...customSqsQueues
208
+ ]);
209
+ const dsqQueues = new Set([
210
+ ...systemDsqQueues,
211
+ ...relationshipDsqQueues,
212
+ ...customDsqQueues
213
+ ]);
214
+
215
+ const customRoles = [
216
+ ...new Set([
217
+ ...supportedCustomFlows(flows).map((flow) =>
218
+ customFlowRoleName(flow.flowTag)
219
+ ),
220
+ ...generatedOwnTopicFlows(flows).map((flow) =>
221
+ customFlowRoleName(flow.flowTag)
222
+ ),
223
+ ...generatedDsqFlows(flows).map((flow) =>
224
+ customFlowRoleName(flow.flowTag)
225
+ ),
226
+ ...flowExtTopicSteps(flows).map(({ flow }) =>
227
+ customFlowRoleName(flow.flowTag)
228
+ ),
229
+ ...flowDsqSteps(flows).map(({ flow }) => customFlowRoleName(flow.flowTag))
230
+ ])
231
+ ];
232
+ const webSocketRoles = new Set();
233
+ if (websocket) {
234
+ webSocketRoles.add('WebSocketMainRole');
235
+ webSocketRoles.add('RegisterRole');
236
+ }
237
+ if (crud) {
238
+ webSocketRoles.add('FlowObjectsRole');
239
+ }
240
+ if (relationships.length > 0) {
241
+ webSocketRoles.add('FlowRelationshipRole');
242
+ }
243
+ if (rbac) {
244
+ webSocketRoles.add('FlowRbacRole');
245
+ }
246
+ const filteredWsFlows = [
247
+ ...supportedCustomFlows(flows),
248
+ ...generatedOwnTopicFlows(flows)
249
+ ].filter(
250
+ (flow) => flowHasOwnTopic(flow) || includesEvent(flow, 's3')
251
+ );
252
+ for (const flow of filteredWsFlows) {
253
+ webSocketRoles.add(customFlowRoleName(flow.flowTag));
254
+ }
255
+ const outTopics = new Set([
256
+ ...ownTopics,
257
+ ...flows
258
+ .filter((flow) => flow?.outputTopic && upperFirst(flow.flowTag))
259
+ .map((flow) => upperFirst(flow.flowTag))
260
+ ]);
261
+ const objectTableNames = [...collectTables(objects).keys()];
262
+ const relationshipTableNames = [...collectTables(relationships).keys()];
263
+ const tableMap = new Map([
264
+ ...collectTables(objects),
265
+ ...collectTables(relationships),
266
+ ...collectTables(flows)
267
+ ]);
268
+ const externalDynamoTables = objects.flatMap(
269
+ (object) => object.externalDynamoResources ?? []
270
+ );
271
+ const s3Buckets = flows
272
+ .filter((flow) => includesEvent(flow, 's3') && flow.createBucket === true)
273
+ .map((flow) => flow.bucketName)
274
+ .filter(Boolean);
275
+ const s3FlowTags = flows
276
+ .filter((flow) => includesEvent(flow, 's3'))
277
+ .map((flow) => upperFirst(flow.flowTag));
278
+ const s3Subscriptions = flows
279
+ .filter((flow) => includesEvent(flow, 's3'))
280
+ .map((flow) => {
281
+ const tag = upperFirst(flow.flowTag);
282
+ let topicName = flow.s3Subscription?.topicName || 'OutCreate';
283
+ if (typeof flow.outputTopic === 'string') {
284
+ if (flow.outputTopic.startsWith('Out')) {
285
+ topicName = flow.outputTopic;
286
+ } else {
287
+ topicName = `Out${upperFirst(flow.outputTopic)}`;
288
+ }
289
+ }
290
+ const queueName =
291
+ flow.s3Subscription?.queueName || `${tag}ReservedLimitHdrSqs`;
292
+ const subscriptionName =
293
+ flow.s3Subscription?.subscriptionName ||
294
+ `Subscription${topicName}To${queueName}`;
295
+ return { subscriptionName, topicName, queueName };
296
+ });
297
+ const systemTables = [...SYSTEM_TABLES];
298
+ if (rbac) {
299
+ systemTables.push(
300
+ 'Roles',
301
+ 'TargetRoles',
302
+ 'RolePermissions',
303
+ 'RoleUsers',
304
+ 'UserRoles',
305
+ 'RbacPermissionCaches',
306
+ 'RunningTotalUsage',
307
+ 'RunningTotalReservedUsage'
308
+ );
309
+ }
310
+ if (s3FlowTags.length > 0) {
311
+ systemTables.push('ReservedDataMain');
312
+ }
313
+ const hasHttpApi =
314
+ crud ||
315
+ relationships.length > 0 ||
316
+ rbac ||
317
+ flows.some((flow) => includesEvent(flow, 'lambdaSyncApi'));
318
+
319
+ let webSocketSubscriptions = [];
320
+ if (websocket) {
321
+ webSocketSubscriptions = [...outTopics]
322
+ .filter(
323
+ (topic) =>
324
+ flows.some(
325
+ (flow) =>
326
+ (flow?.outputTopic ||
327
+ (flowHasOwnTopic(flow) &&
328
+ (flow.initiateBy === 'user' ||
329
+ flow.initiateBy === 'systemWithUser'))) &&
330
+ upperFirst(flow.flowTag) === topic
331
+ ) ||
332
+ (objects.length > 0 &&
333
+ topic !== 'Get' &&
334
+ CRUD_ACTIONS.includes(topic)) ||
335
+ (relationships.length > 0 &&
336
+ topic !== 'GetRelationship' &&
337
+ RELATIONSHIP_ACTIONS.includes(topic))
338
+ )
339
+ .sort();
340
+ }
341
+
342
+ return {
343
+ crud,
344
+ rbac,
345
+ selfRbac,
346
+ crudActionPlans,
347
+ crudOwnTopics: [...new Set(crudOwnTopics)].filter(Boolean).sort(),
348
+ crudSqsQueues: [...new Set(crudSqsQueues)].filter(Boolean).sort(),
349
+ relationshipOwnTopics: [...new Set(relationshipOwnTopics)]
350
+ .filter(Boolean)
351
+ .sort(),
352
+ relationshipSqsQueues: [...new Set(relationshipSqsQueues)]
353
+ .filter(Boolean)
354
+ .sort(),
355
+ relationshipDsqQueues: [...new Set(relationshipDsqQueues)]
356
+ .filter(Boolean)
357
+ .sort(),
358
+ rbacOwnTopics: [...new Set(rbacOwnTopics)].filter(Boolean).sort(),
359
+ rbacSqsQueues: [...new Set(rbacSqsQueues)].filter(Boolean).sort(),
360
+ customOwnTopics: [...new Set(customOwnTopics)].filter(Boolean).sort(),
361
+ customSqsQueues: [...new Set(customSqsQueues)].filter(Boolean).sort(),
362
+ customDsqQueues: [...new Set(customDsqQueues)].filter(Boolean).sort(),
363
+ systemOwnTopics: [...new Set(systemOwnTopics)].filter(Boolean).sort(),
364
+ systemSqsQueues: [...new Set(systemSqsQueues)].filter(Boolean).sort(),
365
+ systemDsqQueues: [...new Set(systemDsqQueues)].filter(Boolean).sort(),
366
+ s3Subscriptions,
367
+ s3FlowTags,
368
+ hasHttpApi,
369
+ relationships: relationships.length > 0,
370
+ ownTopics: [...ownTopics].filter(Boolean).sort(),
371
+ sqsQueues: [...sqsQueues].filter(Boolean).sort(),
372
+ dsqQueues: [...dsqQueues].filter(Boolean).sort(),
373
+ customRoles,
374
+ websocket,
375
+ webSocketRoles,
376
+ webSocketSubscriptions,
377
+ outTopics: [...outTopics].filter(Boolean).sort(),
378
+ tables: [...tableMap.entries()].map(([tableName, keys]) => ({
379
+ tableName,
380
+ ...keys
381
+ })),
382
+ allTableNames: [...new Set([...systemTables, ...tableMap.keys()])].sort(),
383
+ objectTableNames,
384
+ relationshipTableNames,
385
+ externalDynamoTables,
386
+ objectTablesByType: new Map(
387
+ objects
388
+ .map((object) => {
389
+ const resource = Object.values(object.storageResources ?? {}).find(
390
+ (res) =>
391
+ res?.storageType === 'dynamo' ||
392
+ res?.storageType === 'dynamoDB' ||
393
+ !res?.storageType
394
+ );
395
+ return [object.objectType, resource?.tableName];
396
+ })
397
+ .filter(([, tableName]) => tableName)
398
+ ),
399
+ s3Buckets,
400
+ flows,
401
+ objects
402
+ };
403
+ }
404
+
405
+ export function collectTables(schemas) {
406
+ const tables = new Map();
407
+ for (const schema of schemas) {
408
+ if (!schema?.storageResources) continue;
409
+ const identifiers = Array.isArray(schema.identifiers)
410
+ ? schema.identifiers
411
+ : [];
412
+ const pkIdentifier = identifiers.find(
413
+ (identifier) =>
414
+ identifier.type === 'partitionKey' || identifier.type === 'identifier'
415
+ );
416
+ const skIdentifier = identifiers.find(
417
+ (identifier) => identifier.type === 'sortKey'
418
+ );
419
+ const pk = keyName(pkIdentifier) ?? 'PK';
420
+ const sk = keyName(skIdentifier);
421
+ const pkType = keyAttributeType(pkIdentifier, schema);
422
+ const skType = keyAttributeType(skIdentifier, schema);
423
+ for (const resource of Object.values(schema.storageResources)) {
424
+ if (
425
+ (resource?.storageType === 'dynamo' ||
426
+ resource?.storageType === 'dynamoDB' ||
427
+ !resource?.storageType) &&
428
+ resource.tableName &&
429
+ !tables.has(resource.tableName)
430
+ ) {
431
+ tables.set(resource.tableName, { pk, sk, pkType, skType });
432
+ }
433
+ }
434
+ }
435
+ return tables;
436
+ }
437
+
438
+ export function keyAttributeType(identifier, schema) {
439
+ if (!identifier) return 'S';
440
+ if (identifier.attributeType) {
441
+ return identifier.attributeType;
442
+ }
443
+ if (identifier.dataType) {
444
+ return mapTypeToAttributeType(identifier.dataType);
445
+ }
446
+ if (identifier.fieldType) {
447
+ return mapTypeToAttributeType(identifier.fieldType);
448
+ }
449
+ const fieldName = identifier.fieldName ?? identifier.name;
450
+ if (fieldName && schema) {
451
+ let fieldDef =
452
+ schema.fieldNames?.[fieldName] ?? schema.properties?.[fieldName];
453
+ if (!fieldDef && Array.isArray(schema.addOnDataStructure)) {
454
+ for (const addOn of schema.addOnDataStructure) {
455
+ if (addOn?.fieldNames?.[fieldName]) {
456
+ fieldDef = addOn.fieldNames[fieldName];
457
+ break;
458
+ }
459
+ }
460
+ }
461
+ if (fieldDef?.attributeType) {
462
+ return fieldDef.attributeType;
463
+ }
464
+ const type = fieldDef?.type ?? fieldDef?.dataType;
465
+ if (type) {
466
+ return mapTypeToAttributeType(type);
467
+ }
468
+ }
469
+ return 'S';
470
+ }
471
+
472
+ export function mapTypeToAttributeType(type) {
473
+ if (typeof type !== 'string') return 'S';
474
+ const lower = type.toLowerCase();
475
+ if (
476
+ lower === 'number' ||
477
+ lower === 'numeric' ||
478
+ lower === 'integer' ||
479
+ lower === 'float' ||
480
+ lower === 'int' ||
481
+ lower === 'double' ||
482
+ lower === 'n'
483
+ ) {
484
+ return 'N';
485
+ }
486
+ if (lower === 'binary' || lower === 'b') {
487
+ return 'B';
488
+ }
489
+ return 'S';
490
+ }
491
+
492
+ export function keyName(identifier) {
493
+ if (!identifier) return null;
494
+ return (
495
+ identifier.name ??
496
+ identifier.fieldName ??
497
+ (Array.isArray(identifier.fieldNames)
498
+ ? identifier.fieldNames.join(identifier.deliminator ?? '_')
499
+ : null)
500
+ );
501
+ }
502
+
503
+ export function isCrudObject(object) {
504
+ return VALID_CRUD_GENERATED_BY.has(object?.generatedBy);
505
+ }
506
+
507
+ export function hasSelfRbacAddOn(object) {
508
+ return (
509
+ Array.isArray(object?.addOnDataStructure) &&
510
+ object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac')
511
+ );
512
+ }
513
+
514
+ export function hasRbacAddOn(object, objects = []) {
515
+ const selfHasRbac = hasSelfRbacAddOn(object);
516
+ const belongToTargets = object?.belongTo
517
+ ? Array.isArray(object.belongTo)
518
+ ? object.belongTo
519
+ : [object.belongTo]
520
+ : [];
521
+ const belongToHasRbac =
522
+ (Array.isArray(object?.belongToSchema?.addOnDataStructure) &&
523
+ object.belongToSchema.addOnDataStructure.some(
524
+ (addOn) => addOn?.type === 'rbac'
525
+ )) ||
526
+ belongToTargets.some((target) => {
527
+ if (target?.serviceTag === 'UserAccount') return true;
528
+ const matched = objects.find(
529
+ (candidate) =>
530
+ candidate?.serviceTag === target?.serviceTag &&
531
+ candidate?.objectType === target?.objectType
532
+ );
533
+ return hasSelfRbacAddOn(matched);
534
+ });
535
+ return selfHasRbac || belongToHasRbac;
536
+ }
537
+
538
+ export function rbacFlowQueueNames(state) {
539
+ return [];
540
+ }
541
+
542
+ export function includesEvent(flow, event) {
543
+ return Array.isArray(flow?.event) && flow.event.includes(event);
544
+ }
545
+
546
+ export function flowHasOwnTopic(flow) {
547
+ if (includesEvent(flow, 'ownTopic')) {
548
+ return true;
549
+ }
550
+ if (
551
+ includesEvent(flow, 'generateCode') &&
552
+ flow?.generatedCodeConfig?.invocationType === 'ownTopic'
553
+ ) {
554
+ return true;
555
+ }
556
+ return false;
557
+ }
558
+
559
+ export function upperFirst(value) {
560
+ if (typeof value === 'string' && value) {
561
+ return value[0].toUpperCase() + value.slice(1);
562
+ }
563
+ return '';
564
+ }
@@ -0,0 +1,30 @@
1
+ import ejsLib from 'ejs';
2
+ import { readFile } from 'node:fs/promises';
3
+
4
+ const TEMPLATE_URL = new URL('./templates/', import.meta.url);
5
+
6
+ export function indentYaml(content, spaces = 2) {
7
+ const prefix = ' '.repeat(spaces);
8
+ return content
9
+ .split('\n')
10
+ .map((line) => {
11
+ if (line.trim()) {
12
+ return `${prefix}${line}`;
13
+ }
14
+ return '';
15
+ })
16
+ .join('\n');
17
+ }
18
+
19
+ export function normalizeYaml(content) {
20
+ return `${content.trimEnd()}\n`;
21
+ }
22
+
23
+ export async function renderResourceTemplate(templateName, data) {
24
+ const templatePath = new URL(templateName, TEMPLATE_URL);
25
+ const templateContent = await readFile(templatePath, 'utf8');
26
+ return ejsLib.render(templateContent, {
27
+ resourceQueueName: data.queueName,
28
+ ...data
29
+ });
30
+ }
@@ -0,0 +1,14 @@
1
+ import { indentYaml, renderResourceTemplate } from './resourceTemplate.js';
2
+
3
+ export async function renderS3(state) {
4
+ if (state.s3Buckets.length === 0) {
5
+ return '';
6
+ }
7
+ const renderedBuckets = await Promise.all(
8
+ state.s3Buckets.map((bucketName) =>
9
+ renderResourceTemplate('S3Bucket_Yaml.ejs', { bucketName })
10
+ )
11
+ );
12
+ const body = renderedBuckets.join('\n');
13
+ return `Resources:\n${indentYaml(body, 2)}`;
14
+ }