@izara_project/izara-core-generate-service-code 1.0.93 → 1.0.95
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 +1 -1
- package/src/compile/compileProject.js +3 -1
- package/src/compile/flowValidator.js +182 -18
- package/src/compile/loadSchemas.js +144 -9
- package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +1 -26
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +1 -31
- package/src/emit/custom/customFlowPaths.js +3 -16
- package/src/emit/custom/customFlowS3.js +9 -4
- package/src/emit/custom/customFlowSteps.js +14 -47
- package/src/emit/custom/customFlows.js +27 -6
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +1 -47
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +75 -2
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +7 -7
- package/src/emit/relationship/templates/CreateRelationship_Main.ejs +5 -5
- package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +3 -3
- package/src/emit/relationship/templates/GetRelationship_Main.ejs +1 -1
- package/src/emit/relationship/templates/MoveRelationship_Main.ejs +5 -5
- package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +5 -5
- package/src/emit/resource/iamResourceArtifacts.js +6 -6
- package/src/emit/resource/resourceState.js +3 -9
- package/src/emit/system/emitSystemFindDataArtifacts.js +15 -14
- package/test/core.test.js +13 -0
- package/test/customFlowApplicationArtifacts.test.js +285 -17
- package/test/graphDrivenFlowSchema.test.js +325 -0
- package/test/relationshipApplicationArtifacts.test.js +29 -0
- package/test/resourceArtifacts.test.js +46 -1
- package/test/systemFindDataArtifacts.test.js +73 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izara_project/izara-core-generate-service-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.95",
|
|
4
4
|
"description": "Code for locally generating per service files",
|
|
5
5
|
"homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
|
|
6
6
|
"repository": {
|
|
@@ -1,8 +1,67 @@
|
|
|
1
|
+
export const VALID_EVENT_ENUM = [
|
|
2
|
+
'eventBridge',
|
|
3
|
+
'extTopic',
|
|
4
|
+
'lambdaSyncInv',
|
|
5
|
+
'lambdaSyncApi',
|
|
6
|
+
'ownTopic',
|
|
7
|
+
's3',
|
|
8
|
+
'dsq',
|
|
9
|
+
'generateCode'
|
|
10
|
+
];
|
|
11
|
+
|
|
1
12
|
export function validateFlowSchema(flowSchema) {
|
|
2
13
|
if (!flowSchema || typeof flowSchema !== 'object') {
|
|
3
14
|
throw new TypeError('flowSchema must be an object');
|
|
4
15
|
}
|
|
5
16
|
|
|
17
|
+
if (
|
|
18
|
+
typeof flowSchema.flowTag !== 'string' ||
|
|
19
|
+
flowSchema.flowTag.trim() === ''
|
|
20
|
+
) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'FlowSchema validation error: flowTag is required and must be a non-empty string'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const flowTag = flowSchema.flowTag;
|
|
26
|
+
|
|
27
|
+
if (!Array.isArray(flowSchema.event) || flowSchema.event.length === 0) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`FlowSchema validation error: Flow '${flowTag}' requires a non-empty 'event' array`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const [eIdx, eventName] of flowSchema.event.entries()) {
|
|
34
|
+
if (
|
|
35
|
+
typeof eventName !== 'string' ||
|
|
36
|
+
!VALID_EVENT_ENUM.includes(eventName)
|
|
37
|
+
) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`FlowSchema validation error: Flow '${flowTag}' event[${eIdx}] '${eventName}' is invalid. Must be one of ${JSON.stringify(VALID_EVENT_ENUM)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
flowSchema.event.includes('generateCode') &&
|
|
46
|
+
flowSchema.event.length > 1
|
|
47
|
+
) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`FlowSchema validation error: Flow '${flowTag}' with event 'generateCode' must be a standalone event and cannot co-exist with other events`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (flowSchema.event.includes('s3')) {
|
|
54
|
+
if (
|
|
55
|
+
!flowSchema.bucketName ||
|
|
56
|
+
typeof flowSchema.bucketName !== 'string' ||
|
|
57
|
+
flowSchema.bucketName.trim() === ''
|
|
58
|
+
) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`FlowSchema validation error: Flow '${flowTag}' with event 's3' requires 'bucketName' as a non-empty string`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
6
65
|
if (flowSchema.initiateBy !== undefined) {
|
|
7
66
|
const validInitiateBy = [
|
|
8
67
|
'user',
|
|
@@ -25,6 +84,110 @@ export function validateFlowSchema(flowSchema) {
|
|
|
25
84
|
}
|
|
26
85
|
}
|
|
27
86
|
|
|
87
|
+
if (flowSchema.statusType !== undefined) {
|
|
88
|
+
const validStatusType = [
|
|
89
|
+
'none',
|
|
90
|
+
'statusField',
|
|
91
|
+
'storedCache',
|
|
92
|
+
'triggerCache'
|
|
93
|
+
];
|
|
94
|
+
if (!validStatusType.includes(flowSchema.statusType)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`FlowSchema validation error: Flow '${flowTag}' statusType must be one of ${JSON.stringify(validStatusType)}, got '${flowSchema.statusType}'`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (flowSchema.statusType !== 'none') {
|
|
101
|
+
if (!flowSchema.objType) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`FlowSchema validation error: Flow '${flowTag}' with statusType '${flowSchema.statusType}' requires 'objType'`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (flowSchema.statusType === 'storedCache') {
|
|
108
|
+
const objTypeVal =
|
|
109
|
+
typeof flowSchema.objType === 'string'
|
|
110
|
+
? flowSchema.objType
|
|
111
|
+
: flowSchema.objType?.objectType;
|
|
112
|
+
if (typeof objTypeVal !== 'string' || objTypeVal.trim() === '') {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`FlowSchema validation error: Flow '${flowTag}' with statusType 'storedCache' requires 'objType' to be a non-empty string or object with string 'objectType'`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (flowSchema.statusType === 'triggerCache') {
|
|
120
|
+
if (
|
|
121
|
+
!Array.isArray(flowSchema.triggerFlowTypes) ||
|
|
122
|
+
flowSchema.triggerFlowTypes.length === 0
|
|
123
|
+
) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`FlowSchema validation error: Flow '${flowTag}' with statusType 'triggerCache' requires a non-empty 'triggerFlowTypes' array`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
for (const [tIdx, item] of flowSchema.triggerFlowTypes.entries()) {
|
|
129
|
+
if (!item || (typeof item !== 'string' && typeof item !== 'object')) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`FlowSchema validation error: Flow '${flowTag}' triggerFlowTypes[${tIdx}] must be a non-empty string or object`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (typeof item === 'string' && item.trim() === '') {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`FlowSchema validation error: Flow '${flowTag}' triggerFlowTypes[${tIdx}] must not be an empty string`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
flowSchema.event.includes('generateCode') &&
|
|
146
|
+
!flowSchema.generatedCodeConfig
|
|
147
|
+
) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`FlowSchema validation error: Flow '${flowTag}' with event 'generateCode' requires 'generatedCodeConfig'`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (flowSchema.generatedCodeConfig) {
|
|
154
|
+
const config = flowSchema.generatedCodeConfig;
|
|
155
|
+
if (
|
|
156
|
+
config.invocationType !== undefined &&
|
|
157
|
+
!['ownTopic', 'lambdaSyncInv'].includes(config.invocationType)
|
|
158
|
+
) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`FlowSchema validation error: Flow '${flowTag}' generatedCodeConfig.invocationType '${config.invocationType}' is invalid. Must be 'ownTopic' or 'lambdaSyncInv'`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const isOwnTopic = flowSchema.event.includes('ownTopic');
|
|
164
|
+
const isDsq = flowSchema.event.includes('dsq');
|
|
165
|
+
const isHook = Boolean(
|
|
166
|
+
config.generateCodeTag ||
|
|
167
|
+
config.invocationType ||
|
|
168
|
+
config.objType ||
|
|
169
|
+
flowSchema.event.includes('generateCode') ||
|
|
170
|
+
(!isOwnTopic && !isDsq)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const hookTag = config.codeHookTag;
|
|
174
|
+
if (hookTag === 'beforeLogical') {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`FlowSchema validation error: Flow '${flowTag}' generatedCodeConfig.codeHookTag is no longer supported`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (isHook && !hookTag) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`FlowSchema validation error: Flow '${flowTag}' generatedCodeConfig requires 'codeHookTag' ('before' | 'after'), got '${hookTag}'`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (hookTag !== undefined && hookTag !== 'before' && hookTag !== 'after') {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`FlowSchema validation error: Flow '${flowTag}' generatedCodeConfig.codeHookTag '${hookTag}' is invalid. Must be 'before' or 'after'`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
28
191
|
const steps = flowSchema.flowSteps;
|
|
29
192
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
30
193
|
throw new Error(
|
|
@@ -32,10 +195,7 @@ export function validateFlowSchema(flowSchema) {
|
|
|
32
195
|
);
|
|
33
196
|
}
|
|
34
197
|
|
|
35
|
-
|
|
36
|
-
if (Array.isArray(flowSchema.event)) {
|
|
37
|
-
flowEvents = flowSchema.event;
|
|
38
|
-
}
|
|
198
|
+
const flowEvents = flowSchema.event;
|
|
39
199
|
const syncEvents = ['lambdaSyncInv', 'lambdaSyncApi'];
|
|
40
200
|
let hasSyncEvent = false;
|
|
41
201
|
for (const eventName of flowEvents) {
|
|
@@ -52,10 +212,6 @@ export function validateFlowSchema(flowSchema) {
|
|
|
52
212
|
}
|
|
53
213
|
|
|
54
214
|
if (hasSyncEvent && steps.length > 1) {
|
|
55
|
-
let flowTag = 'unknown';
|
|
56
|
-
if (flowSchema.flowTag) {
|
|
57
|
-
flowTag = flowSchema.flowTag;
|
|
58
|
-
}
|
|
59
215
|
throw new Error(
|
|
60
216
|
`FlowSchema validation error: Flow '${flowTag}' with synchronous ` +
|
|
61
217
|
`events ('lambdaSyncInv' | 'lambdaSyncApi') must only define a ` +
|
|
@@ -110,6 +266,17 @@ export function validateFlowSchema(flowSchema) {
|
|
|
110
266
|
);
|
|
111
267
|
}
|
|
112
268
|
|
|
269
|
+
if (step.stepName.toLowerCase().includes('checktrigger')) {
|
|
270
|
+
if (
|
|
271
|
+
!Array.isArray(flowSchema.triggerFlowTypes) ||
|
|
272
|
+
flowSchema.triggerFlowTypes.length === 0
|
|
273
|
+
) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`FlowSchema validation error: Flow '${flowTag}' defines step '${step.stepName}' but requires a non-empty 'triggerFlowTypes' array`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
113
280
|
if (Array.isArray(step.nextSteps) && step.nextSteps.length > 0) {
|
|
114
281
|
for (let nIdx = 0; nIdx < step.nextSteps.length; nIdx++) {
|
|
115
282
|
const item = step.nextSteps[nIdx];
|
|
@@ -128,9 +295,10 @@ export function validateFlowSchema(flowSchema) {
|
|
|
128
295
|
);
|
|
129
296
|
}
|
|
130
297
|
if (item.targetType === 'ownTopic') {
|
|
298
|
+
const ownTopicTarget = item.stepName || item.topic;
|
|
131
299
|
if (
|
|
132
|
-
typeof
|
|
133
|
-
|
|
300
|
+
typeof ownTopicTarget !== 'string' ||
|
|
301
|
+
ownTopicTarget.trim() === ''
|
|
134
302
|
) {
|
|
135
303
|
throw new Error(
|
|
136
304
|
`FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] ownTopic target requires non-empty 'stepName'`
|
|
@@ -145,10 +313,8 @@ export function validateFlowSchema(flowSchema) {
|
|
|
145
313
|
`FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] extTopic target requires non-empty 'serviceTag'`
|
|
146
314
|
);
|
|
147
315
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
item.topicName.trim() === ''
|
|
151
|
-
) {
|
|
316
|
+
const extTopicName = item.topicName || item.topic;
|
|
317
|
+
if (typeof extTopicName !== 'string' || extTopicName.trim() === '') {
|
|
152
318
|
throw new Error(
|
|
153
319
|
`FlowSchema validation error: flowSteps[${idx}].nextSteps[${nIdx}] extTopic target requires non-empty 'topicName'`
|
|
154
320
|
);
|
|
@@ -203,10 +369,8 @@ export function validateFlowSchema(flowSchema) {
|
|
|
203
369
|
`FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] extTopic source requires non-empty 'serviceTag'`
|
|
204
370
|
);
|
|
205
371
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
item.topicName.trim() === ''
|
|
209
|
-
) {
|
|
372
|
+
const extSourceName = item.topicName || item.topic;
|
|
373
|
+
if (typeof extSourceName !== 'string' || extSourceName.trim() === '') {
|
|
210
374
|
throw new Error(
|
|
211
375
|
`FlowSchema validation error: flowSteps[${idx}].previousSteps[${pIdx}] extTopic source requires non-empty 'topicName'`
|
|
212
376
|
);
|
|
@@ -206,6 +206,118 @@ export async function loadLocalSchemaDirectory(
|
|
|
206
206
|
|
|
207
207
|
if (directoryName === 'FlowSchemas') {
|
|
208
208
|
for (const schema of loadedSchemas) {
|
|
209
|
+
if (!schema?._sourceFile) {
|
|
210
|
+
schema._sourceFile = entry.name;
|
|
211
|
+
}
|
|
212
|
+
if (
|
|
213
|
+
!schema?.flowTag ||
|
|
214
|
+
typeof schema.flowTag !== 'string' ||
|
|
215
|
+
schema.flowTag.trim() === ''
|
|
216
|
+
) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`FlowSchema validation error in ${entry.name}: flowTag is required and must be a non-empty string`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const flowTag = schema.flowTag;
|
|
222
|
+
|
|
223
|
+
if (!Array.isArray(schema.event) || schema.event.length === 0) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' requires a non-empty 'event' array`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const [eIdx, eventName] of schema.event.entries()) {
|
|
230
|
+
if (
|
|
231
|
+
typeof eventName !== 'string' ||
|
|
232
|
+
!VALID_EVENT_ENUM.includes(eventName)
|
|
233
|
+
) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' event[${eIdx}] '${eventName}' is invalid. ` +
|
|
236
|
+
`Must be one of ${JSON.stringify(VALID_EVENT_ENUM)}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
schema.event.includes('generateCode') &&
|
|
243
|
+
schema.event.length > 1
|
|
244
|
+
) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' with event 'generateCode' must be a standalone event and cannot co-exist with other events`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (schema?.statusType !== undefined && schema?.statusType !== 'none') {
|
|
251
|
+
const validStatusTypes = [
|
|
252
|
+
'statusField',
|
|
253
|
+
'storedCache',
|
|
254
|
+
'triggerCache',
|
|
255
|
+
'none'
|
|
256
|
+
];
|
|
257
|
+
if (!validStatusTypes.includes(schema.statusType)) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' statusType must be one of ${JSON.stringify(validStatusTypes)}, got '${schema.statusType}'`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if (!schema.objType) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' with statusType '${schema.statusType}' requires 'objType'`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (schema.statusType === 'storedCache') {
|
|
268
|
+
const objTypeVal =
|
|
269
|
+
typeof schema.objType === 'string'
|
|
270
|
+
? schema.objType
|
|
271
|
+
: schema.objType?.objectType;
|
|
272
|
+
if (typeof objTypeVal !== 'string' || objTypeVal.trim() === '') {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' with statusType 'storedCache' requires 'objType' to be a non-empty string or object with string 'objectType'`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (schema.statusType === 'triggerCache') {
|
|
279
|
+
if (
|
|
280
|
+
!Array.isArray(schema.triggerFlowTypes) ||
|
|
281
|
+
schema.triggerFlowTypes.length === 0
|
|
282
|
+
) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' with statusType 'triggerCache' requires a non-empty 'triggerFlowTypes' array`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
for (const [tIdx, item] of schema.triggerFlowTypes.entries()) {
|
|
288
|
+
if (
|
|
289
|
+
!item ||
|
|
290
|
+
(typeof item !== 'string' && typeof item !== 'object')
|
|
291
|
+
) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' triggerFlowTypes[${tIdx}] must be a non-empty string or object`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
if (typeof item === 'string' && item.trim() === '') {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' triggerFlowTypes[${tIdx}] must not be an empty string`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (Array.isArray(schema?.flowSteps)) {
|
|
305
|
+
for (const step of schema.flowSteps) {
|
|
306
|
+
if (
|
|
307
|
+
typeof step?.stepName === 'string' &&
|
|
308
|
+
step.stepName.toLowerCase().includes('checktrigger')
|
|
309
|
+
) {
|
|
310
|
+
if (
|
|
311
|
+
!Array.isArray(schema.triggerFlowTypes) ||
|
|
312
|
+
schema.triggerFlowTypes.length === 0
|
|
313
|
+
) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`FlowSchema validation error in ${entry.name}: Flow '${flowTag}' defines step '${step.stepName}' but requires a non-empty 'triggerFlowTypes' array`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
209
321
|
if (
|
|
210
322
|
Array.isArray(schema?.event) &&
|
|
211
323
|
schema.event.includes('generateCode') &&
|
|
@@ -234,19 +346,37 @@ export async function loadLocalSchemaDirectory(
|
|
|
234
346
|
);
|
|
235
347
|
}
|
|
236
348
|
}
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
'
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
349
|
+
if (schema?.generatedCodeConfig) {
|
|
350
|
+
const config = schema.generatedCodeConfig;
|
|
351
|
+
const isOwnTopic =
|
|
352
|
+
Array.isArray(schema.event) && schema.event.includes('ownTopic');
|
|
353
|
+
const isDsq =
|
|
354
|
+
Array.isArray(schema.event) && schema.event.includes('dsq');
|
|
355
|
+
const isHook = Boolean(
|
|
356
|
+
config.generateCodeTag ||
|
|
357
|
+
config.invocationType ||
|
|
358
|
+
config.objType ||
|
|
359
|
+
(Array.isArray(schema.event) &&
|
|
360
|
+
schema.event.includes('generateCode')) ||
|
|
361
|
+
(!isOwnTopic && !isDsq)
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const hookTag = config.codeHookTag;
|
|
244
365
|
if (hookTag === 'beforeLogical') {
|
|
245
366
|
throw new Error(
|
|
246
367
|
`Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag is no longer supported`
|
|
247
368
|
);
|
|
248
369
|
}
|
|
249
|
-
if (
|
|
370
|
+
if (isHook && !hookTag) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Unsupported FlowHook configuration in ${entry.name}: Flow '${flowTag}' with generatedCodeConfig requires 'codeHookTag' ('before' or 'after')`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (
|
|
376
|
+
hookTag !== undefined &&
|
|
377
|
+
hookTag !== 'before' &&
|
|
378
|
+
hookTag !== 'after'
|
|
379
|
+
) {
|
|
250
380
|
throw new Error(
|
|
251
381
|
`Unsupported FlowHook configuration in ${entry.name}: generatedCodeConfig.codeHookTag '${hookTag}' is invalid. Must be 'before' or 'after'`
|
|
252
382
|
);
|
|
@@ -718,6 +848,11 @@ function _isTestEnvironment() {
|
|
|
718
848
|
process.env.NODE_ENV === 'test' ||
|
|
719
849
|
process.env.npm_lifecycle_event === 'test' ||
|
|
720
850
|
Boolean(process.execArgv?.includes('--test')) ||
|
|
721
|
-
Boolean(process.argv?.includes('--test'))
|
|
851
|
+
Boolean(process.argv?.includes('--test')) ||
|
|
852
|
+
Boolean(
|
|
853
|
+
process.argv?.some(
|
|
854
|
+
(arg) => arg.includes('.test.') || arg.includes('test/')
|
|
855
|
+
)
|
|
856
|
+
)
|
|
722
857
|
);
|
|
723
858
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ejsLib from 'ejs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
|
|
4
|
-
import { customFlowNextSteps
|
|
4
|
+
import { customFlowNextSteps } from '../custom/customFlows.js';
|
|
5
5
|
import { createCrudActionPlans } from '../crud/crudActionPlan.js';
|
|
6
6
|
|
|
7
7
|
const TEMPLATE_URL = new URL('./templates/', import.meta.url);
|
|
@@ -217,29 +217,6 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
|
|
|
217
217
|
(objects || []).filter(o => o.belongTo?.serviceTag && o.belongTo.serviceTag !== 'Rbac').map(o => o.belongTo.serviceTag)
|
|
218
218
|
)).sort();
|
|
219
219
|
|
|
220
|
-
const customRbacFlows = (flows || []).filter((f) => flowHasRbac(f, objects));
|
|
221
|
-
const customRbacFlowTags = customRbacFlows.map((f) => f.flowTag);
|
|
222
|
-
const customRbacFlowsWithTags = customRbacFlows.map((f) => {
|
|
223
|
-
const roleName = upperFirst(f.flowTag);
|
|
224
|
-
const matched = objects.find((o) => o.objectType === f.objType?.objectType);
|
|
225
|
-
const targetServiceTags = [];
|
|
226
|
-
const belongTo = matched?.belongTo;
|
|
227
|
-
if (belongTo) {
|
|
228
|
-
if (Array.isArray(belongTo)) {
|
|
229
|
-
for (const b of belongTo) {
|
|
230
|
-
if (b?.serviceTag) targetServiceTags.push(b.serviceTag);
|
|
231
|
-
}
|
|
232
|
-
} else if (belongTo.serviceTag) {
|
|
233
|
-
targetServiceTags.push(belongTo.serviceTag);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return {
|
|
237
|
-
roleName,
|
|
238
|
-
flowTag: f.flowTag,
|
|
239
|
-
targetServiceTags: Array.from(new Set(targetServiceTags))
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
|
|
243
220
|
const crudPlans = createCrudActionPlans(objects);
|
|
244
221
|
const hasQuotaReservation = crudPlans?.Create?.hasQuotaReservation ?? false;
|
|
245
222
|
|
|
@@ -250,8 +227,6 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
|
|
|
250
227
|
relationships,
|
|
251
228
|
flows,
|
|
252
229
|
customFlowNextSteps: customFlowNextSteps(flows),
|
|
253
|
-
customRbacFlowTags,
|
|
254
|
-
customRbacFlowsWithTags,
|
|
255
230
|
objectGraphServiceTags,
|
|
256
231
|
relationshipGraphServiceTags,
|
|
257
232
|
crud,
|
|
@@ -160,7 +160,7 @@ const tableConfigs = {
|
|
|
160
160
|
(flow?.flowSteps || []).forEach(function(step) {
|
|
161
161
|
if (Array.isArray(step?.previousSteps)) {
|
|
162
162
|
const stepName = step?.stepName ? step.stepName[0].toUpperCase() + step.stepName.slice(1) : '';
|
|
163
|
-
const functionName =
|
|
163
|
+
const functionName = `${flowTag}${stepName}`;
|
|
164
164
|
const queueName = `${functionName}HdrSqs`;
|
|
165
165
|
step.previousSteps.forEach(function(prev) {
|
|
166
166
|
if (prev && prev.sourceType === 'extTopic' && prev.serviceTag && prev.topicName) {
|
|
@@ -178,23 +178,6 @@ const tableConfigs = {
|
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
180
|
});
|
|
181
|
-
|
|
182
|
-
const currentServiceTag = (typeof serviceTag !== 'undefined' && serviceTag) ? serviceTag : ((typeof project !== 'undefined' && project?.serviceTag) ? project.serviceTag : '');
|
|
183
|
-
const rbacSubs = [];
|
|
184
|
-
|
|
185
|
-
if (typeof customRbacFlowTags !== 'undefined' && customRbacFlowTags && customRbacFlowTags.length > 0) {
|
|
186
|
-
const allRbacTags = Array.from(new Set([
|
|
187
|
-
'Rbac',
|
|
188
|
-
currentServiceTag,
|
|
189
|
-
...(typeof rbacValidatorServiceTags !== 'undefined' ? rbacValidatorServiceTags : [])
|
|
190
|
-
])).filter(Boolean);
|
|
191
|
-
customRbacFlowTags.forEach(function(flowTag) {
|
|
192
|
-
const tagUpper = flowTag[0].toUpperCase() + flowTag.slice(1);
|
|
193
|
-
allRbacTags.forEach(function(tag) {
|
|
194
|
-
rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: `${tagUpper}RbacCallbackHdrSqs` });
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
181
|
-%>
|
|
199
182
|
const snsTopicSubscriptions = [
|
|
200
183
|
<% if (relSubs.length > 0) { -%>
|
|
@@ -247,19 +230,6 @@ const snsTopicSubscriptions = [
|
|
|
247
230
|
sqsEndpoint: '<%= sub.sqsEndpoint %>'
|
|
248
231
|
},
|
|
249
232
|
<% }); -%>
|
|
250
|
-
<% } -%>
|
|
251
|
-
<% if (rbacSubs.length > 0) { -%>
|
|
252
|
-
|
|
253
|
-
// ------------------------------------------------------------
|
|
254
|
-
// RBAC Validator Subscriptions
|
|
255
|
-
// ------------------------------------------------------------
|
|
256
|
-
<% rbacSubs.forEach(function(sub, idx) { -%>
|
|
257
|
-
{
|
|
258
|
-
serviceTag: '<%= sub.serviceTag %>',
|
|
259
|
-
topicName: '<%= sub.topicName %>',
|
|
260
|
-
sqsEndpoint: '<%= sub.sqsEndpoint %>'
|
|
261
|
-
},
|
|
262
|
-
<% }); -%>
|
|
263
233
|
<% } -%>
|
|
264
234
|
|
|
265
235
|
//(<additionalSubscription>)
|
|
@@ -4,8 +4,7 @@ import {
|
|
|
4
4
|
generatedDsqFlows,
|
|
5
5
|
generatedOwnTopicFlows,
|
|
6
6
|
supportedCustomFlows,
|
|
7
|
-
upperFirst
|
|
8
|
-
flowHasRbac
|
|
7
|
+
upperFirst
|
|
9
8
|
} from './customFlows.js';
|
|
10
9
|
|
|
11
10
|
export const YAML_PATH = 'app/sls_yaml/generatedCode/source/flow-schema.yml';
|
|
@@ -35,12 +34,6 @@ export function ownTopicRootPaths(flow, objects = []) {
|
|
|
35
34
|
`${source}/${tag}_WbsMain.js`,
|
|
36
35
|
`${source}/${tag}_HdrWbs.js`
|
|
37
36
|
];
|
|
38
|
-
if (flowHasRbac(flow, objects)) {
|
|
39
|
-
paths.push(
|
|
40
|
-
`${source}/${tag}RbacCallback_Main.js`,
|
|
41
|
-
`${source}/${tag}RbacCallback_HdrSqs.js`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
37
|
return paths;
|
|
45
38
|
}
|
|
46
39
|
|
|
@@ -52,12 +45,6 @@ export function flowPaths(flow, objects = []) {
|
|
|
52
45
|
if (needsProcessMain(flow)) {
|
|
53
46
|
paths.push(`${source}/${process}_Main.js`);
|
|
54
47
|
}
|
|
55
|
-
if (flowHasRbac(flow, objects)) {
|
|
56
|
-
paths.push(
|
|
57
|
-
`${source}/${tag}RbacCallback_Main.js`,
|
|
58
|
-
`${source}/${tag}RbacCallback_HdrSqs.js`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
48
|
if (flow.event.includes('extTopic') && !flow.event.includes('ownTopic')) {
|
|
62
49
|
paths.push(`${source}/${process}_HdrSqs.js`);
|
|
63
50
|
}
|
|
@@ -116,7 +103,7 @@ export function customFlowApplicationManagedPaths(
|
|
|
116
103
|
const extSteps = flowExtTopicSteps(flows);
|
|
117
104
|
const dsqSteps = flowDsqSteps(flows);
|
|
118
105
|
|
|
119
|
-
return [
|
|
106
|
+
return Array.from(new Set([
|
|
120
107
|
...customFlowList.flatMap((flow) => flowPaths(flow, objects)),
|
|
121
108
|
...generatedRoots.flatMap((flow) => ownTopicRootPaths(flow, objects)),
|
|
122
109
|
...extSteps.flatMap(({ flowTag, stepName }) => {
|
|
@@ -145,5 +132,5 @@ export function customFlowApplicationManagedPaths(
|
|
|
145
132
|
dsqSteps.length > 0
|
|
146
133
|
? [YAML_PATH]
|
|
147
134
|
: [])
|
|
148
|
-
];
|
|
135
|
+
]));
|
|
149
136
|
}
|
|
@@ -88,13 +88,18 @@ export async function emitS3Flow(flow, source, handlerRoot) {
|
|
|
88
88
|
filterPatternFunctionName
|
|
89
89
|
});
|
|
90
90
|
} else if (type === 's3') {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
if (
|
|
92
|
+
!flow.bucketName ||
|
|
93
|
+
typeof flow.bucketName !== 'string' ||
|
|
94
|
+
flow.bucketName.trim() === ''
|
|
95
|
+
) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' with event 's3' requires 'bucketName' as a non-empty string`
|
|
98
|
+
);
|
|
94
99
|
}
|
|
95
100
|
functionYaml = renderS3FunctionYaml({
|
|
96
101
|
...descriptor,
|
|
97
|
-
bucketName,
|
|
102
|
+
bucketName: flow.bucketName,
|
|
98
103
|
flowTag: flow.flowTag,
|
|
99
104
|
flowStepName: ''
|
|
100
105
|
});
|