@izara_project/izara-core-generate-service-code 1.0.93 → 1.0.94
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/templates/InitialSetupConfig.ejs +1 -1
- package/src/emit/custom/customFlowS3.js +9 -4
- package/src/emit/custom/customFlowSteps.js +14 -6
- package/src/emit/custom/customFlows.js +27 -6
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +73 -0
- package/src/emit/resource/iamResourceArtifacts.js +5 -1
- package/src/emit/system/emitSystemFindDataArtifacts.js +15 -14
- package/test/core.test.js +13 -0
- package/test/customFlowApplicationArtifacts.test.js +159 -17
- package/test/graphDrivenFlowSchema.test.js +325 -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.94",
|
|
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
|
}
|
|
@@ -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) {
|
|
@@ -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
|
});
|
|
@@ -13,10 +13,7 @@ import {
|
|
|
13
13
|
export async function emitExtTopicStep({ flow, step, flowTag, stepName }) {
|
|
14
14
|
const source = `app/src/generatedCode/FlowSchemas/${flowTag}/source`;
|
|
15
15
|
const handlerRoot = `src/generatedCode/FlowSchemas/${flowTag}/source`;
|
|
16
|
-
|
|
17
|
-
if (stepName.startsWith(flowTag)) {
|
|
18
|
-
functionName = stepName;
|
|
19
|
-
}
|
|
16
|
+
const functionName = `${flowTag}${stepName}`;
|
|
20
17
|
const mainFileName = `${functionName}_Main`;
|
|
21
18
|
const startConfig = stepConfig(flow, step);
|
|
22
19
|
|
|
@@ -352,8 +349,12 @@ export function storedCacheTableName(flow, objects) {
|
|
|
352
349
|
if (flow.statusType !== 'storedCache') {
|
|
353
350
|
return null;
|
|
354
351
|
}
|
|
352
|
+
const targetObjectType =
|
|
353
|
+
typeof flow.objType === 'string'
|
|
354
|
+
? flow.objType
|
|
355
|
+
: flow.objType?.objectType;
|
|
355
356
|
const candidates = objects.filter(
|
|
356
|
-
(object) => object.objectType ===
|
|
357
|
+
(object) => object.objectType === targetObjectType
|
|
357
358
|
);
|
|
358
359
|
let object;
|
|
359
360
|
if (flow.objType?.serviceTag) {
|
|
@@ -373,7 +374,14 @@ export function storedCacheTableName(flow, objects) {
|
|
|
373
374
|
if (targetResource?.tableName) {
|
|
374
375
|
return targetResource.tableName;
|
|
375
376
|
}
|
|
376
|
-
|
|
377
|
+
const flowTag = flow.flowTag || 'unknown';
|
|
378
|
+
const targetObj = JSON.stringify(flow.objType);
|
|
379
|
+
console.error(
|
|
380
|
+
`[ERROR] [FlowSchema Validation] Flow '${flowTag}' has statusType 'storedCache' but cannot resolve DynamoDB tableName from objType ${targetObj}`
|
|
381
|
+
);
|
|
382
|
+
throw new Error(
|
|
383
|
+
`FlowSchema validation error: Flow '${flowTag}' has statusType 'storedCache' but cannot resolve DynamoDB tableName from objType ${targetObj}. Ensure the ObjectSchema exists and defines a DynamoDB storage resource with tableName.`
|
|
384
|
+
);
|
|
377
385
|
}
|
|
378
386
|
|
|
379
387
|
export async function renderStepMain(functionName, startConfig = null) {
|
|
@@ -14,11 +14,20 @@ export function supportedCustomFlows(flows) {
|
|
|
14
14
|
throw new TypeError('flows must be an array');
|
|
15
15
|
}
|
|
16
16
|
return flows.filter((flow) => {
|
|
17
|
-
if (typeof flow?.flowTag !== 'string' || flow.flowTag === '') {
|
|
18
|
-
|
|
17
|
+
if (typeof flow?.flowTag !== 'string' || flow.flowTag.trim() === '') {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'FlowSchema validation error: flowTag is required and must be a non-empty string'
|
|
20
|
+
);
|
|
19
21
|
}
|
|
20
|
-
if (!Array.isArray(flow.event)) {
|
|
21
|
-
|
|
22
|
+
if (!Array.isArray(flow.event) || flow.event.length === 0) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' requires a non-empty 'event' array`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (flow.event.includes('generateCode') && flow.event.length > 1) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' with event 'generateCode' must be a standalone event and cannot co-exist with other events`
|
|
30
|
+
);
|
|
22
31
|
}
|
|
23
32
|
const hasSupportedEvent = flow.event.some((e) => SUPPORTED_EVENTS.has(e));
|
|
24
33
|
if (!hasSupportedEvent) {
|
|
@@ -28,9 +37,14 @@ export function supportedCustomFlows(flows) {
|
|
|
28
37
|
if (flowHasOwnTopic(flow)) {
|
|
29
38
|
return false;
|
|
30
39
|
}
|
|
31
|
-
if (
|
|
40
|
+
if (flow.event.includes('dsq')) {
|
|
32
41
|
return false;
|
|
33
42
|
}
|
|
43
|
+
if (!flow.generatedCodeConfig.codeHookTag) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' has generatedCodeConfig without ownTopic, which requires 'codeHookTag' ('before' | 'after')`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
34
48
|
}
|
|
35
49
|
return true;
|
|
36
50
|
});
|
|
@@ -67,6 +81,11 @@ export function flowExtTopicSteps(flows) {
|
|
|
67
81
|
}
|
|
68
82
|
const hasTriggerFlowTypes =
|
|
69
83
|
Array.isArray(flow.triggerFlowTypes) && flow.triggerFlowTypes.length > 0;
|
|
84
|
+
if (flow.statusType === 'triggerCache' && !hasTriggerFlowTypes) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' has statusType 'triggerCache' but requires a non-empty 'triggerFlowTypes' array`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
70
89
|
const steps = flow.flowSteps ?? [];
|
|
71
90
|
const hasCheckStep = steps.some(
|
|
72
91
|
(step) =>
|
|
@@ -87,7 +106,9 @@ export function flowExtTopicSteps(flows) {
|
|
|
87
106
|
}
|
|
88
107
|
const isCheckStep = step.stepName.toLowerCase().includes('checktrigger');
|
|
89
108
|
if (isCheckStep && !hasTriggerFlowTypes) {
|
|
90
|
-
|
|
109
|
+
throw new Error(
|
|
110
|
+
`FlowSchema validation error: Flow '${flow.flowTag}' defines step '${step.stepName}' but requires a non-empty 'triggerFlowTypes' array`
|
|
111
|
+
);
|
|
91
112
|
}
|
|
92
113
|
return [
|
|
93
114
|
{
|