@izara_project/izara-core-generate-service-code 1.0.66 → 1.0.67
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/docs/TriggeredCache Flow.drawio +193 -0
- package/docs/test.mmd +47 -0
- package/docs/test.png +0 -0
- package/package.json +2 -2
- package/src/compile/loadSchemas.js +23 -1
- package/src/emit/crud/emitCrudApplicationArtifacts.js +9 -5
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +5 -5
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +5 -5
- package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +25 -4
- package/src/emit/crud/templates/FlowHandler_HdrApi.ejs +18 -0
- package/src/emit/crud/templates/GetEndpoint_Main.ejs +2 -2
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +10 -8
- package/src/emit/custom/customFlows.js +76 -25
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +92 -75
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +4 -4
- package/src/emit/custom/templates/triggerCache/CheckTriggerCache_Main.ejs +1 -1
- package/src/emit/function/functionYamlArtifact.js +26 -12
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +21 -7
- package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +6 -5
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +2 -2
- package/src/emit/resource/emitResourceArtifacts.js +400 -105
- package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
- package/src/generate.js +2 -2
- package/test/crudApplicationArtifacts.test.js +1 -1
- package/test/customFlowApplicationArtifacts.test.js +37 -34
- package/test/publicApi.test.js +9 -9
- package/test/resourceArtifacts.test.js +1 -1
|
@@ -1,22 +1,84 @@
|
|
|
1
1
|
import ejs from 'ejs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
customFlowRoleName,
|
|
6
|
+
flowDsqSteps,
|
|
7
|
+
flowExtTopicSteps,
|
|
8
|
+
generatedOwnTopicFlows,
|
|
9
|
+
supportedCustomFlows
|
|
10
|
+
} from '../custom/customFlows.js';
|
|
5
11
|
import { hasSystemWebSocket } from '../system/emitSystemWebSocketArtifacts.js';
|
|
6
12
|
|
|
7
13
|
const TEMPLATE_URL = new URL('./templates/', import.meta.url);
|
|
8
14
|
const RESOURCE_ROOT = 'resource/sls_yaml/generatedCode/source';
|
|
9
15
|
const APP_ROOT = 'app/sls_yaml/generatedCode/source';
|
|
10
16
|
const CRUD_ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
|
|
11
|
-
const RELATIONSHIP_ACTIONS = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const RELATIONSHIP_ACTIONS = [
|
|
18
|
+
'CreateRelationship',
|
|
19
|
+
'UpdateRelationship',
|
|
20
|
+
'DeleteRelationship',
|
|
21
|
+
'GetRelationship',
|
|
22
|
+
'ChangeRelationship',
|
|
23
|
+
'MoveRelationship'
|
|
24
|
+
];
|
|
25
|
+
const RBAC_ACTIONS = [
|
|
26
|
+
'CreateTargetRole',
|
|
27
|
+
'ListTargetRole',
|
|
28
|
+
'DeleteTargetRole',
|
|
29
|
+
'CreateRolePermissions',
|
|
30
|
+
'ListRolePermissions',
|
|
31
|
+
'DeleteRolePermissions',
|
|
32
|
+
'CreateUserRole',
|
|
33
|
+
'ListUserInRoles',
|
|
34
|
+
'DeleteUserFromRole'
|
|
35
|
+
];
|
|
36
|
+
const SYSTEM_TABLES = [
|
|
37
|
+
'WebSocketTask',
|
|
38
|
+
'UserUploadRecords',
|
|
39
|
+
'RegisterRecords',
|
|
40
|
+
'AwaitingMultipleSteps',
|
|
41
|
+
'AwaitingMultipleStepByPending',
|
|
42
|
+
'FindDataMain',
|
|
43
|
+
'LogicalResultsMain',
|
|
44
|
+
'LogicalResultsData',
|
|
45
|
+
'AwaitingStep',
|
|
46
|
+
'TargetRoles',
|
|
47
|
+
'RolePermissions',
|
|
48
|
+
'RoleUsers',
|
|
49
|
+
'UserRoles'
|
|
50
|
+
];
|
|
51
|
+
const RBAC_TABLES = [
|
|
52
|
+
'TargetRoles',
|
|
53
|
+
'RolePermissions',
|
|
54
|
+
'RoleUsers',
|
|
55
|
+
'UserRoles',
|
|
56
|
+
'WebSocketTask'
|
|
57
|
+
];
|
|
58
|
+
const LEGACY_PROCESS_QUEUES = [
|
|
59
|
+
'FindDataHdrSqs',
|
|
60
|
+
'ProcessLogicalHdrSqs',
|
|
61
|
+
'PaginateProcessLogicalHdrDsq',
|
|
62
|
+
'PaginateProcessLogicalHdrSqs'
|
|
63
|
+
];
|
|
64
|
+
const DYNAMO_ACTIONS = [
|
|
65
|
+
'dynamodb:DeleteItem',
|
|
66
|
+
'dynamodb:GetItem',
|
|
67
|
+
'dynamodb:PutItem',
|
|
68
|
+
'dynamodb:Query',
|
|
69
|
+
'dynamodb:UpdateItem'
|
|
70
|
+
];
|
|
71
|
+
const SQS_CONSUMER_ACTIONS = [
|
|
72
|
+
'sqs:DeleteMessage',
|
|
73
|
+
'sqs:GetQueueAttributes',
|
|
74
|
+
'sqs:ReceiveMessage'
|
|
75
|
+
];
|
|
18
76
|
|
|
19
|
-
export async function emitResourceArtifacts({
|
|
77
|
+
export async function emitResourceArtifacts({
|
|
78
|
+
objects,
|
|
79
|
+
relationships,
|
|
80
|
+
flows = []
|
|
81
|
+
}) {
|
|
20
82
|
if (![objects, relationships, flows].every(Array.isArray)) {
|
|
21
83
|
throw new TypeError('objects, relationships, and flows must be arrays');
|
|
22
84
|
}
|
|
@@ -28,10 +90,22 @@ export async function emitResourceArtifacts({ objects, relationships, flows = []
|
|
|
28
90
|
renderIam(state)
|
|
29
91
|
]);
|
|
30
92
|
return [
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
93
|
+
{
|
|
94
|
+
path: `${RESOURCE_ROOT}/generated-sns-in-sqs.yml`,
|
|
95
|
+
content: normalizeYaml(inSqs)
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
path: `${RESOURCE_ROOT}/generated-sns-out.yml`,
|
|
99
|
+
content: normalizeYaml(out)
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
path: `${RESOURCE_ROOT}/generated-dynamoDB-table.yml`,
|
|
103
|
+
content: normalizeYaml(dynamo)
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
path: `${APP_ROOT}/generate-shared-resource.yml`,
|
|
107
|
+
content: normalizeYaml(iam)
|
|
108
|
+
}
|
|
35
109
|
];
|
|
36
110
|
}
|
|
37
111
|
|
|
@@ -45,27 +119,41 @@ export const resourceArtifactPaths = [
|
|
|
45
119
|
function createResourceState({ objects, relationships, flows }) {
|
|
46
120
|
const crud = objects.some(isCrudObject);
|
|
47
121
|
const rbac = objects.some(hasRbacAddOn);
|
|
48
|
-
const ownTopics = new Set(
|
|
122
|
+
const ownTopics = new Set(
|
|
123
|
+
flows
|
|
124
|
+
.filter(flow => includesEvent(flow, 'ownTopic'))
|
|
125
|
+
.map(flow => upperFirst(flow.flowTag))
|
|
126
|
+
);
|
|
49
127
|
const sqsQueues = new Set([
|
|
50
128
|
...LEGACY_PROCESS_QUEUES,
|
|
51
|
-
...flows
|
|
129
|
+
...flows
|
|
130
|
+
.filter(flow => includesEvent(flow, 'extTopic'))
|
|
131
|
+
.map(flow => `Process${upperFirst(flow.flowTag)}HdrSqs`)
|
|
52
132
|
]);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
133
|
+
flowExtTopicSteps(flows).forEach(({ flowTag, stepName }) =>
|
|
134
|
+
sqsQueues.add(`${flowTag}${stepName}HdrSqs`)
|
|
135
|
+
);
|
|
136
|
+
flows
|
|
137
|
+
.filter(flow => includesEvent(flow, 's3'))
|
|
138
|
+
.forEach(flow => {
|
|
139
|
+
const tag = upperFirst(flow.flowTag);
|
|
140
|
+
sqsQueues.add(`${tag}CreatePreSignUrlHdrSqs`);
|
|
141
|
+
sqsQueues.add(`${tag}ReservedLimitHdrSqs`);
|
|
142
|
+
sqsQueues.add(`${tag}ConfirmAfterUploadHdrSqs`);
|
|
143
|
+
});
|
|
144
|
+
const dsqQueues = new Set(
|
|
145
|
+
flows
|
|
146
|
+
.filter(flow => includesEvent(flow, 'dsq'))
|
|
147
|
+
.map(flow => `${upperFirst(flow.flowTag)}HdrDsq`)
|
|
148
|
+
);
|
|
149
|
+
flowDsqSteps(flows).forEach(({ flowTag, stepName }) =>
|
|
150
|
+
dsqQueues.add(`${flowTag}${stepName}HdrDsq`)
|
|
151
|
+
);
|
|
66
152
|
if (crud) {
|
|
67
153
|
CRUD_ACTIONS.forEach(action => ownTopics.add(action));
|
|
68
|
-
['Create', 'Update', 'Delete'].forEach(action =>
|
|
154
|
+
['Create', 'Update', 'Delete'].forEach(action =>
|
|
155
|
+
sqsQueues.add(`${action}CompleteHdrSqs`)
|
|
156
|
+
);
|
|
69
157
|
}
|
|
70
158
|
if (relationships.length > 0) {
|
|
71
159
|
RELATIONSHIP_ACTIONS.forEach(action => {
|
|
@@ -79,75 +167,136 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
79
167
|
ownTopics.add(action);
|
|
80
168
|
sqsQueues.add(`Process${action}HdrSqs`);
|
|
81
169
|
});
|
|
82
|
-
objects
|
|
170
|
+
objects
|
|
171
|
+
.filter(hasRbacAddOn)
|
|
172
|
+
.forEach(object =>
|
|
173
|
+
sqsQueues.add(`Process${upperFirst(object.objectType)}RbacFlowHdrInv`)
|
|
174
|
+
);
|
|
83
175
|
}
|
|
84
176
|
const beforeLogicalByAction = new Map();
|
|
85
|
-
for (const flow of flows.filter(
|
|
177
|
+
for (const flow of flows.filter(
|
|
178
|
+
flow => flow?.generatedCodeConfig?.codeHookTag === 'beforeLogical'
|
|
179
|
+
)) {
|
|
86
180
|
const action = upperFirst(flow.generatedCodeConfig.generatedCodeTag);
|
|
87
181
|
if (!action || !flow.flowTag) continue;
|
|
88
182
|
const flowTags = beforeLogicalByAction.get(action) ?? new Set();
|
|
89
183
|
flowTags.add(flow.flowTag);
|
|
90
184
|
beforeLogicalByAction.set(action, flowTags);
|
|
91
185
|
}
|
|
92
|
-
const beforeLogical = [...beforeLogicalByAction].map(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
...
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
186
|
+
const beforeLogical = [...beforeLogicalByAction].map(
|
|
187
|
+
([action, flowTags]) => ({ action, flowTags: [...flowTags].sort() })
|
|
188
|
+
);
|
|
189
|
+
const customRoles = [
|
|
190
|
+
...new Set([
|
|
191
|
+
...supportedCustomFlows(flows).map(flow =>
|
|
192
|
+
customFlowRoleName(flow.flowTag)
|
|
193
|
+
),
|
|
194
|
+
...generatedOwnTopicFlows(flows).map(flow =>
|
|
195
|
+
customFlowRoleName(flow.flowTag)
|
|
196
|
+
),
|
|
197
|
+
...flowExtTopicSteps(flows).map(({ flow }) =>
|
|
198
|
+
customFlowRoleName(flow.flowTag)
|
|
199
|
+
),
|
|
200
|
+
...flowDsqSteps(flows).map(({ flow }) =>
|
|
201
|
+
customFlowRoleName(flow.flowTag)
|
|
202
|
+
)
|
|
203
|
+
])
|
|
204
|
+
];
|
|
205
|
+
const websocket = hasSystemWebSocket(flows, {
|
|
206
|
+
includeRelationshipOwnTopic: true
|
|
207
|
+
});
|
|
100
208
|
const webSocketRoles = new Set(websocket ? ['WebSocketMainRole'] : []);
|
|
101
209
|
if (crud) webSocketRoles.add('FlowObjectsRole');
|
|
102
210
|
if (relationships.length > 0) webSocketRoles.add('FlowRelationshipRole');
|
|
103
211
|
if (rbac) webSocketRoles.add('FlowRbacRole');
|
|
104
212
|
[...supportedCustomFlows(flows), ...generatedOwnTopicFlows(flows)]
|
|
105
|
-
.filter(
|
|
213
|
+
.filter(
|
|
214
|
+
flow => includesEvent(flow, 'ownTopic') || includesEvent(flow, 's3')
|
|
215
|
+
)
|
|
106
216
|
.forEach(flow => webSocketRoles.add(customFlowRoleName(flow.flowTag)));
|
|
107
217
|
const outTopics = new Set([
|
|
108
218
|
...ownTopics,
|
|
109
219
|
...beforeLogical.map(({ action }) => action),
|
|
110
|
-
...flows
|
|
220
|
+
...flows
|
|
221
|
+
.filter(flow => flow?.outputTopic && upperFirst(flow.flowTag))
|
|
222
|
+
.map(flow => upperFirst(flow.flowTag))
|
|
111
223
|
]);
|
|
112
224
|
const objectTableNames = [...collectTables(objects).keys()];
|
|
113
225
|
const relationshipTableNames = [...collectTables(relationships).keys()];
|
|
114
|
-
const tableMap = new Map([
|
|
226
|
+
const tableMap = new Map([
|
|
227
|
+
...collectTables(objects),
|
|
228
|
+
...collectTables(relationships)
|
|
229
|
+
]);
|
|
115
230
|
return {
|
|
116
231
|
crud,
|
|
117
232
|
rbac,
|
|
118
233
|
relationships: relationships.length > 0,
|
|
119
234
|
ownTopics: [...ownTopics].filter(Boolean).sort(),
|
|
120
235
|
sqsQueues: [...sqsQueues].filter(Boolean).sort(),
|
|
121
|
-
triggerCacheQueues,
|
|
122
236
|
dsqQueues: [...dsqQueues].filter(Boolean).sort(),
|
|
123
237
|
beforeLogical,
|
|
124
238
|
customRoles,
|
|
125
239
|
websocket,
|
|
126
240
|
webSocketRoles,
|
|
127
241
|
webSocketSubscriptions: websocket
|
|
128
|
-
? [...outTopics]
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
242
|
+
? [...outTopics]
|
|
243
|
+
.filter(
|
|
244
|
+
topic =>
|
|
245
|
+
flows.some(
|
|
246
|
+
flow => (flow?.outputTopic || (includesEvent(flow, 'ownTopic') && flow.initiateBy === 'user')) && upperFirst(flow.flowTag) === topic
|
|
247
|
+
) ||
|
|
248
|
+
(relationships.length > 0 &&
|
|
249
|
+
topic !== 'GetRelationship' &&
|
|
250
|
+
RELATIONSHIP_ACTIONS.includes(topic))
|
|
251
|
+
)
|
|
252
|
+
.sort()
|
|
132
253
|
: [],
|
|
133
254
|
outTopics: [...outTopics].filter(Boolean).sort(),
|
|
134
|
-
tables: [...tableMap.entries()].map(([tableName, keys]) => ({
|
|
255
|
+
tables: [...tableMap.entries()].map(([tableName, keys]) => ({
|
|
256
|
+
tableName,
|
|
257
|
+
...keys
|
|
258
|
+
})),
|
|
135
259
|
allTableNames: [...new Set([...SYSTEM_TABLES, ...tableMap.keys()])].sort(),
|
|
136
260
|
objectTableNames,
|
|
137
261
|
relationshipTableNames,
|
|
138
|
-
objectTablesByType: new Map(
|
|
262
|
+
objectTablesByType: new Map(
|
|
263
|
+
objects
|
|
264
|
+
.map(object => [
|
|
265
|
+
object.objectType,
|
|
266
|
+
Object.values(object.storageResources ?? {}).find(
|
|
267
|
+
resource => resource?.storageType === 'dynamoDB'
|
|
268
|
+
)?.tableName
|
|
269
|
+
])
|
|
270
|
+
.filter(([, tableName]) => tableName)
|
|
271
|
+
),
|
|
139
272
|
flows
|
|
140
273
|
};
|
|
141
274
|
}
|
|
142
275
|
|
|
143
276
|
async function renderInSqs(state) {
|
|
144
277
|
const chunks = await Promise.all([
|
|
145
|
-
...state.ownTopics.map(queueName =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
...state.
|
|
149
|
-
|
|
150
|
-
|
|
278
|
+
...state.ownTopics.map(queueName =>
|
|
279
|
+
render('ResourceYaml.ejs', { queueName })
|
|
280
|
+
),
|
|
281
|
+
...state.sqsQueues.map(queueName =>
|
|
282
|
+
render('ResourceSqsYaml.ejs', { queueName })
|
|
283
|
+
),
|
|
284
|
+
...state.dsqQueues.map(queueName =>
|
|
285
|
+
render('ResourceSqsYaml.ejs', { queueName })
|
|
286
|
+
),
|
|
287
|
+
...state.beforeLogical.map(({ action, flowTags }) =>
|
|
288
|
+
render('ResourceBeforeLogicalYaml.ejs', {
|
|
289
|
+
queueName: `${action}BeforeLogical`,
|
|
290
|
+
flowTags
|
|
291
|
+
})
|
|
292
|
+
),
|
|
293
|
+
...(state.websocket
|
|
294
|
+
? [
|
|
295
|
+
render('ResourceSqsYaml.ejs', {
|
|
296
|
+
queueName: 'WebSocketCompleteHdrSqs'
|
|
297
|
+
})
|
|
298
|
+
]
|
|
299
|
+
: [])
|
|
151
300
|
]);
|
|
152
301
|
return `Resources:\n${chunks.join('\n')}\n${state.webSocketSubscriptions.map(renderWebSocketSubscription).join('\n')}`;
|
|
153
302
|
}
|
|
@@ -171,19 +320,46 @@ async function renderIam(state) {
|
|
|
171
320
|
if (state.rbac) roles.push('FlowRbacRole');
|
|
172
321
|
if (state.websocket) roles.push('WebSocketMainRole');
|
|
173
322
|
roles.push(...state.customRoles);
|
|
174
|
-
return render('SharedResource_Yaml.ejs', {
|
|
323
|
+
return render('SharedResource_Yaml.ejs', {
|
|
324
|
+
roles: roles.sort().map(roleName => ({
|
|
325
|
+
roleName,
|
|
326
|
+
statements: roleStatements(state, roleName)
|
|
327
|
+
}))
|
|
328
|
+
});
|
|
175
329
|
}
|
|
176
330
|
|
|
177
331
|
function roleStatements(state, roleName) {
|
|
178
332
|
const statements = [];
|
|
179
333
|
if (roleNeedsSchemaRead(state, roleName)) {
|
|
180
|
-
statements.push({
|
|
334
|
+
statements.push({
|
|
335
|
+
action: ['s3:GetObject'],
|
|
336
|
+
resource: [
|
|
337
|
+
'arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}/perServiceSchemas/*'
|
|
338
|
+
]
|
|
339
|
+
});
|
|
181
340
|
}
|
|
182
|
-
addStatement(
|
|
183
|
-
|
|
184
|
-
|
|
341
|
+
addStatement(
|
|
342
|
+
statements,
|
|
343
|
+
dynamoActionsForRole(roleName),
|
|
344
|
+
dynamoArns(roleTableNames(state, roleName))
|
|
345
|
+
);
|
|
346
|
+
addStatement(
|
|
347
|
+
statements,
|
|
348
|
+
SQS_CONSUMER_ACTIONS,
|
|
349
|
+
queueArns(roleQueueNames(state, roleName))
|
|
350
|
+
);
|
|
351
|
+
addStatement(
|
|
352
|
+
statements,
|
|
353
|
+
['sns:Publish'],
|
|
354
|
+
topicArns(roleTopicNames(state, roleName))
|
|
355
|
+
);
|
|
185
356
|
if (state.webSocketRoles.has(roleName)) {
|
|
186
|
-
statements.push({
|
|
357
|
+
statements.push({
|
|
358
|
+
action: ['execute-api:ManageConnections'],
|
|
359
|
+
resource: [
|
|
360
|
+
'arn:aws:execute-api:${self:custom.iz_region}:${self:custom.iz_accountId}:*/*'
|
|
361
|
+
]
|
|
362
|
+
});
|
|
187
363
|
}
|
|
188
364
|
return statements;
|
|
189
365
|
}
|
|
@@ -199,15 +375,39 @@ function roleNeedsSchemaRead(state, roleName) {
|
|
|
199
375
|
|
|
200
376
|
function roleTableNames(state, roleName) {
|
|
201
377
|
if (roleName === 'RegisterRole') return [];
|
|
202
|
-
if (roleName === 'WebSocketMainRole')
|
|
203
|
-
|
|
204
|
-
if (roleName === '
|
|
378
|
+
if (roleName === 'WebSocketMainRole')
|
|
379
|
+
return ['UserUploadRecords', 'WebSocketTask'];
|
|
380
|
+
if (roleName === 'FlowObjectsRole')
|
|
381
|
+
return [
|
|
382
|
+
...state.objectTableNames,
|
|
383
|
+
'AwaitingMultipleSteps',
|
|
384
|
+
'AwaitingMultipleStepByPending',
|
|
385
|
+
'WebSocketTask'
|
|
386
|
+
];
|
|
387
|
+
if (roleName === 'FlowRelationshipRole')
|
|
388
|
+
return [
|
|
389
|
+
...state.objectTableNames,
|
|
390
|
+
...state.relationshipTableNames,
|
|
391
|
+
'AwaitingMultipleSteps',
|
|
392
|
+
'AwaitingMultipleStepByPending',
|
|
393
|
+
'WebSocketTask'
|
|
394
|
+
];
|
|
205
395
|
if (roleName === 'FlowRbacRole') return RBAC_TABLES;
|
|
206
|
-
if (roleName === 'ProcFindDataRole')
|
|
207
|
-
|
|
396
|
+
if (roleName === 'ProcFindDataRole')
|
|
397
|
+
return [
|
|
398
|
+
...state.objectTableNames,
|
|
399
|
+
...state.relationshipTableNames,
|
|
400
|
+
'FindDataMain',
|
|
401
|
+
'LogicalResultsMain',
|
|
402
|
+
'LogicalResultsData'
|
|
403
|
+
];
|
|
404
|
+
const flow = state.flows.find(
|
|
405
|
+
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
406
|
+
);
|
|
208
407
|
if (!flow) return [];
|
|
209
408
|
const tables = [];
|
|
210
|
-
if (includesEvent(flow, 'ownTopic') || includesEvent(flow, 's3'))
|
|
409
|
+
if (includesEvent(flow, 'ownTopic') || includesEvent(flow, 's3'))
|
|
410
|
+
tables.push('WebSocketTask');
|
|
211
411
|
if (includesEvent(flow, 's3')) tables.push('UserUploadRecords');
|
|
212
412
|
if (flow.statusType === 'storedCache') {
|
|
213
413
|
const object = state.objectTablesByType.get(flow.objType?.objectType);
|
|
@@ -217,56 +417,106 @@ function roleTableNames(state, roleName) {
|
|
|
217
417
|
}
|
|
218
418
|
|
|
219
419
|
function dynamoActionsForRole(roleName) {
|
|
220
|
-
if (roleName === 'ProcFindDataRole')
|
|
221
|
-
|
|
222
|
-
if (roleName === '
|
|
420
|
+
if (roleName === 'ProcFindDataRole')
|
|
421
|
+
return ['dynamodb:GetItem', 'dynamodb:Query'];
|
|
422
|
+
if (roleName === 'WebSocketMainRole')
|
|
423
|
+
return ['dynamodb:DeleteItem', 'dynamodb:PutItem', 'dynamodb:Query'];
|
|
424
|
+
if (roleName === 'FlowRbacRole')
|
|
425
|
+
return [...DYNAMO_ACTIONS, 'dynamodb:TransactWriteItems'];
|
|
223
426
|
return DYNAMO_ACTIONS;
|
|
224
427
|
}
|
|
225
428
|
|
|
226
429
|
function roleQueueNames(state, roleName) {
|
|
227
430
|
if (roleName === 'ProcFindDataRole') return LEGACY_PROCESS_QUEUES;
|
|
228
|
-
if (roleName === 'WebSocketMainRole')
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
431
|
+
if (roleName === 'WebSocketMainRole')
|
|
432
|
+
return state.websocket ? ['WebSocketCompleteHdrSqs'] : [];
|
|
433
|
+
if (roleName === 'FlowObjectsRole')
|
|
434
|
+
return [
|
|
435
|
+
...CRUD_ACTIONS.map(action => `${action}HdrSqs`),
|
|
436
|
+
...CRUD_ACTIONS.filter(action => action !== 'Get').map(
|
|
437
|
+
action => `${action}CompleteHdrSqs`
|
|
438
|
+
),
|
|
439
|
+
...state.beforeLogical
|
|
440
|
+
.filter(({ action }) => CRUD_ACTIONS.includes(action))
|
|
441
|
+
.map(({ action }) => `${action}BeforeLogicalHdrSqs`)
|
|
442
|
+
];
|
|
443
|
+
if (roleName === 'FlowRelationshipRole')
|
|
444
|
+
return [
|
|
445
|
+
...RELATIONSHIP_ACTIONS.flatMap(action => [
|
|
446
|
+
`${action}HdrSqs`,
|
|
447
|
+
`${action}CompleteHdrSqs`
|
|
448
|
+
]),
|
|
449
|
+
'GetRelationshipHdrDsq'
|
|
450
|
+
];
|
|
451
|
+
if (roleName === 'FlowRbacRole')
|
|
452
|
+
return RBAC_ACTIONS.flatMap(action => [
|
|
453
|
+
`${action}HdrSqs`,
|
|
454
|
+
`Process${action}HdrSqs`
|
|
455
|
+
]);
|
|
456
|
+
const flow = state.flows.find(
|
|
457
|
+
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
458
|
+
);
|
|
240
459
|
if (!flow) return [];
|
|
241
460
|
const tag = upperFirst(flow.flowTag);
|
|
242
461
|
return [
|
|
243
|
-
...(includesEvent(flow, 'extTopic') ? [
|
|
462
|
+
...(includesEvent(flow, 'extTopic') ? [`${tag}HdrSqs`] : []),
|
|
244
463
|
...(includesEvent(flow, 'ownTopic') ? [`${tag}HdrSqs`] : []),
|
|
245
|
-
...
|
|
246
|
-
...
|
|
247
|
-
|
|
464
|
+
...(includesEvent(flow, 'dsq') ? [`${tag}HdrDsq`] : []),
|
|
465
|
+
...flowExtTopicSteps([flow]).map(
|
|
466
|
+
({ flowTag, stepName }) => `${flowTag}${stepName}HdrSqs`
|
|
467
|
+
),
|
|
468
|
+
...flowDsqSteps([flow]).map(
|
|
469
|
+
({ flowTag, stepName }) => `${flowTag}${stepName}HdrDsq`
|
|
470
|
+
),
|
|
471
|
+
...state.sqsQueues.filter(
|
|
472
|
+
queueName => includesEvent(flow, 's3') && queueName.startsWith(tag)
|
|
473
|
+
)
|
|
248
474
|
];
|
|
249
475
|
}
|
|
250
476
|
|
|
251
477
|
function roleTopicNames(state, roleName) {
|
|
252
478
|
if (roleName === 'ProcFindDataRole') return [];
|
|
253
|
-
if (roleName === 'FlowObjectsRole')
|
|
254
|
-
|
|
255
|
-
if (roleName === '
|
|
256
|
-
|
|
257
|
-
|
|
479
|
+
if (roleName === 'FlowObjectsRole')
|
|
480
|
+
return state.outTopics.filter(topic => CRUD_ACTIONS.includes(topic));
|
|
481
|
+
if (roleName === 'FlowRelationshipRole')
|
|
482
|
+
return state.outTopics.filter(topic =>
|
|
483
|
+
RELATIONSHIP_ACTIONS.includes(topic)
|
|
484
|
+
);
|
|
485
|
+
if (roleName === 'FlowRbacRole')
|
|
486
|
+
return state.outTopics.filter(topic => RBAC_ACTIONS.includes(topic));
|
|
487
|
+
const flow = state.flows.find(
|
|
488
|
+
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
489
|
+
);
|
|
490
|
+
return flow
|
|
491
|
+
? state.outTopics.filter(topic => topic === upperFirst(flow.flowTag))
|
|
492
|
+
: [];
|
|
258
493
|
}
|
|
259
494
|
|
|
260
495
|
function dynamoArns(tableNames) {
|
|
261
|
-
return [...new Set(tableNames)]
|
|
496
|
+
return [...new Set(tableNames)]
|
|
497
|
+
.sort()
|
|
498
|
+
.map(
|
|
499
|
+
tableName =>
|
|
500
|
+
`arn:aws:dynamodb:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:table/${'${self:custom.iz_resourcePrefix}'}${tableName}`
|
|
501
|
+
);
|
|
262
502
|
}
|
|
263
503
|
|
|
264
504
|
function queueArns(queueNames) {
|
|
265
|
-
return [...new Set(queueNames)]
|
|
505
|
+
return [...new Set(queueNames)]
|
|
506
|
+
.sort()
|
|
507
|
+
.map(
|
|
508
|
+
queueName =>
|
|
509
|
+
`arn:aws:sqs:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_resourcePrefix}'}${queueName}`
|
|
510
|
+
);
|
|
266
511
|
}
|
|
267
512
|
|
|
268
513
|
function topicArns(topicNames) {
|
|
269
|
-
return [...new Set(topicNames)]
|
|
514
|
+
return [...new Set(topicNames)]
|
|
515
|
+
.sort()
|
|
516
|
+
.map(
|
|
517
|
+
topic =>
|
|
518
|
+
`arn:aws:sns:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_serviceTag}'}_${'${self:custom.iz_stage}'}_${topic}_Out`
|
|
519
|
+
);
|
|
270
520
|
}
|
|
271
521
|
|
|
272
522
|
function renderWebSocketSubscription(topicName) {
|
|
@@ -277,11 +527,27 @@ function collectTables(schemas) {
|
|
|
277
527
|
const tables = new Map();
|
|
278
528
|
for (const schema of schemas) {
|
|
279
529
|
if (!schema?.storageResources) continue;
|
|
280
|
-
const identifiers = Array.isArray(schema.identifiers)
|
|
281
|
-
|
|
282
|
-
|
|
530
|
+
const identifiers = Array.isArray(schema.identifiers)
|
|
531
|
+
? schema.identifiers
|
|
532
|
+
: [];
|
|
533
|
+
const pk =
|
|
534
|
+
keyName(
|
|
535
|
+
identifiers.find(
|
|
536
|
+
identifier =>
|
|
537
|
+
identifier.type === 'partitionKey' ||
|
|
538
|
+
identifier.type === 'identifier'
|
|
539
|
+
)
|
|
540
|
+
) ?? 'PK';
|
|
541
|
+
const sk = keyName(
|
|
542
|
+
identifiers.find(identifier => identifier.type === 'sortKey')
|
|
543
|
+
);
|
|
283
544
|
for (const resource of Object.values(schema.storageResources)) {
|
|
284
|
-
if (
|
|
545
|
+
if (
|
|
546
|
+
resource?.storageType === 'dynamoDB' &&
|
|
547
|
+
resource.tableName &&
|
|
548
|
+
!tables.has(resource.tableName)
|
|
549
|
+
)
|
|
550
|
+
tables.set(resource.tableName, { pk, sk });
|
|
285
551
|
}
|
|
286
552
|
}
|
|
287
553
|
return tables;
|
|
@@ -289,12 +555,41 @@ function collectTables(schemas) {
|
|
|
289
555
|
|
|
290
556
|
function keyName(identifier) {
|
|
291
557
|
if (!identifier) return undefined;
|
|
292
|
-
return
|
|
558
|
+
return (
|
|
559
|
+
identifier.name ??
|
|
560
|
+
identifier.fieldName ??
|
|
561
|
+
(Array.isArray(identifier.fieldNames)
|
|
562
|
+
? identifier.fieldNames.join(identifier.deliminator ?? '_')
|
|
563
|
+
: undefined)
|
|
564
|
+
);
|
|
293
565
|
}
|
|
294
566
|
|
|
295
|
-
function isCrudObject(object) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
567
|
+
function isCrudObject(object) {
|
|
568
|
+
return (
|
|
569
|
+
object?.generatedBy === 'userGenerated' ||
|
|
570
|
+
object?.generatedBy === 'systemGenerated'
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
function hasRbacAddOn(object) {
|
|
574
|
+
return (
|
|
575
|
+
Array.isArray(object?.addOnDataStructure) &&
|
|
576
|
+
object.addOnDataStructure.some(addOn => addOn?.type === 'rbac')
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
function includesEvent(flow, event) {
|
|
580
|
+
return Array.isArray(flow?.event) && flow.event.includes(event);
|
|
581
|
+
}
|
|
582
|
+
function upperFirst(value) {
|
|
583
|
+
return typeof value === 'string' && value
|
|
584
|
+
? value[0].toUpperCase() + value.slice(1)
|
|
585
|
+
: '';
|
|
586
|
+
}
|
|
587
|
+
function normalizeYaml(content) {
|
|
588
|
+
return `${content.trimEnd()}\n`;
|
|
589
|
+
}
|
|
590
|
+
async function render(templateName, data) {
|
|
591
|
+
return ejs.render(
|
|
592
|
+
await readFile(new URL(templateName, TEMPLATE_URL), 'utf8'),
|
|
593
|
+
{ resourceQueueName: data.queueName, ...data }
|
|
594
|
+
);
|
|
595
|
+
}
|
|
@@ -25,7 +25,8 @@ export function systemWebSocketManagedPaths() {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function hasSystemWebSocket(flows, { includeRelationshipOwnTopic = false } = {}) {
|
|
28
|
-
return
|
|
28
|
+
// return true;
|
|
29
|
+
return Array.isArray(flows) && flows.some(flow => Array.isArray(flow?.event) && (flow.event.includes('s3') || (includeRelationshipOwnTopic && flow.event.includes('ownTopic') && flow.initiateBy === 'user')));
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function renderWebSocketConnectYaml() {
|