@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
|
@@ -66,6 +66,7 @@ test('validateFlowSchema passes for valid Graph-Driven FlowSchema', () => {
|
|
|
66
66
|
test('validateFlowSchema rejects flowSteps[0] without stepName Start', () => {
|
|
67
67
|
const invalidSchema = {
|
|
68
68
|
flowTag: 'TestFlow',
|
|
69
|
+
event: ['ownTopic'],
|
|
69
70
|
flowSteps: [
|
|
70
71
|
{
|
|
71
72
|
stepName: 'Begin',
|
|
@@ -83,6 +84,7 @@ test('validateFlowSchema rejects flowSteps[0] without stepName Start', () => {
|
|
|
83
84
|
test('validateFlowSchema rejects entry step with previousSteps or event', () => {
|
|
84
85
|
const schemaWithPrev = {
|
|
85
86
|
flowTag: 'TestFlow',
|
|
87
|
+
event: ['ownTopic'],
|
|
86
88
|
flowSteps: [
|
|
87
89
|
{
|
|
88
90
|
stepName: 'Start',
|
|
@@ -98,6 +100,7 @@ test('validateFlowSchema rejects entry step with previousSteps or event', () =>
|
|
|
98
100
|
|
|
99
101
|
const schemaWithEvent = {
|
|
100
102
|
flowTag: 'TestFlow',
|
|
103
|
+
event: ['ownTopic'],
|
|
101
104
|
flowSteps: [
|
|
102
105
|
{
|
|
103
106
|
stepName: 'Start',
|
|
@@ -115,6 +118,7 @@ test('validateFlowSchema rejects entry step with previousSteps or event', () =>
|
|
|
115
118
|
test('validateFlowSchema rejects nextSteps item missing targetType', () => {
|
|
116
119
|
const invalidSchema = {
|
|
117
120
|
flowTag: 'TestFlow',
|
|
121
|
+
event: ['ownTopic'],
|
|
118
122
|
flowSteps: [
|
|
119
123
|
{
|
|
120
124
|
stepName: 'Start',
|
|
@@ -132,6 +136,7 @@ test('validateFlowSchema rejects nextSteps item missing targetType', () => {
|
|
|
132
136
|
test('validateFlowSchema rejects previousSteps item missing sourceType', () => {
|
|
133
137
|
const invalidSchema = {
|
|
134
138
|
flowTag: 'TestFlow',
|
|
139
|
+
event: ['ownTopic'],
|
|
135
140
|
flowSteps: [
|
|
136
141
|
{
|
|
137
142
|
stepName: 'Start',
|
|
@@ -153,6 +158,7 @@ test('validateFlowSchema rejects previousSteps item missing sourceType', () => {
|
|
|
153
158
|
test('validateFlowSchema rejects extTopic nextSteps missing toStepNames or serviceTag', () => {
|
|
154
159
|
const missingToSteps = {
|
|
155
160
|
flowTag: 'TestFlow',
|
|
161
|
+
event: ['ownTopic'],
|
|
156
162
|
flowSteps: [
|
|
157
163
|
{
|
|
158
164
|
stepName: 'Start',
|
|
@@ -176,6 +182,7 @@ test('validateFlowSchema rejects extTopic nextSteps missing toStepNames or servi
|
|
|
176
182
|
test('validateFlowSchema rejects extTopic previousSteps missing fromStepNames', () => {
|
|
177
183
|
const missingFromSteps = {
|
|
178
184
|
flowTag: 'TestFlow',
|
|
185
|
+
event: ['ownTopic'],
|
|
179
186
|
flowSteps: [
|
|
180
187
|
{
|
|
181
188
|
stepName: 'Start',
|
|
@@ -204,6 +211,7 @@ test('validateFlowSchema rejects extTopic previousSteps missing fromStepNames',
|
|
|
204
211
|
test('validateFlowSchema allows optional omitted nextSteps and previousSteps', () => {
|
|
205
212
|
const minimalSchema = {
|
|
206
213
|
flowTag: 'MinimalFlow',
|
|
214
|
+
event: ['ownTopic'],
|
|
207
215
|
flowSteps: [
|
|
208
216
|
{
|
|
209
217
|
stepName: 'Start'
|
|
@@ -217,6 +225,7 @@ test('validateFlowSchema allows optional omitted nextSteps and previousSteps', (
|
|
|
217
225
|
test('validateFlowSchema passes for lambdaSyncInv with functionName', () => {
|
|
218
226
|
const syncInvSchema = {
|
|
219
227
|
flowTag: 'SyncInvFlow',
|
|
228
|
+
event: ['lambdaSyncInv'],
|
|
220
229
|
flowSteps: [
|
|
221
230
|
{
|
|
222
231
|
stepName: 'Start',
|
|
@@ -237,6 +246,7 @@ test('validateFlowSchema passes for lambdaSyncInv with functionName', () => {
|
|
|
237
246
|
test('validateFlowSchema rejects lambdaSyncInv nextSteps missing functionName', () => {
|
|
238
247
|
const missingFnSchema = {
|
|
239
248
|
flowTag: 'SyncInvFlow',
|
|
249
|
+
event: ['lambdaSyncInv'],
|
|
240
250
|
flowSteps: [
|
|
241
251
|
{
|
|
242
252
|
stepName: 'Start',
|
|
@@ -291,3 +301,318 @@ test('validateFlowSchema rejects flow with synchronous events having multiple fl
|
|
|
291
301
|
/Flow 'ConfirmUsage' with synchronous events .* must only define a single entry step 'Start'/
|
|
292
302
|
);
|
|
293
303
|
});
|
|
304
|
+
|
|
305
|
+
test('validateFlowSchema rejects statusType triggerCache without triggerFlowTypes', () => {
|
|
306
|
+
const invalidSchema = {
|
|
307
|
+
flowTag: 'cart',
|
|
308
|
+
event: ['ownTopic'],
|
|
309
|
+
statusType: 'triggerCache',
|
|
310
|
+
objType: { serviceTag: 'Order' },
|
|
311
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
312
|
+
};
|
|
313
|
+
assert.throws(
|
|
314
|
+
() => validateFlowSchema(invalidSchema),
|
|
315
|
+
/Flow 'cart' with statusType 'triggerCache' requires a non-empty 'triggerFlowTypes' array/
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('validateFlowSchema rejects statusType triggerCache with empty triggerFlowTypes', () => {
|
|
320
|
+
const invalidSchema = {
|
|
321
|
+
flowTag: 'cart',
|
|
322
|
+
event: ['ownTopic'],
|
|
323
|
+
statusType: 'triggerCache',
|
|
324
|
+
triggerFlowTypes: [],
|
|
325
|
+
objType: { serviceTag: 'Order' },
|
|
326
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
327
|
+
};
|
|
328
|
+
assert.throws(
|
|
329
|
+
() => validateFlowSchema(invalidSchema),
|
|
330
|
+
/Flow 'cart' with statusType 'triggerCache' requires a non-empty 'triggerFlowTypes' array/
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('validateFlowSchema rejects statusType triggerCache without objType', () => {
|
|
335
|
+
const invalidSchema = {
|
|
336
|
+
flowTag: 'cart',
|
|
337
|
+
event: ['ownTopic'],
|
|
338
|
+
statusType: 'triggerCache',
|
|
339
|
+
triggerFlowTypes: ['OrderCheckout'],
|
|
340
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
341
|
+
};
|
|
342
|
+
assert.throws(
|
|
343
|
+
() => validateFlowSchema(invalidSchema),
|
|
344
|
+
/Flow 'cart' with statusType 'triggerCache' requires 'objType'/
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('validateFlowSchema rejects statusType storedCache without objType', () => {
|
|
349
|
+
const invalidSchema = {
|
|
350
|
+
flowTag: 'cache',
|
|
351
|
+
event: ['ownTopic'],
|
|
352
|
+
statusType: 'storedCache',
|
|
353
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
354
|
+
};
|
|
355
|
+
assert.throws(
|
|
356
|
+
() => validateFlowSchema(invalidSchema),
|
|
357
|
+
/Flow 'cache' with statusType 'storedCache' requires 'objType'/
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test('validateFlowSchema rejects checktrigger step without triggerFlowTypes', () => {
|
|
362
|
+
const invalidSchema = {
|
|
363
|
+
flowTag: 'customCheck',
|
|
364
|
+
event: ['ownTopic'],
|
|
365
|
+
flowSteps: [
|
|
366
|
+
{ stepName: 'Start' },
|
|
367
|
+
{ stepName: 'CheckTriggerFlows', event: ['extTopic'] }
|
|
368
|
+
]
|
|
369
|
+
};
|
|
370
|
+
assert.throws(
|
|
371
|
+
() => validateFlowSchema(invalidSchema),
|
|
372
|
+
/Flow 'customCheck' defines step 'CheckTriggerFlows' but requires a non-empty 'triggerFlowTypes' array/
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('validateFlowSchema passes for valid triggerCache and storedCache schemas', () => {
|
|
377
|
+
const validTriggerCache = {
|
|
378
|
+
flowTag: 'cart',
|
|
379
|
+
event: ['ownTopic'],
|
|
380
|
+
statusType: 'triggerCache',
|
|
381
|
+
triggerFlowTypes: ['OrderCheckout'],
|
|
382
|
+
objType: { serviceTag: 'Order' },
|
|
383
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
384
|
+
};
|
|
385
|
+
assert.equal(validateFlowSchema(validTriggerCache), true);
|
|
386
|
+
|
|
387
|
+
const validStoredCache = {
|
|
388
|
+
flowTag: 'cache',
|
|
389
|
+
event: ['ownTopic'],
|
|
390
|
+
statusType: 'storedCache',
|
|
391
|
+
objType: { objectType: 'order' },
|
|
392
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
393
|
+
};
|
|
394
|
+
assert.equal(validateFlowSchema(validStoredCache), true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('validateFlowSchema rejects missing or empty flowTag', () => {
|
|
398
|
+
assert.throws(
|
|
399
|
+
() => validateFlowSchema({ event: ['ownTopic'], flowSteps: [{ stepName: 'Start' }] }),
|
|
400
|
+
/flowTag is required and must be a non-empty string/
|
|
401
|
+
);
|
|
402
|
+
assert.throws(
|
|
403
|
+
() => validateFlowSchema({ flowTag: ' ', event: ['ownTopic'], flowSteps: [{ stepName: 'Start' }] }),
|
|
404
|
+
/flowTag is required and must be a non-empty string/
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test('validateFlowSchema rejects missing, non-array, or empty event', () => {
|
|
409
|
+
assert.throws(
|
|
410
|
+
() => validateFlowSchema({ flowTag: 'testFlow', flowSteps: [{ stepName: 'Start' }] }),
|
|
411
|
+
/Flow 'testFlow' requires a non-empty 'event' array/
|
|
412
|
+
);
|
|
413
|
+
assert.throws(
|
|
414
|
+
() => validateFlowSchema({ flowTag: 'testFlow', event: 'ownTopic', flowSteps: [{ stepName: 'Start' }] }),
|
|
415
|
+
/Flow 'testFlow' requires a non-empty 'event' array/
|
|
416
|
+
);
|
|
417
|
+
assert.throws(
|
|
418
|
+
() => validateFlowSchema({ flowTag: 'testFlow', event: [], flowSteps: [{ stepName: 'Start' }] }),
|
|
419
|
+
/Flow 'testFlow' requires a non-empty 'event' array/
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('validateFlowSchema rejects unsupported event in event array', () => {
|
|
424
|
+
assert.throws(
|
|
425
|
+
() => validateFlowSchema({ flowTag: 'testFlow', event: ['invalidEvent'], flowSteps: [{ stepName: 'Start' }] }),
|
|
426
|
+
/event\[0\] 'invalidEvent' is invalid/
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('validateFlowSchema rejects generateCode event without generatedCodeConfig', () => {
|
|
431
|
+
assert.throws(
|
|
432
|
+
() => validateFlowSchema({ flowTag: 'testFlow', event: ['generateCode'], flowSteps: [{ stepName: 'Start' }] }),
|
|
433
|
+
/with event 'generateCode' requires 'generatedCodeConfig'/
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test('validateFlowSchema rejects non-ownTopic hook without codeHookTag', () => {
|
|
438
|
+
assert.throws(
|
|
439
|
+
() =>
|
|
440
|
+
validateFlowSchema({
|
|
441
|
+
flowTag: 'testHook',
|
|
442
|
+
event: ['lambdaSyncInv'],
|
|
443
|
+
generatedCodeConfig: { generateCodeTag: 'create' },
|
|
444
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
445
|
+
}),
|
|
446
|
+
/requires 'codeHookTag' \('before' \| 'after'\)/
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('validateFlowSchema rejects ownTopic hook with generateCodeTag without codeHookTag', () => {
|
|
451
|
+
assert.throws(
|
|
452
|
+
() =>
|
|
453
|
+
validateFlowSchema({
|
|
454
|
+
flowTag: 'testOwnHook',
|
|
455
|
+
event: ['ownTopic'],
|
|
456
|
+
generatedCodeConfig: {
|
|
457
|
+
generateCodeTag: 'create',
|
|
458
|
+
invocationType: 'ownTopic'
|
|
459
|
+
},
|
|
460
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
461
|
+
}),
|
|
462
|
+
/requires 'codeHookTag' \('before' \| 'after'\)/
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test('validateFlowSchema rejects generateCode co-existing with other events', () => {
|
|
467
|
+
assert.throws(
|
|
468
|
+
() =>
|
|
469
|
+
validateFlowSchema({
|
|
470
|
+
flowTag: 'testHook',
|
|
471
|
+
event: ['generateCode', 'ownTopic'],
|
|
472
|
+
generatedCodeConfig: {
|
|
473
|
+
generateCodeTag: 'create',
|
|
474
|
+
codeHookTag: 'before',
|
|
475
|
+
invocationType: 'ownTopic'
|
|
476
|
+
},
|
|
477
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
478
|
+
}),
|
|
479
|
+
/Flow 'testHook' with event 'generateCode' must be a standalone event and cannot co-exist with other events/
|
|
480
|
+
);
|
|
481
|
+
assert.throws(
|
|
482
|
+
() =>
|
|
483
|
+
validateFlowSchema({
|
|
484
|
+
flowTag: 'testHook',
|
|
485
|
+
event: ['extTopic', 'generateCode'],
|
|
486
|
+
generatedCodeConfig: {
|
|
487
|
+
generateCodeTag: 'create',
|
|
488
|
+
codeHookTag: 'after',
|
|
489
|
+
invocationType: 'lambdaSyncInv'
|
|
490
|
+
},
|
|
491
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
492
|
+
}),
|
|
493
|
+
/Flow 'testHook' with event 'generateCode' must be a standalone event and cannot co-exist with other events/
|
|
494
|
+
);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test('validateFlowSchema passes when generateCode is the sole event with valid config', () => {
|
|
498
|
+
assert.equal(
|
|
499
|
+
validateFlowSchema({
|
|
500
|
+
flowTag: 'testHook',
|
|
501
|
+
event: ['generateCode'],
|
|
502
|
+
generatedCodeConfig: {
|
|
503
|
+
generateCodeTag: 'create',
|
|
504
|
+
codeHookTag: 'before',
|
|
505
|
+
invocationType: 'lambdaSyncInv'
|
|
506
|
+
},
|
|
507
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
508
|
+
}),
|
|
509
|
+
true
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('validateFlowSchema rejects s3 event without bucketName', () => {
|
|
514
|
+
assert.throws(
|
|
515
|
+
() =>
|
|
516
|
+
validateFlowSchema({
|
|
517
|
+
flowTag: 'uploadFlow',
|
|
518
|
+
event: ['s3'],
|
|
519
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
520
|
+
}),
|
|
521
|
+
/Flow 'uploadFlow' with event 's3' requires 'bucketName' as a non-empty string/
|
|
522
|
+
);
|
|
523
|
+
assert.throws(
|
|
524
|
+
() =>
|
|
525
|
+
validateFlowSchema({
|
|
526
|
+
flowTag: 'uploadFlow',
|
|
527
|
+
event: ['s3'],
|
|
528
|
+
bucketName: ' ',
|
|
529
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
530
|
+
}),
|
|
531
|
+
/Flow 'uploadFlow' with event 's3' requires 'bucketName' as a non-empty string/
|
|
532
|
+
);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('validateFlowSchema passes for s3 event with valid bucketName', () => {
|
|
536
|
+
assert.equal(
|
|
537
|
+
validateFlowSchema({
|
|
538
|
+
flowTag: 'uploadFlow',
|
|
539
|
+
event: ['s3'],
|
|
540
|
+
bucketName: 'MyUploadBucket',
|
|
541
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
542
|
+
}),
|
|
543
|
+
true
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test('validateFlowSchema rejects event with storedCache or triggerCache as invalid event', () => {
|
|
548
|
+
assert.throws(
|
|
549
|
+
() =>
|
|
550
|
+
validateFlowSchema({
|
|
551
|
+
flowTag: 'cacheFlow',
|
|
552
|
+
event: ['storedCache'],
|
|
553
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
554
|
+
}),
|
|
555
|
+
/Flow 'cacheFlow' event\[0\] 'storedCache' is invalid\. Must be one of/
|
|
556
|
+
);
|
|
557
|
+
assert.throws(
|
|
558
|
+
() =>
|
|
559
|
+
validateFlowSchema({
|
|
560
|
+
flowTag: 'triggerFlow',
|
|
561
|
+
event: ['triggerCache'],
|
|
562
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
563
|
+
}),
|
|
564
|
+
/Flow 'triggerFlow' event\[0\] 'triggerCache' is invalid\. Must be one of/
|
|
565
|
+
);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
test('validateFlowSchema rejects statusType storedCache with invalid objType object', () => {
|
|
569
|
+
assert.throws(
|
|
570
|
+
() =>
|
|
571
|
+
validateFlowSchema({
|
|
572
|
+
flowTag: 'cacheFlow',
|
|
573
|
+
event: ['ownTopic'],
|
|
574
|
+
statusType: 'storedCache',
|
|
575
|
+
objType: {},
|
|
576
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
577
|
+
}),
|
|
578
|
+
/Flow 'cacheFlow' with statusType 'storedCache' requires 'objType' to be a non-empty string or object with string 'objectType'/
|
|
579
|
+
);
|
|
580
|
+
assert.throws(
|
|
581
|
+
() =>
|
|
582
|
+
validateFlowSchema({
|
|
583
|
+
flowTag: 'cacheFlow',
|
|
584
|
+
event: ['ownTopic'],
|
|
585
|
+
statusType: 'storedCache',
|
|
586
|
+
objType: { objectType: ' ' },
|
|
587
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
588
|
+
}),
|
|
589
|
+
/Flow 'cacheFlow' with statusType 'storedCache' requires 'objType' to be a non-empty string or object with string 'objectType'/
|
|
590
|
+
);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test('validateFlowSchema passes statusType storedCache with string objType or object objType', () => {
|
|
594
|
+
assert.equal(
|
|
595
|
+
validateFlowSchema({
|
|
596
|
+
flowTag: 'cacheFlow1',
|
|
597
|
+
event: ['ownTopic'],
|
|
598
|
+
statusType: 'storedCache',
|
|
599
|
+
objType: 'order',
|
|
600
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
601
|
+
}),
|
|
602
|
+
true
|
|
603
|
+
);
|
|
604
|
+
assert.equal(
|
|
605
|
+
validateFlowSchema({
|
|
606
|
+
flowTag: 'cacheFlow2',
|
|
607
|
+
event: ['ownTopic'],
|
|
608
|
+
statusType: 'storedCache',
|
|
609
|
+
objType: { objectType: 'order' },
|
|
610
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
611
|
+
}),
|
|
612
|
+
true
|
|
613
|
+
);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
|
|
@@ -125,6 +125,35 @@ test('emits relationship action and complete handlers with data-first function Y
|
|
|
125
125
|
assert.match(yaml, /CreateRelationshipCompleteHdrSqs:/);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
+
test('emits valid hook tags without trailing periods and with correct callingFlowProperties placement in relationship Main files', async () => {
|
|
129
|
+
const rootPath = await createProject(true);
|
|
130
|
+
await generateRelationshipApplicationArtifacts(rootPath, {
|
|
131
|
+
serviceTag: 'orders'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const actions = ['Create', 'Update', 'Delete', 'Move', 'Change'];
|
|
135
|
+
for (const action of actions) {
|
|
136
|
+
const mainContent = await readFile(
|
|
137
|
+
join(
|
|
138
|
+
rootPath,
|
|
139
|
+
`app/src/generatedCode/FlowRelationships/${action}Relationship/source/${action}Relationship_Main.js`
|
|
140
|
+
),
|
|
141
|
+
'utf8'
|
|
142
|
+
);
|
|
143
|
+
assert.doesNotMatch(mainContent, /\/\/\(<\/[^>]+>\)\./);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const changeMain = await readFile(
|
|
147
|
+
join(
|
|
148
|
+
rootPath,
|
|
149
|
+
'app/src/generatedCode/FlowRelationships/ChangeRelationship/source/ChangeRelationship_Main.js'
|
|
150
|
+
),
|
|
151
|
+
'utf8'
|
|
152
|
+
);
|
|
153
|
+
assert.match(changeMain, /\/\/\(<requestParamChangeRel>\)/);
|
|
154
|
+
assert.doesNotMatch(changeMain, /\/\/\(<requestParamCreateRel>\)/);
|
|
155
|
+
});
|
|
156
|
+
|
|
128
157
|
test('removes managed relationship artifacts after relationships are removed', async () => {
|
|
129
158
|
const rootPath = await createProject(true);
|
|
130
159
|
await generateRelationshipApplicationArtifacts(rootPath, {
|
|
@@ -236,6 +236,51 @@ test('rejects empty generateCodeTag array configuration', async () => {
|
|
|
236
236
|
);
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
test('rejects FlowHook schema missing codeHookTag at loader', async () => {
|
|
240
|
+
const rootPath = await createProject();
|
|
241
|
+
const flowDirectory = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
242
|
+
await writeFile(
|
|
243
|
+
join(flowDirectory, 'MissingHookTag.js'),
|
|
244
|
+
"export default { flowTag: 'missingHookTag', event: ['lambdaSyncInv'], " +
|
|
245
|
+
"generatedCodeConfig: { generateCodeTag: 'create' } };\n"
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
await assert.rejects(
|
|
249
|
+
generateResourceArtifacts(rootPath),
|
|
250
|
+
/Flow 'missingHookTag' with generatedCodeConfig requires 'codeHookTag' \('before' or 'after'\)/
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('rejects ownTopic FlowHook schema missing codeHookTag at loader', async () => {
|
|
255
|
+
const rootPath = await createProject();
|
|
256
|
+
const flowDirectory = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
257
|
+
await writeFile(
|
|
258
|
+
join(flowDirectory, 'MissingOwnHookTag.js'),
|
|
259
|
+
"export default { flowTag: 'missingOwnHookTag', event: ['ownTopic'], " +
|
|
260
|
+
"generatedCodeConfig: { generateCodeTag: 'create', invocationType: 'ownTopic' } };\n"
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
await assert.rejects(
|
|
264
|
+
generateResourceArtifacts(rootPath),
|
|
265
|
+
/Flow 'missingOwnHookTag' with generatedCodeConfig requires 'codeHookTag' \('before' or 'after'\)/
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('rejects FlowHook schema with invalid codeHookTag at loader', async () => {
|
|
270
|
+
const rootPath = await createProject();
|
|
271
|
+
const flowDirectory = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
272
|
+
await writeFile(
|
|
273
|
+
join(flowDirectory, 'InvalidCodeHookTag.js'),
|
|
274
|
+
"export default { flowTag: 'invalidCodeHookTag', event: ['lambdaSyncInv'], " +
|
|
275
|
+
"generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'middle' } };\n"
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
await assert.rejects(
|
|
279
|
+
generateResourceArtifacts(rootPath),
|
|
280
|
+
/generatedCodeConfig\.codeHookTag 'middle' is invalid\. Must be 'before' or 'after'/
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
239
284
|
test('emits the shared WebSocket completion queue and output-topic subscriptions', async () => {
|
|
240
285
|
const rootPath = await createProject();
|
|
241
286
|
await generateResourceArtifacts(rootPath);
|
|
@@ -807,7 +852,7 @@ async function createProject() {
|
|
|
807
852
|
);
|
|
808
853
|
await writeFile(
|
|
809
854
|
join(schemas, 'FlowSchemas/Upload.js'),
|
|
810
|
-
"export default { flowTag: 'upload', event: ['s3'], outputTopic: true, s3Subscription: { topicName: 'OutCreate', queueName: 'UploadReservedLimitHdrSqs' } };\n"
|
|
855
|
+
"export default { flowTag: 'upload', event: ['s3'], bucketName: 'UploadBucket', outputTopic: true, s3Subscription: { topicName: 'OutCreate', queueName: 'UploadReservedLimitHdrSqs' } };\n"
|
|
811
856
|
);
|
|
812
857
|
return rootPath;
|
|
813
858
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { emitSystemFindDataArtifacts } from '../src/emit/system/emitSystemFindDataArtifacts.js';
|
|
5
|
+
|
|
6
|
+
test('emitSystemFindDataArtifacts returns empty when no objects have storage resources', async () => {
|
|
7
|
+
const result = await emitSystemFindDataArtifacts([
|
|
8
|
+
{ objectType: 'plainObject' }
|
|
9
|
+
]);
|
|
10
|
+
assert.deepEqual(result.artifacts, []);
|
|
11
|
+
assert.deepEqual(result.functions, []);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('emitSystemFindDataArtifacts omits FindData handlers and YAML function when no fields qualify for find data', async () => {
|
|
15
|
+
const objects = [
|
|
16
|
+
{
|
|
17
|
+
objectType: 'item',
|
|
18
|
+
storageResources: {
|
|
19
|
+
main: { storageType: 'dynamoDB', tableName: 'Items' }
|
|
20
|
+
},
|
|
21
|
+
fieldNames: {
|
|
22
|
+
description: { type: 'boolean' }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const result = await emitSystemFindDataArtifacts(objects);
|
|
28
|
+
assert.ok(result.artifacts.length > 0);
|
|
29
|
+
assert.ok(
|
|
30
|
+
!result.artifacts.some((a) => a.path.includes('FindData')),
|
|
31
|
+
'FindData artifacts must not be emitted when no fields qualify'
|
|
32
|
+
);
|
|
33
|
+
assert.ok(
|
|
34
|
+
!result.functions.some((fn) => fn.includes('FindDataHdrSqs:')),
|
|
35
|
+
'FindData YAML function must not be emitted when no fields qualify'
|
|
36
|
+
);
|
|
37
|
+
assert.ok(
|
|
38
|
+
result.functions.some((fn) => fn.includes('ProcessLogicalHdrSqs:')),
|
|
39
|
+
'ProcessLogical functions should still be emitted'
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('emitSystemFindDataArtifacts includes FindData handlers and YAML function when fields qualify', async () => {
|
|
44
|
+
const objects = [
|
|
45
|
+
{
|
|
46
|
+
objectType: 'product',
|
|
47
|
+
storageResources: {
|
|
48
|
+
main: { storageType: 'dynamoDB', tableName: 'Products' }
|
|
49
|
+
},
|
|
50
|
+
identifiers: [{ fieldName: 'productId' }],
|
|
51
|
+
fieldNames: {
|
|
52
|
+
productId: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
storageResourceTags: ['main']
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const result = await emitSystemFindDataArtifacts(objects);
|
|
61
|
+
assert.ok(
|
|
62
|
+
result.artifacts.some((a) => a.path.includes('FindData_Main.js')),
|
|
63
|
+
'FindData_Main.js must be emitted'
|
|
64
|
+
);
|
|
65
|
+
assert.ok(
|
|
66
|
+
result.artifacts.some((a) => a.path.includes('FindData_HdrSqs.js')),
|
|
67
|
+
'FindData_HdrSqs.js must be emitted'
|
|
68
|
+
);
|
|
69
|
+
assert.ok(
|
|
70
|
+
result.functions.some((fn) => fn.includes('FindDataHdrSqs:')),
|
|
71
|
+
'FindDataHdrSqs YAML function must be emitted'
|
|
72
|
+
);
|
|
73
|
+
});
|