@izara_project/izara-core-generate-service-code 1.0.67 → 1.0.69
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/.prettierrc +11 -0
- package/package.json +16 -12
- package/scripts/format-ejs.js +104 -0
- package/src/compile/compileProject.js +40 -9
- package/src/compile/loadSchemas.js +120 -11
- package/src/emit/crud/emitCrudApplicationArtifacts.js +188 -41
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +34 -18
- package/src/emit/crud/generateCrudApplicationArtifacts.js +14 -6
- package/src/emit/crud/generateDynamicCrudFlowSchemas.js +4 -1
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +296 -105
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +222 -144
- package/src/emit/crud/templates/FlowEndpointBeforeLogical_Main.ejs +22 -6
- package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +134 -43
- package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +51 -31
- package/src/emit/crud/templates/FlowHandler_HdrApi.ejs +53 -45
- package/src/emit/crud/templates/FlowHandler_HdrInv.ejs +28 -26
- package/src/emit/crud/templates/FlowHandler_HdrSqs.ejs +15 -11
- package/src/emit/crud/templates/FlowMain_Wbs.ejs +53 -38
- package/src/emit/crud/templates/GetEndpoint_Main.ejs +6 -3
- package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +2 -2
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +402 -249
- package/src/emit/custom/customFlows.js +68 -28
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +520 -123
- package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +28 -8
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +48 -36
- package/src/emit/custom/templates/FlowStep_Main.ejs +7 -9
- package/src/emit/custom/templates/s3/ConfirmReserved_HdrSqs.ejs +59 -54
- package/src/emit/custom/templates/s3/ConfirmReserved_Main.ejs +33 -23
- package/src/emit/custom/templates/s3/CreateObject_HdrWbs.ejs +30 -23
- package/src/emit/custom/templates/s3/CreateObject_Main.ejs +56 -39
- package/src/emit/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +56 -51
- package/src/emit/custom/templates/s3/CreatePreSignUrl_Main.ejs +112 -75
- package/src/emit/custom/templates/s3/FlowHandler_HdrS3.ejs +14 -9
- package/src/emit/custom/templates/s3/ProcessS3File_Main.ejs +11 -8
- package/src/emit/custom/templates/s3/ReservedLimit_HdrSqs.ejs +65 -56
- package/src/emit/custom/templates/s3/ReservedLimit_Main.ejs +74 -52
- package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +58 -14
- package/src/emit/custom/templates/triggerCache/CheckTriggerCache_Main.ejs +12 -17
- package/src/emit/custom/templates/triggerCache/TriggerCacheComplete_Main.ejs +20 -13
- package/src/emit/custom/templates/triggerCache/TriggerCache_Main.ejs +4 -1
- package/src/emit/function/functionYamlArtifact.js +272 -79
- package/src/emit/function/templates/ApiFunction_Yaml.ejs +7 -0
- package/src/emit/function/templates/EventBridgeFunction_Yaml.ejs +6 -0
- package/src/emit/function/templates/InvFunction_Yaml.ejs +5 -0
- package/src/emit/function/templates/S3Function_Yaml.ejs +16 -0
- package/src/emit/function/templates/SqsFunction_Yaml.ejs +10 -0
- package/src/emit/function/templates/WbsFunction_Yaml.ejs +10 -0
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +40 -29
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +171 -72
- package/src/emit/rbac/generateCreateTargetRoleHandler.js +6 -4
- package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +8 -2
- package/src/emit/rbac/rbacFlowTag.js +3 -1
- package/src/emit/rbac/templates/CreateTargetRole_HdrSqs.ejs +41 -18
- package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +23 -3
- package/src/emit/rbac/templates/ListRbac_HdrApi.ejs +27 -5
- package/src/emit/rbac/templates/ListRbac_HdrSqs.ejs +41 -8
- package/src/emit/rbac/templates/ListRbac_Main.ejs +26 -6
- package/src/emit/rbac/templates/RbacMutation_Main.ejs +23 -3
- package/src/emit/rbac/templates/UserRbacFlow_HdrSqs.ejs +46 -0
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +152 -33
- package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +24 -4
- package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +26 -11
- package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +179 -37
- package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +8 -2
- package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +15 -6
- package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +64 -37
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +258 -142
- package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +68 -43
- package/src/emit/relationship/templates/CreateRelationship_Main.ejs +315 -171
- package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +67 -41
- package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +226 -115
- package/src/emit/relationship/templates/FlowHandler_HdrApi.ejs +41 -33
- package/src/emit/relationship/templates/FlowHandler_HdrDsq.ejs +15 -11
- package/src/emit/relationship/templates/FlowHandler_HdrInv.ejs +28 -26
- package/src/emit/relationship/templates/FlowHandler_HdrSqs.ejs +15 -11
- package/src/emit/relationship/templates/GetRelationshipComplete_Main.ejs +46 -26
- package/src/emit/relationship/templates/GetRelationship_Main.ejs +233 -126
- package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +72 -44
- package/src/emit/relationship/templates/MoveRelationship_Main.ejs +266 -150
- package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +74 -45
- package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +285 -152
- package/src/emit/resource/emitResourceArtifacts.js +274 -105
- package/src/emit/resource/generateResourceArtifacts.js +15 -6
- package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +139 -0
- package/src/emit/resource/templates/S3Bucket_Yaml.ejs +19 -0
- package/src/emit/resource/templates/S3Subscription_Yaml.ejs +7 -0
- package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
- package/src/emit/system/emitSystemWebSocketArtifacts.js +61 -165
- package/src/emit/system/templates/WebSocketComplete_HdrSqs.ejs +32 -0
- package/src/emit/system/templates/WebSocketComplete_Main.ejs +46 -0
- package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +19 -0
- package/src/emit/system/templates/WebSocketConnect.ejs +25 -0
- package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +11 -0
- package/src/generate.js +141 -34
- package/src/generateIntTest.js +76 -28
- package/src/generateResourceIntTest.js +35 -12
- package/src/generateResources/IntTest/generateResources/generateResources.js +5 -2
- package/src/generateResources/IntTest/generateResources/templateResources.ejs +18 -30
- package/src/generateResources/IntTest/generateTests/events/events.js +1 -1
- package/src/generateResources/IntTest/generateTests/events/templateEvents.ejs +13 -10
- package/src/generateResources/IntTest/generateTests/generateTemplate.js +2 -2
- package/src/generateResources/IntTest/generateTests/pathIntTest/pathIntTest.js +2 -1
- package/src/generateResources/IntTest/generateTests/pathIntTest/template.ejs +1 -3
- package/src/generateResources/IntTest/generateTests/tests/templateTests.ejs +1 -1
- package/src/generateResources/IntTest/generateTests/tests/tests.js +7 -3
- package/src/generateResources/IntTest/libs/{libs.js → IntTestGenerateLib.js} +34 -23
- package/src/generateResources/IntTest/upload/uploadIntTest.js +33 -19
- package/src/intTestGeneratorShared.js +15 -10
- package/src/output/commitArtifacts.js +99 -39
- package/src/output/mergeHooks.js +24 -10
- package/src/output/validateArtifacts.js +37 -27
- package/src/plan/buildGenerationPlan.js +24 -13
- package/test/core.test.js +116 -49
- package/test/createTargetRoleHandler.test.js +63 -14
- package/test/crudApplicationArtifacts.test.js +128 -23
- package/test/customFlowApplicationArtifacts.test.js +391 -84
- package/test/dynamicCrudFlowSchemas.test.js +74 -26
- package/test/dynamicRbacFlowSchemas.test.js +63 -20
- package/test/dynamicRelationshipFlowSchemas.test.js +65 -18
- package/test/functionYamlArtifact.test.js +67 -10
- package/test/intTestApi.test.js +9 -10
- package/test/publicApi.test.js +184 -44
- package/test/relationshipApplicationArtifacts.test.js +90 -19
- package/test/resourceArtifacts.test.js +183 -33
- package/docs/test.png +0 -0
- package/src/emit/rbac/templates/UserRbacFlow_HdrInv.ejs +0 -10
- package/src/emit/relationship/dynamicRelationshipFlowSchemaTemplate.js +0 -11
- /package/docs/{test.mmd → TriggeredCacheFlow.mmd} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ejsLib from 'ejs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -13,6 +13,7 @@ import { hasSystemWebSocket } from '../system/emitSystemWebSocketArtifacts.js';
|
|
|
13
13
|
const TEMPLATE_URL = new URL('./templates/', import.meta.url);
|
|
14
14
|
const RESOURCE_ROOT = 'resource/sls_yaml/generatedCode/source';
|
|
15
15
|
const APP_ROOT = 'app/sls_yaml/generatedCode/source';
|
|
16
|
+
const INITIAL_SETUP_ROOT = 'app/initial_setup/generatedCode/source';
|
|
16
17
|
const CRUD_ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
|
|
17
18
|
const RELATIONSHIP_ACTIONS = [
|
|
18
19
|
'CreateRelationship',
|
|
@@ -83,13 +84,15 @@ export async function emitResourceArtifacts({
|
|
|
83
84
|
throw new TypeError('objects, relationships, and flows must be arrays');
|
|
84
85
|
}
|
|
85
86
|
const state = createResourceState({ objects, relationships, flows });
|
|
86
|
-
const [inSqs, out, dynamo, iam] = await Promise.all([
|
|
87
|
+
const [inSqs, out, dynamo, iam, lambdaRoleConfig, s3] = await Promise.all([
|
|
87
88
|
renderInSqs(state),
|
|
88
89
|
renderOut(state),
|
|
89
90
|
renderDynamo(state),
|
|
90
|
-
renderIam(state)
|
|
91
|
+
renderIam(state),
|
|
92
|
+
renderLambdaRoleConfig(state),
|
|
93
|
+
renderS3(state)
|
|
91
94
|
]);
|
|
92
|
-
|
|
95
|
+
const artifacts = [
|
|
93
96
|
{
|
|
94
97
|
path: `${RESOURCE_ROOT}/generated-sns-in-sqs.yml`,
|
|
95
98
|
content: normalizeYaml(inSqs)
|
|
@@ -105,15 +108,28 @@ export async function emitResourceArtifacts({
|
|
|
105
108
|
{
|
|
106
109
|
path: `${APP_ROOT}/generate-shared-resource.yml`,
|
|
107
110
|
content: normalizeYaml(iam)
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
path: `${INITIAL_SETUP_ROOT}/generatedLambdaRoleServiceConfig.js`,
|
|
114
|
+
content: lambdaRoleConfig
|
|
108
115
|
}
|
|
109
116
|
];
|
|
117
|
+
if (state.s3Buckets.length > 0) {
|
|
118
|
+
artifacts.push({
|
|
119
|
+
path: `${RESOURCE_ROOT}/generated-s3.yml`,
|
|
120
|
+
content: normalizeYaml(s3)
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return artifacts;
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
export const resourceArtifactPaths = [
|
|
113
127
|
`${RESOURCE_ROOT}/generated-sns-in-sqs.yml`,
|
|
114
128
|
`${RESOURCE_ROOT}/generated-sns-out.yml`,
|
|
115
129
|
`${RESOURCE_ROOT}/generated-dynamoDB-table.yml`,
|
|
116
|
-
`${
|
|
130
|
+
`${RESOURCE_ROOT}/generated-s3.yml`,
|
|
131
|
+
`${APP_ROOT}/generate-shared-resource.yml`,
|
|
132
|
+
`${INITIAL_SETUP_ROOT}/generatedLambdaRoleServiceConfig.js`
|
|
117
133
|
];
|
|
118
134
|
|
|
119
135
|
function createResourceState({ objects, relationships, flows }) {
|
|
@@ -121,61 +137,60 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
121
137
|
const rbac = objects.some(hasRbacAddOn);
|
|
122
138
|
const ownTopics = new Set(
|
|
123
139
|
flows
|
|
124
|
-
.filter(flow => includesEvent(flow, 'ownTopic'))
|
|
125
|
-
.map(flow => upperFirst(flow.flowTag))
|
|
140
|
+
.filter((flow) => includesEvent(flow, 'ownTopic'))
|
|
141
|
+
.map((flow) => upperFirst(flow.flowTag))
|
|
126
142
|
);
|
|
127
143
|
const sqsQueues = new Set([
|
|
128
144
|
...LEGACY_PROCESS_QUEUES,
|
|
129
145
|
...flows
|
|
130
|
-
.filter(flow => includesEvent(flow, 'extTopic'))
|
|
131
|
-
.map(flow => `Process${upperFirst(flow.flowTag)}HdrSqs`)
|
|
146
|
+
.filter((flow) => includesEvent(flow, 'extTopic'))
|
|
147
|
+
.map((flow) => `Process${upperFirst(flow.flowTag)}HdrSqs`)
|
|
132
148
|
]);
|
|
133
|
-
|
|
134
|
-
sqsQueues.add(`${flowTag}${stepName}HdrSqs`)
|
|
135
|
-
|
|
136
|
-
flows
|
|
137
|
-
|
|
138
|
-
.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
sqsQueues.add(`${tag}ConfirmAfterUploadHdrSqs`);
|
|
143
|
-
});
|
|
149
|
+
for (const { flowTag, stepName } of flowExtTopicSteps(flows)) {
|
|
150
|
+
sqsQueues.add(`${flowTag}${stepName}HdrSqs`);
|
|
151
|
+
}
|
|
152
|
+
for (const flow of flows.filter((flow) => includesEvent(flow, 's3'))) {
|
|
153
|
+
const tag = upperFirst(flow.flowTag);
|
|
154
|
+
sqsQueues.add(`${tag}CreatePreSignUrlHdrSqs`);
|
|
155
|
+
sqsQueues.add(`${tag}ReservedLimitHdrSqs`);
|
|
156
|
+
sqsQueues.add(`${tag}ConfirmAfterUploadHdrSqs`);
|
|
157
|
+
}
|
|
144
158
|
const dsqQueues = new Set(
|
|
145
159
|
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`)
|
|
160
|
+
.filter((flow) => includesEvent(flow, 'dsq'))
|
|
161
|
+
.map((flow) => `${upperFirst(flow.flowTag)}HdrDsq`)
|
|
151
162
|
);
|
|
163
|
+
for (const { flowTag, stepName } of flowDsqSteps(flows)) {
|
|
164
|
+
dsqQueues.add(`${flowTag}${stepName}HdrDsq`);
|
|
165
|
+
}
|
|
152
166
|
if (crud) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
167
|
+
for (const action of CRUD_ACTIONS) {
|
|
168
|
+
ownTopics.add(action);
|
|
169
|
+
}
|
|
170
|
+
for (const action of ['Create', 'Update', 'Delete']) {
|
|
171
|
+
sqsQueues.add(`${action}CompleteHdrSqs`);
|
|
172
|
+
}
|
|
157
173
|
}
|
|
158
174
|
if (relationships.length > 0) {
|
|
159
|
-
|
|
175
|
+
for (const action of RELATIONSHIP_ACTIONS) {
|
|
160
176
|
ownTopics.add(action);
|
|
161
177
|
sqsQueues.add(`${action}CompleteHdrSqs`);
|
|
162
|
-
}
|
|
178
|
+
}
|
|
163
179
|
dsqQueues.add('GetRelationshipHdrDsq');
|
|
164
180
|
}
|
|
165
181
|
if (rbac) {
|
|
166
|
-
|
|
182
|
+
for (const action of RBAC_ACTIONS) {
|
|
167
183
|
ownTopics.add(action);
|
|
168
184
|
sqsQueues.add(`Process${action}HdrSqs`);
|
|
169
|
-
}
|
|
170
|
-
objects
|
|
171
|
-
.
|
|
172
|
-
.
|
|
173
|
-
|
|
174
|
-
);
|
|
185
|
+
}
|
|
186
|
+
for (const object of objects.filter(hasRbacAddOn)) {
|
|
187
|
+
const flowTag = upperFirst(object.objectType) + 'RbacFlow';
|
|
188
|
+
ownTopics.add(flowTag);
|
|
189
|
+
}
|
|
175
190
|
}
|
|
176
191
|
const beforeLogicalByAction = new Map();
|
|
177
192
|
for (const flow of flows.filter(
|
|
178
|
-
flow => flow?.generatedCodeConfig?.codeHookTag === 'beforeLogical'
|
|
193
|
+
(flow) => flow?.generatedCodeConfig?.codeHookTag === 'beforeLogical'
|
|
179
194
|
)) {
|
|
180
195
|
const action = upperFirst(flow.generatedCodeConfig.generatedCodeTag);
|
|
181
196
|
if (!action || !flow.flowTag) continue;
|
|
@@ -188,38 +203,43 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
188
203
|
);
|
|
189
204
|
const customRoles = [
|
|
190
205
|
...new Set([
|
|
191
|
-
...supportedCustomFlows(flows).map(flow =>
|
|
206
|
+
...supportedCustomFlows(flows).map((flow) =>
|
|
192
207
|
customFlowRoleName(flow.flowTag)
|
|
193
208
|
),
|
|
194
|
-
...generatedOwnTopicFlows(flows).map(flow =>
|
|
209
|
+
...generatedOwnTopicFlows(flows).map((flow) =>
|
|
195
210
|
customFlowRoleName(flow.flowTag)
|
|
196
211
|
),
|
|
197
212
|
...flowExtTopicSteps(flows).map(({ flow }) =>
|
|
198
213
|
customFlowRoleName(flow.flowTag)
|
|
199
214
|
),
|
|
200
|
-
...flowDsqSteps(flows).map(({ flow }) =>
|
|
201
|
-
customFlowRoleName(flow.flowTag)
|
|
202
|
-
)
|
|
215
|
+
...flowDsqSteps(flows).map(({ flow }) => customFlowRoleName(flow.flowTag))
|
|
203
216
|
])
|
|
204
217
|
];
|
|
205
218
|
const websocket = hasSystemWebSocket(flows, {
|
|
206
219
|
includeRelationshipOwnTopic: true
|
|
207
220
|
});
|
|
208
|
-
const webSocketRoles = new Set(
|
|
221
|
+
const webSocketRoles = new Set();
|
|
222
|
+
if (websocket) {
|
|
223
|
+
webSocketRoles.add('WebSocketMainRole');
|
|
224
|
+
}
|
|
209
225
|
if (crud) webSocketRoles.add('FlowObjectsRole');
|
|
210
226
|
if (relationships.length > 0) webSocketRoles.add('FlowRelationshipRole');
|
|
211
227
|
if (rbac) webSocketRoles.add('FlowRbacRole');
|
|
212
|
-
[
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
const filteredWsFlows = [
|
|
229
|
+
...supportedCustomFlows(flows),
|
|
230
|
+
...generatedOwnTopicFlows(flows)
|
|
231
|
+
].filter(
|
|
232
|
+
(flow) => includesEvent(flow, 'ownTopic') || includesEvent(flow, 's3')
|
|
233
|
+
);
|
|
234
|
+
for (const flow of filteredWsFlows) {
|
|
235
|
+
webSocketRoles.add(customFlowRoleName(flow.flowTag));
|
|
236
|
+
}
|
|
217
237
|
const outTopics = new Set([
|
|
218
238
|
...ownTopics,
|
|
219
239
|
...beforeLogical.map(({ action }) => action),
|
|
220
240
|
...flows
|
|
221
|
-
.filter(flow => flow?.outputTopic && upperFirst(flow.flowTag))
|
|
222
|
-
.map(flow => upperFirst(flow.flowTag))
|
|
241
|
+
.filter((flow) => flow?.outputTopic && upperFirst(flow.flowTag))
|
|
242
|
+
.map((flow) => upperFirst(flow.flowTag))
|
|
223
243
|
]);
|
|
224
244
|
const objectTableNames = [...collectTables(objects).keys()];
|
|
225
245
|
const relationshipTableNames = [...collectTables(relationships).keys()];
|
|
@@ -227,9 +247,37 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
227
247
|
...collectTables(objects),
|
|
228
248
|
...collectTables(relationships)
|
|
229
249
|
]);
|
|
250
|
+
const externalDynamoTables = objects.flatMap(
|
|
251
|
+
(object) => object.externalDynamoResources ?? []
|
|
252
|
+
);
|
|
253
|
+
const s3Buckets = flows
|
|
254
|
+
.filter((flow) => includesEvent(flow, 's3') && flow.createBucket === true)
|
|
255
|
+
.map((flow) => flow.bucketName)
|
|
256
|
+
.filter(Boolean);
|
|
257
|
+
const s3FlowTags = flows
|
|
258
|
+
.filter((flow) => includesEvent(flow, 's3'))
|
|
259
|
+
.map((flow) => upperFirst(flow.flowTag));
|
|
260
|
+
const s3Subscriptions = flows
|
|
261
|
+
.filter((flow) => includesEvent(flow, 's3'))
|
|
262
|
+
.map((flow) => {
|
|
263
|
+
const tag = upperFirst(flow.flowTag);
|
|
264
|
+
const topicName = flow.outputTopic
|
|
265
|
+
? flow.outputTopic.startsWith('Out')
|
|
266
|
+
? flow.outputTopic
|
|
267
|
+
: `Out${upperFirst(flow.outputTopic)}`
|
|
268
|
+
: flow.s3Subscription?.topicName || 'OutCreate';
|
|
269
|
+
const queueName =
|
|
270
|
+
flow.s3Subscription?.queueName || `${tag}ReservedLimitHdrSqs`;
|
|
271
|
+
const subscriptionName =
|
|
272
|
+
flow.s3Subscription?.subscriptionName ||
|
|
273
|
+
`Subscription${topicName}To${queueName}`;
|
|
274
|
+
return { subscriptionName, topicName, queueName };
|
|
275
|
+
});
|
|
230
276
|
return {
|
|
231
277
|
crud,
|
|
232
278
|
rbac,
|
|
279
|
+
s3Subscriptions,
|
|
280
|
+
s3FlowTags,
|
|
233
281
|
relationships: relationships.length > 0,
|
|
234
282
|
ownTopics: [...ownTopics].filter(Boolean).sort(),
|
|
235
283
|
sqsQueues: [...sqsQueues].filter(Boolean).sort(),
|
|
@@ -241,9 +289,13 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
241
289
|
webSocketSubscriptions: websocket
|
|
242
290
|
? [...outTopics]
|
|
243
291
|
.filter(
|
|
244
|
-
topic =>
|
|
292
|
+
(topic) =>
|
|
245
293
|
flows.some(
|
|
246
|
-
|
|
294
|
+
(flow) =>
|
|
295
|
+
(flow?.outputTopic ||
|
|
296
|
+
(includesEvent(flow, 'ownTopic') &&
|
|
297
|
+
flow.initiateBy === 'user')) &&
|
|
298
|
+
upperFirst(flow.flowTag) === topic
|
|
247
299
|
) ||
|
|
248
300
|
(relationships.length > 0 &&
|
|
249
301
|
topic !== 'GetRelationship' &&
|
|
@@ -259,29 +311,43 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
259
311
|
allTableNames: [...new Set([...SYSTEM_TABLES, ...tableMap.keys()])].sort(),
|
|
260
312
|
objectTableNames,
|
|
261
313
|
relationshipTableNames,
|
|
314
|
+
externalDynamoTables,
|
|
262
315
|
objectTablesByType: new Map(
|
|
263
316
|
objects
|
|
264
|
-
.map(object => [
|
|
317
|
+
.map((object) => [
|
|
265
318
|
object.objectType,
|
|
266
319
|
Object.values(object.storageResources ?? {}).find(
|
|
267
|
-
resource => resource?.storageType === 'dynamoDB'
|
|
320
|
+
(resource) => resource?.storageType === 'dynamoDB'
|
|
268
321
|
)?.tableName
|
|
269
322
|
])
|
|
270
323
|
.filter(([, tableName]) => tableName)
|
|
271
324
|
),
|
|
325
|
+
s3Buckets,
|
|
272
326
|
flows
|
|
273
327
|
};
|
|
274
328
|
}
|
|
275
329
|
|
|
330
|
+
async function renderS3(state) {
|
|
331
|
+
if (state.s3Buckets.length === 0) return 'Resources:';
|
|
332
|
+
const template = await readFile(
|
|
333
|
+
new URL('S3Bucket_Yaml.ejs', TEMPLATE_URL),
|
|
334
|
+
'utf8'
|
|
335
|
+
);
|
|
336
|
+
const chunks = await Promise.all(
|
|
337
|
+
state.s3Buckets.map((bucketName) => ejsLib.render(template, { bucketName }))
|
|
338
|
+
);
|
|
339
|
+
return `Resources:\n${chunks.join('\n')}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
276
342
|
async function renderInSqs(state) {
|
|
277
343
|
const chunks = await Promise.all([
|
|
278
|
-
...state.ownTopics.map(queueName =>
|
|
344
|
+
...state.ownTopics.map((queueName) =>
|
|
279
345
|
render('ResourceYaml.ejs', { queueName })
|
|
280
346
|
),
|
|
281
|
-
...state.sqsQueues.map(queueName =>
|
|
347
|
+
...state.sqsQueues.map((queueName) =>
|
|
282
348
|
render('ResourceSqsYaml.ejs', { queueName })
|
|
283
349
|
),
|
|
284
|
-
...state.dsqQueues.map(queueName =>
|
|
350
|
+
...state.dsqQueues.map((queueName) =>
|
|
285
351
|
render('ResourceSqsYaml.ejs', { queueName })
|
|
286
352
|
),
|
|
287
353
|
...state.beforeLogical.map(({ action, flowTags }) =>
|
|
@@ -298,17 +364,39 @@ async function renderInSqs(state) {
|
|
|
298
364
|
]
|
|
299
365
|
: [])
|
|
300
366
|
]);
|
|
301
|
-
|
|
367
|
+
const webSocketSubscriptions = await Promise.all(
|
|
368
|
+
state.webSocketSubscriptions.map((topicName) =>
|
|
369
|
+
renderWebSocketSubscription(topicName)
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
const s3Subscriptions = await Promise.all(
|
|
373
|
+
(state.s3Subscriptions ?? []).map(
|
|
374
|
+
({ subscriptionName, topicName, queueName }) =>
|
|
375
|
+
render('S3Subscription_Yaml.ejs', {
|
|
376
|
+
subscriptionName,
|
|
377
|
+
topicName,
|
|
378
|
+
queueName
|
|
379
|
+
})
|
|
380
|
+
)
|
|
381
|
+
);
|
|
382
|
+
const parts = [chunks.join('\n')];
|
|
383
|
+
if (webSocketSubscriptions.length > 0) {
|
|
384
|
+
parts.push(webSocketSubscriptions.join('\n'));
|
|
385
|
+
}
|
|
386
|
+
if (s3Subscriptions.length > 0) {
|
|
387
|
+
parts.push(s3Subscriptions.join('\n'));
|
|
388
|
+
}
|
|
389
|
+
return `Resources:\n${parts.join('\n')}`;
|
|
302
390
|
}
|
|
303
391
|
|
|
304
392
|
async function renderOut(state) {
|
|
305
|
-
return `Resources:\n${(await Promise.all(state.outTopics.map(topicName => render('FlowOut_Yaml.ejs', { topicName })))).join('\n')}`;
|
|
393
|
+
return `Resources:\n${(await Promise.all(state.outTopics.map((topicName) => render('FlowOut_Yaml.ejs', { topicName })))).join('\n')}`;
|
|
306
394
|
}
|
|
307
395
|
|
|
308
396
|
async function renderDynamo(state) {
|
|
309
397
|
const [system, ...tables] = await Promise.all([
|
|
310
398
|
render('SystemDynamoDB_Yaml.ejs', {}),
|
|
311
|
-
...state.tables.map(table => render('DynamoDBTable_Yaml.ejs', table))
|
|
399
|
+
...state.tables.map((table) => render('DynamoDBTable_Yaml.ejs', table))
|
|
312
400
|
]);
|
|
313
401
|
return `${system}\n${tables.join('\n')}`;
|
|
314
402
|
}
|
|
@@ -321,13 +409,26 @@ async function renderIam(state) {
|
|
|
321
409
|
if (state.websocket) roles.push('WebSocketMainRole');
|
|
322
410
|
roles.push(...state.customRoles);
|
|
323
411
|
return render('SharedResource_Yaml.ejs', {
|
|
324
|
-
roles: roles.sort().map(roleName => ({
|
|
412
|
+
roles: roles.sort().map((roleName) => ({
|
|
325
413
|
roleName,
|
|
326
414
|
statements: roleStatements(state, roleName)
|
|
327
415
|
}))
|
|
328
416
|
});
|
|
329
417
|
}
|
|
330
418
|
|
|
419
|
+
async function renderLambdaRoleConfig(state) {
|
|
420
|
+
const s3Flows = state.flows
|
|
421
|
+
.filter((flow) => includesEvent(flow, 's3'))
|
|
422
|
+
.map((flow) => ({
|
|
423
|
+
roleName: upperFirst(flow.flowTag)
|
|
424
|
+
}));
|
|
425
|
+
return render('GeneratedLambdaRoleServiceConfig.ejs', {
|
|
426
|
+
crud: state.crud,
|
|
427
|
+
relationships: state.relationships,
|
|
428
|
+
s3Flows
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
331
432
|
function roleStatements(state, roleName) {
|
|
332
433
|
const statements = [];
|
|
333
434
|
if (roleNeedsSchemaRead(state, roleName)) {
|
|
@@ -338,11 +439,10 @@ function roleStatements(state, roleName) {
|
|
|
338
439
|
]
|
|
339
440
|
});
|
|
340
441
|
}
|
|
341
|
-
addStatement(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
);
|
|
442
|
+
addStatement(statements, dynamoActionsForRole(roleName), [
|
|
443
|
+
...dynamoArns(roleTableNames(state, roleName)),
|
|
444
|
+
...externalDynamoArns(roleExternalDynamoTables(state, roleName))
|
|
445
|
+
]);
|
|
346
446
|
addStatement(
|
|
347
447
|
statements,
|
|
348
448
|
SQS_CONSUMER_ACTIONS,
|
|
@@ -402,7 +502,7 @@ function roleTableNames(state, roleName) {
|
|
|
402
502
|
'LogicalResultsData'
|
|
403
503
|
];
|
|
404
504
|
const flow = state.flows.find(
|
|
405
|
-
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
505
|
+
(candidate) => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
406
506
|
);
|
|
407
507
|
if (!flow) return [];
|
|
408
508
|
const tables = [];
|
|
@@ -427,68 +527,91 @@ function dynamoActionsForRole(roleName) {
|
|
|
427
527
|
}
|
|
428
528
|
|
|
429
529
|
function roleQueueNames(state, roleName) {
|
|
430
|
-
if (roleName === 'ProcFindDataRole')
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (roleName === '
|
|
530
|
+
if (roleName === 'ProcFindDataRole') {
|
|
531
|
+
return LEGACY_PROCESS_QUEUES;
|
|
532
|
+
}
|
|
533
|
+
if (roleName === 'WebSocketMainRole') {
|
|
534
|
+
if (state.websocket) {
|
|
535
|
+
return ['WebSocketCompleteHdrSqs'];
|
|
536
|
+
}
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
539
|
+
if (roleName === 'FlowObjectsRole') {
|
|
434
540
|
return [
|
|
435
|
-
...CRUD_ACTIONS.map(action => `${action}HdrSqs`),
|
|
436
|
-
...CRUD_ACTIONS.filter(action => action !== 'Get').map(
|
|
437
|
-
action => `${action}CompleteHdrSqs`
|
|
541
|
+
...CRUD_ACTIONS.map((action) => `${action}HdrSqs`),
|
|
542
|
+
...CRUD_ACTIONS.filter((action) => action !== 'Get').map(
|
|
543
|
+
(action) => `${action}CompleteHdrSqs`
|
|
438
544
|
),
|
|
439
545
|
...state.beforeLogical
|
|
440
546
|
.filter(({ action }) => CRUD_ACTIONS.includes(action))
|
|
441
547
|
.map(({ action }) => `${action}BeforeLogicalHdrSqs`)
|
|
442
548
|
];
|
|
443
|
-
|
|
549
|
+
}
|
|
550
|
+
if (roleName === 'FlowRelationshipRole') {
|
|
444
551
|
return [
|
|
445
|
-
...RELATIONSHIP_ACTIONS.flatMap(action => [
|
|
552
|
+
...RELATIONSHIP_ACTIONS.flatMap((action) => [
|
|
446
553
|
`${action}HdrSqs`,
|
|
447
554
|
`${action}CompleteHdrSqs`
|
|
448
555
|
]),
|
|
449
556
|
'GetRelationshipHdrDsq'
|
|
450
557
|
];
|
|
451
|
-
|
|
452
|
-
|
|
558
|
+
}
|
|
559
|
+
if (roleName === 'FlowRbacRole') {
|
|
560
|
+
return RBAC_ACTIONS.flatMap((action) => [
|
|
453
561
|
`${action}HdrSqs`,
|
|
454
562
|
`Process${action}HdrSqs`
|
|
455
563
|
]);
|
|
564
|
+
}
|
|
456
565
|
const flow = state.flows.find(
|
|
457
|
-
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
566
|
+
(candidate) => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
458
567
|
);
|
|
459
|
-
if (!flow)
|
|
568
|
+
if (!flow) {
|
|
569
|
+
return [];
|
|
570
|
+
}
|
|
460
571
|
const tag = upperFirst(flow.flowTag);
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
572
|
+
const queueList = [];
|
|
573
|
+
if (includesEvent(flow, 'extTopic')) {
|
|
574
|
+
queueList.push(`${tag}HdrSqs`);
|
|
575
|
+
}
|
|
576
|
+
if (includesEvent(flow, 'ownTopic')) {
|
|
577
|
+
queueList.push(`${tag}HdrSqs`);
|
|
578
|
+
}
|
|
579
|
+
if (includesEvent(flow, 'dsq')) {
|
|
580
|
+
queueList.push(`${tag}HdrDsq`);
|
|
581
|
+
}
|
|
582
|
+
queueList.push(
|
|
465
583
|
...flowExtTopicSteps([flow]).map(
|
|
466
584
|
({ flowTag, stepName }) => `${flowTag}${stepName}HdrSqs`
|
|
467
|
-
)
|
|
585
|
+
)
|
|
586
|
+
);
|
|
587
|
+
queueList.push(
|
|
468
588
|
...flowDsqSteps([flow]).map(
|
|
469
589
|
({ flowTag, stepName }) => `${flowTag}${stepName}HdrDsq`
|
|
470
|
-
)
|
|
590
|
+
)
|
|
591
|
+
);
|
|
592
|
+
queueList.push(
|
|
471
593
|
...state.sqsQueues.filter(
|
|
472
|
-
queueName => includesEvent(flow, 's3') && queueName.startsWith(tag)
|
|
594
|
+
(queueName) => includesEvent(flow, 's3') && queueName.startsWith(tag)
|
|
473
595
|
)
|
|
474
|
-
|
|
596
|
+
);
|
|
597
|
+
return queueList;
|
|
475
598
|
}
|
|
476
599
|
|
|
477
600
|
function roleTopicNames(state, roleName) {
|
|
478
601
|
if (roleName === 'ProcFindDataRole') return [];
|
|
479
602
|
if (roleName === 'FlowObjectsRole')
|
|
480
|
-
return state.outTopics.filter(topic => CRUD_ACTIONS.includes(topic));
|
|
603
|
+
return state.outTopics.filter((topic) => CRUD_ACTIONS.includes(topic));
|
|
481
604
|
if (roleName === 'FlowRelationshipRole')
|
|
482
|
-
return state.outTopics.filter(topic =>
|
|
605
|
+
return state.outTopics.filter((topic) =>
|
|
483
606
|
RELATIONSHIP_ACTIONS.includes(topic)
|
|
484
607
|
);
|
|
485
608
|
if (roleName === 'FlowRbacRole')
|
|
486
|
-
return state.outTopics.filter(topic => RBAC_ACTIONS.includes(topic));
|
|
609
|
+
return state.outTopics.filter((topic) => RBAC_ACTIONS.includes(topic));
|
|
487
610
|
const flow = state.flows.find(
|
|
488
|
-
candidate => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
611
|
+
(candidate) => customFlowRoleName(candidate?.flowTag ?? '') === roleName
|
|
489
612
|
);
|
|
490
613
|
return flow
|
|
491
|
-
? state.outTopics.filter(topic => topic === upperFirst(flow.flowTag))
|
|
614
|
+
? state.outTopics.filter((topic) => topic === upperFirst(flow.flowTag))
|
|
492
615
|
: [];
|
|
493
616
|
}
|
|
494
617
|
|
|
@@ -496,16 +619,62 @@ function dynamoArns(tableNames) {
|
|
|
496
619
|
return [...new Set(tableNames)]
|
|
497
620
|
.sort()
|
|
498
621
|
.map(
|
|
499
|
-
tableName =>
|
|
622
|
+
(tableName) =>
|
|
500
623
|
`arn:aws:dynamodb:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:table/${'${self:custom.iz_resourcePrefix}'}${tableName}`
|
|
501
624
|
);
|
|
502
625
|
}
|
|
503
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Generates DynamoDB ARNs for tables that belong to a parent service
|
|
629
|
+
* (via extendObjType). Uses the parent serviceTag instead of the
|
|
630
|
+
* current service's resourcePrefix, because those tables are owned
|
|
631
|
+
* by a different CloudFormation stack.
|
|
632
|
+
*
|
|
633
|
+
* ARN format:
|
|
634
|
+
* ...table/{parentServiceTag}_{iz_stage}_{tableName}
|
|
635
|
+
*/
|
|
636
|
+
function externalDynamoArns(externalTables) {
|
|
637
|
+
const seen = new Set();
|
|
638
|
+
const unique = [];
|
|
639
|
+
for (const table of externalTables) {
|
|
640
|
+
const dedupeKey = `${table.serviceTag}::${table.tableName}`;
|
|
641
|
+
if (!seen.has(dedupeKey)) {
|
|
642
|
+
seen.add(dedupeKey);
|
|
643
|
+
unique.push(table);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return unique
|
|
647
|
+
.sort((a, b) =>
|
|
648
|
+
`${a.serviceTag}_${a.tableName}`.localeCompare(
|
|
649
|
+
`${b.serviceTag}_${b.tableName}`
|
|
650
|
+
)
|
|
651
|
+
)
|
|
652
|
+
.map(
|
|
653
|
+
({ serviceTag, tableName }) =>
|
|
654
|
+
`arn:aws:dynamodb:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:table/${serviceTag}${'${self:custom.iz_stage}'}${tableName}`
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Returns the external DynamoDB table references (cross-service)
|
|
660
|
+
* that should be accessible from a given IAM role.
|
|
661
|
+
*/
|
|
662
|
+
function roleExternalDynamoTables(state, roleName) {
|
|
663
|
+
if (
|
|
664
|
+
roleName === 'FlowObjectsRole' ||
|
|
665
|
+
roleName === 'FlowRelationshipRole' ||
|
|
666
|
+
roleName === 'ProcFindDataRole'
|
|
667
|
+
) {
|
|
668
|
+
return state.externalDynamoTables;
|
|
669
|
+
}
|
|
670
|
+
return [];
|
|
671
|
+
}
|
|
672
|
+
|
|
504
673
|
function queueArns(queueNames) {
|
|
505
674
|
return [...new Set(queueNames)]
|
|
506
675
|
.sort()
|
|
507
676
|
.map(
|
|
508
|
-
queueName =>
|
|
677
|
+
(queueName) =>
|
|
509
678
|
`arn:aws:sqs:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_resourcePrefix}'}${queueName}`
|
|
510
679
|
);
|
|
511
680
|
}
|
|
@@ -514,13 +683,13 @@ function topicArns(topicNames) {
|
|
|
514
683
|
return [...new Set(topicNames)]
|
|
515
684
|
.sort()
|
|
516
685
|
.map(
|
|
517
|
-
topic =>
|
|
686
|
+
(topic) =>
|
|
518
687
|
`arn:aws:sns:${'${self:custom.iz_region}'}:${'${self:custom.iz_accountId}'}:${'${self:custom.iz_serviceTag}'}_${'${self:custom.iz_stage}'}_${topic}_Out`
|
|
519
688
|
);
|
|
520
689
|
}
|
|
521
690
|
|
|
522
691
|
function renderWebSocketSubscription(topicName) {
|
|
523
|
-
return
|
|
692
|
+
return render('WebSocketSubscription_Yaml.ejs', { topicName });
|
|
524
693
|
}
|
|
525
694
|
|
|
526
695
|
function collectTables(schemas) {
|
|
@@ -533,13 +702,13 @@ function collectTables(schemas) {
|
|
|
533
702
|
const pk =
|
|
534
703
|
keyName(
|
|
535
704
|
identifiers.find(
|
|
536
|
-
identifier =>
|
|
705
|
+
(identifier) =>
|
|
537
706
|
identifier.type === 'partitionKey' ||
|
|
538
707
|
identifier.type === 'identifier'
|
|
539
708
|
)
|
|
540
709
|
) ?? 'PK';
|
|
541
710
|
const sk = keyName(
|
|
542
|
-
identifiers.find(identifier => identifier.type === 'sortKey')
|
|
711
|
+
identifiers.find((identifier) => identifier.type === 'sortKey')
|
|
543
712
|
);
|
|
544
713
|
for (const resource of Object.values(schema.storageResources)) {
|
|
545
714
|
if (
|
|
@@ -554,13 +723,13 @@ function collectTables(schemas) {
|
|
|
554
723
|
}
|
|
555
724
|
|
|
556
725
|
function keyName(identifier) {
|
|
557
|
-
if (!identifier) return
|
|
726
|
+
if (!identifier) return null;
|
|
558
727
|
return (
|
|
559
728
|
identifier.name ??
|
|
560
729
|
identifier.fieldName ??
|
|
561
730
|
(Array.isArray(identifier.fieldNames)
|
|
562
731
|
? identifier.fieldNames.join(identifier.deliminator ?? '_')
|
|
563
|
-
:
|
|
732
|
+
: null)
|
|
564
733
|
);
|
|
565
734
|
}
|
|
566
735
|
|
|
@@ -573,7 +742,7 @@ function isCrudObject(object) {
|
|
|
573
742
|
function hasRbacAddOn(object) {
|
|
574
743
|
return (
|
|
575
744
|
Array.isArray(object?.addOnDataStructure) &&
|
|
576
|
-
object.addOnDataStructure.some(addOn => addOn?.type === 'rbac')
|
|
745
|
+
object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac')
|
|
577
746
|
);
|
|
578
747
|
}
|
|
579
748
|
function includesEvent(flow, event) {
|
|
@@ -588,7 +757,7 @@ function normalizeYaml(content) {
|
|
|
588
757
|
return `${content.trimEnd()}\n`;
|
|
589
758
|
}
|
|
590
759
|
async function render(templateName, data) {
|
|
591
|
-
return
|
|
760
|
+
return ejsLib.render(
|
|
592
761
|
await readFile(new URL(templateName, TEMPLATE_URL), 'utf8'),
|
|
593
762
|
{ resourceQueueName: data.queueName, ...data }
|
|
594
763
|
);
|