@izara_project/izara-core-generate-service-code 1.0.91 → 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.
- package/package.json +7 -10
- package/src/compile/flowValidator.js +6 -0
- package/src/compile/loadSchemas.js +177 -69
- package/src/emit/crud/generateCrudApplicationArtifacts.js +15 -17
- package/src/emit/crud/generateDynamicCrudFlowSchemas.js +16 -21
- package/src/emit/crud/loadLocalSchemaArrays.js +4 -8
- package/src/emit/custom/customFlowPaths.js +149 -0
- package/src/emit/custom/customFlowS3.js +156 -0
- package/src/emit/custom/customFlowSteps.js +385 -0
- package/src/emit/custom/customFlowTemplates.js +28 -0
- package/src/emit/custom/customFlows.js +48 -19
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +57 -692
- package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +27 -25
- package/src/emit/rbac/generateCreateTargetRoleHandler.js +11 -13
- package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +14 -18
- package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +16 -19
- package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +19 -19
- package/src/emit/resource/dynamoResourceArtifacts.js +18 -0
- package/src/emit/resource/emitResourceArtifacts.js +13 -1247
- package/src/emit/resource/generateResourceArtifacts.js +20 -20
- package/src/emit/resource/iamResourceArtifacts.js +612 -0
- package/src/emit/resource/resourceState.js +564 -0
- package/src/emit/resource/resourceTemplate.js +30 -0
- package/src/emit/resource/s3ResourceArtifacts.js +14 -0
- package/src/emit/resource/sqsResourceArtifacts.js +113 -0
- package/src/emit/shared/runSubsystemGeneration.js +55 -0
- package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
- package/test/crudApplicationArtifacts.test.js +72 -0
- package/test/customFlowApplicationArtifacts.test.js +98 -0
- 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
|
+
}
|