@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/test/core.test.js
CHANGED
|
@@ -34,6 +34,19 @@ test('compiles a request and commits only declared relative artifacts', async ()
|
|
|
34
34
|
test('rejects invalid generation inputs before writing files', async () => {
|
|
35
35
|
assert.throws(() => compileProject({}), /rootPath/);
|
|
36
36
|
|
|
37
|
+
const corruptPath = await mkdtemp(
|
|
38
|
+
join(tmpdir(), 'generate-service-code-v2-corrupt-yml-')
|
|
39
|
+
);
|
|
40
|
+
await mkdir(join(corruptPath, 'config'), { recursive: true });
|
|
41
|
+
await writeFile(
|
|
42
|
+
join(corruptPath, 'config/serverless.config.yml'),
|
|
43
|
+
'main_config: [unbalanced brackets:'
|
|
44
|
+
);
|
|
45
|
+
assert.throws(
|
|
46
|
+
() => compileProject({ rootPath: corruptPath }),
|
|
47
|
+
/Failed to read or parse configuration at/
|
|
48
|
+
);
|
|
49
|
+
|
|
37
50
|
const outputPath = await mkdtemp(
|
|
38
51
|
join(tmpdir(), 'generate-service-code-v2-invalid-')
|
|
39
52
|
);
|
|
@@ -88,6 +88,15 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
|
|
|
88
88
|
assert.match(iam, /ApiRole:/);
|
|
89
89
|
assert.match(iam, /OwnRole:/);
|
|
90
90
|
assert.match(iam, /UploadRole:/);
|
|
91
|
+
const customSqsYaml = await readFile(
|
|
92
|
+
join(
|
|
93
|
+
rootPath,
|
|
94
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-custom-flows.yml'
|
|
95
|
+
),
|
|
96
|
+
'utf8'
|
|
97
|
+
);
|
|
98
|
+
assert.match(customSqsYaml, /ExtHdrSqs:/);
|
|
99
|
+
assert.doesNotMatch(customSqsYaml, /ProcessExtHdrSqs/);
|
|
91
100
|
assert.match(
|
|
92
101
|
await readFile(
|
|
93
102
|
join(
|
|
@@ -254,7 +263,7 @@ test('removes only V2-owned custom-flow application artifacts when supported eve
|
|
|
254
263
|
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
255
264
|
await writeFile(
|
|
256
265
|
join(rootPath, 'app/src/schemas/FlowSchemas/Flows.js'),
|
|
257
|
-
"export default [
|
|
266
|
+
"export default [];\n"
|
|
258
267
|
);
|
|
259
268
|
|
|
260
269
|
assert.deepEqual(
|
|
@@ -469,6 +478,53 @@ test('emits and removes the trigger-cache artifact suite when triggerFlowTypes i
|
|
|
469
478
|
);
|
|
470
479
|
});
|
|
471
480
|
|
|
481
|
+
test('triggerCache entrypoint Cart_Main.js contains correct library import and logic', async () => {
|
|
482
|
+
const rootPath = await createTriggerCacheProject(['OrderCheckout']);
|
|
483
|
+
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
484
|
+
|
|
485
|
+
const source = 'app/src/generatedCode/FlowSchemas/Cart/source';
|
|
486
|
+
const mainContent = await readFile(
|
|
487
|
+
join(rootPath, `${source}/Cart_Main.js`),
|
|
488
|
+
'utf8'
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
assert.match(
|
|
492
|
+
mainContent,
|
|
493
|
+
/import \{ triggeredCacheSharedLibV2 \} from '@izara_project\/izara-core-library-trigger-cache'/,
|
|
494
|
+
'should import triggeredCacheSharedLibV2'
|
|
495
|
+
);
|
|
496
|
+
assert.match(
|
|
497
|
+
mainContent,
|
|
498
|
+
/triggeredCacheSharedLibV2\.checkTriggerResetedV2/,
|
|
499
|
+
'should call checkTriggerResetedV2'
|
|
500
|
+
);
|
|
501
|
+
assert.match(
|
|
502
|
+
mainContent,
|
|
503
|
+
/triggeredCacheSharedLibV2\.checkTriggeredCacheV2/,
|
|
504
|
+
'should call checkTriggeredCacheV2'
|
|
505
|
+
);
|
|
506
|
+
assert.match(
|
|
507
|
+
mainContent,
|
|
508
|
+
/\/\/\(<beforeCheckTriggeredCache>\)/,
|
|
509
|
+
'should have beforeCheckTriggeredCache hook'
|
|
510
|
+
);
|
|
511
|
+
assert.match(
|
|
512
|
+
mainContent,
|
|
513
|
+
/\/\/\(<dispatchTriggerFlowHook>\)/,
|
|
514
|
+
'should have dispatchTriggerFlowHook hook'
|
|
515
|
+
);
|
|
516
|
+
assert.match(
|
|
517
|
+
mainContent,
|
|
518
|
+
/if \(triggeredCacheResult\?\.hasPendingTasks\)/,
|
|
519
|
+
'should guard dispatch with hasPendingTasks check'
|
|
520
|
+
);
|
|
521
|
+
assert.doesNotMatch(
|
|
522
|
+
mainContent,
|
|
523
|
+
/checkStoredCacheStatus/,
|
|
524
|
+
'should not contain storedCache logic'
|
|
525
|
+
);
|
|
526
|
+
});
|
|
527
|
+
|
|
472
528
|
test('uses custom CheckTrigger step if provided by developer in flowSteps', async () => {
|
|
473
529
|
const rootPath = await mkdtemp(
|
|
474
530
|
join(tmpdir(), 'generate-service-code-v2-trigger-cache-custom-')
|
|
@@ -494,28 +550,77 @@ test('uses custom CheckTrigger step if provided by developer in flowSteps', asyn
|
|
|
494
550
|
assert.match(flowYaml, /^CartCheckTriggerCustomStepHdrSqs:/m);
|
|
495
551
|
});
|
|
496
552
|
|
|
497
|
-
test('
|
|
553
|
+
test('uses full {flowTag}{stepName} queue name when stepName starts with flowTag', async () => {
|
|
554
|
+
const rootPath = await createTriggerCacheProject(['OrderCheckout']);
|
|
555
|
+
await writeFile(
|
|
556
|
+
join(rootPath, 'app/src/schemas/FlowSchemas/Cart.js'),
|
|
557
|
+
"export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'triggerCache', triggerFlowTypes: ['OrderCheckout'], objType: { serviceTag: 'Order' }, flowSteps: [{ stepName: 'start' }, { stepName: 'CartCheckX', event: ['extTopic'] }] };\n"
|
|
558
|
+
);
|
|
559
|
+
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
560
|
+
await generateResourceArtifacts(rootPath);
|
|
561
|
+
|
|
562
|
+
const source = 'app/src/generatedCode/FlowSchemas/Cart/source';
|
|
563
|
+
for (const fileName of [
|
|
564
|
+
'CartCartCheckX_Main.js',
|
|
565
|
+
'CartCartCheckX_HdrSqs.js'
|
|
566
|
+
]) {
|
|
567
|
+
const path = join(rootPath, source, fileName);
|
|
568
|
+
await access(path);
|
|
569
|
+
await parseAsEsm(path);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const flowYaml = await readFile(
|
|
573
|
+
join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'),
|
|
574
|
+
'utf8'
|
|
575
|
+
);
|
|
576
|
+
assert.match(flowYaml, /^CartCartCheckXHdrSqs:/m);
|
|
577
|
+
assert.match(
|
|
578
|
+
await readFile(join(rootPath, `${source}/CartCartCheckX_HdrSqs.js`), 'utf8'),
|
|
579
|
+
/CartCartCheckXHdrSqs/
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
const resources = await readFile(
|
|
583
|
+
join(
|
|
584
|
+
rootPath,
|
|
585
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-custom-flows.yml'
|
|
586
|
+
),
|
|
587
|
+
'utf8'
|
|
588
|
+
);
|
|
589
|
+
assert.match(
|
|
590
|
+
resources,
|
|
591
|
+
new RegExp(
|
|
592
|
+
'QueueName: \\$\\{self:custom\\.iz_resourcePrefix\\}CartCartCheckXHdrSqs'
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test('rejects when statusType triggerCache has triggerFlowTypes empty, null, undefined, or missing', async () => {
|
|
498
598
|
for (const emptyTriggerFlowTypes of [[], null, undefined]) {
|
|
499
599
|
const rootPath = await createTriggerCacheProject(emptyTriggerFlowTypes);
|
|
500
|
-
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
501
|
-
await generateResourceArtifacts(rootPath);
|
|
502
|
-
|
|
503
|
-
const source = 'app/src/generatedCode/FlowSchemas/Cart/source';
|
|
504
|
-
await assert.rejects(
|
|
505
|
-
readFile(join(rootPath, `${source}/CartCheckTriggerFlows_Main.js`))
|
|
506
|
-
);
|
|
507
600
|
await assert.rejects(
|
|
508
|
-
|
|
601
|
+
generateCustomFlowApplicationArtifacts(rootPath),
|
|
602
|
+
/FlowSchema validation error.*triggerCache.*requires a non-empty 'triggerFlowTypes' array/
|
|
509
603
|
);
|
|
510
|
-
|
|
511
|
-
const flowYaml = await readFile(
|
|
512
|
-
join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'),
|
|
513
|
-
'utf8'
|
|
514
|
-
);
|
|
515
|
-
assert.doesNotMatch(flowYaml, /CartCheckTriggerFlowsHdrSqs/);
|
|
516
604
|
}
|
|
517
605
|
});
|
|
518
606
|
|
|
607
|
+
test('rejects when statusType storedCache cannot resolve DynamoDB table from objType', async () => {
|
|
608
|
+
const rootPath = await mkdtemp(
|
|
609
|
+
join(tmpdir(), 'generate-service-code-v2-stored-cache-err-')
|
|
610
|
+
);
|
|
611
|
+
roots.add(rootPath);
|
|
612
|
+
const schemaRoot = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
613
|
+
await mkdir(schemaRoot, { recursive: true });
|
|
614
|
+
await writeFile(
|
|
615
|
+
join(schemaRoot, 'CacheFlow.js'),
|
|
616
|
+
"export default { flowTag: 'cacheFlow', event: ['ownTopic'], statusType: 'storedCache', objType: { objectType: 'missingObj' }, flowSteps: [{ stepName: 'Start' }] };\n"
|
|
617
|
+
);
|
|
618
|
+
await assert.rejects(
|
|
619
|
+
generateCustomFlowApplicationArtifacts(rootPath),
|
|
620
|
+
/FlowSchema validation error.*storedCache.*cannot resolve DynamoDB tableName/
|
|
621
|
+
);
|
|
622
|
+
});
|
|
623
|
+
|
|
519
624
|
test('rejects an EventBridge schedule without a deployable name and rate', () => {
|
|
520
625
|
assert.throws(
|
|
521
626
|
() =>
|
|
@@ -565,7 +670,7 @@ export default [
|
|
|
565
670
|
{ ...base, flowTag: 'cache', event: ['ownTopic'], statusType: 'storedCache', objType: { objectType: 'order', serviceTag: 'Current' } },
|
|
566
671
|
{ ...base, flowTag: 'mixed', event: ['ownTopic', 'extTopic'] },
|
|
567
672
|
{ ...base, flowTag: 'generated', event: ['ownTopic'], generatedCodeConfig: { generatedCodeTag: 'create' } },
|
|
568
|
-
{ ...base, flowTag: 'upload', event: ['s3'] }
|
|
673
|
+
{ ...base, flowTag: 'upload', event: ['s3'], bucketName: 'UploadBucket' }
|
|
569
674
|
];
|
|
570
675
|
`
|
|
571
676
|
);
|
|
@@ -1271,3 +1376,166 @@ test('emits handler and YAML for flow with event generateCode and invocationType
|
|
|
1271
1376
|
assert.match(yaml, /GenSyncHdrInv:/);
|
|
1272
1377
|
});
|
|
1273
1378
|
|
|
1379
|
+
test('rejects FlowSchema with event storedCache at loader', async () => {
|
|
1380
|
+
const rootPath = await createProject();
|
|
1381
|
+
await writeFile(
|
|
1382
|
+
join(rootPath, 'app/src/schemas/FlowSchemas/BadEvent.js'),
|
|
1383
|
+
`export default {
|
|
1384
|
+
flowTag: 'badEvent',
|
|
1385
|
+
event: ['storedCache'],
|
|
1386
|
+
statusType: 'storedCache',
|
|
1387
|
+
objType: { objectType: 'order' },
|
|
1388
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
1389
|
+
};\n`
|
|
1390
|
+
);
|
|
1391
|
+
|
|
1392
|
+
await assert.rejects(
|
|
1393
|
+
() => generateCustomFlowApplicationArtifacts(rootPath),
|
|
1394
|
+
/Flow 'badEvent' event\[0\] 'storedCache' is invalid\. Must be one of/
|
|
1395
|
+
);
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
test('emits early return cacheMain on cache hit in FlowEndpoint_Main for storedCache', async () => {
|
|
1399
|
+
const rootPath = await createProject();
|
|
1400
|
+
await writeFile(
|
|
1401
|
+
join(rootPath, 'app/src/schemas/FlowSchemas/CacheFlow.js'),
|
|
1402
|
+
`export default {
|
|
1403
|
+
flowTag: 'cacheOrder',
|
|
1404
|
+
event: ['ownTopic'],
|
|
1405
|
+
statusType: 'storedCache',
|
|
1406
|
+
objType: { objectType: 'order', serviceTag: 'Current' },
|
|
1407
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
1408
|
+
};\n`
|
|
1409
|
+
);
|
|
1410
|
+
|
|
1411
|
+
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
1412
|
+
|
|
1413
|
+
const mainPath = join(
|
|
1414
|
+
rootPath,
|
|
1415
|
+
'app/src/generatedCode/FlowSchemas/CacheOrder/source/CacheOrder_Main.js'
|
|
1416
|
+
);
|
|
1417
|
+
const mainContent = await readFile(mainPath, 'utf8');
|
|
1418
|
+
assert.match(mainContent, /checkStoredCacheStatus/);
|
|
1419
|
+
assert.match(mainContent, /if \(cacheMainStatus === 'complete'\) \{/);
|
|
1420
|
+
assert.match(mainContent, /return cacheMain;/);
|
|
1421
|
+
await parseAsEsm(mainPath);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
test('does not emit RbacCallback artifacts or duplicate artifact paths for custom flow with RBAC', async () => {
|
|
1425
|
+
const { generate } = await import('../src/generate.js');
|
|
1426
|
+
const rootPath = await createProject();
|
|
1427
|
+
await writeFile(
|
|
1428
|
+
join(rootPath, 'app/src/schemas/ObjectSchemas/ComAcc.js'),
|
|
1429
|
+
`export default {
|
|
1430
|
+
objectType: 'comAcc',
|
|
1431
|
+
belongTo: {
|
|
1432
|
+
objectType: 'user',
|
|
1433
|
+
serviceTag: 'UserAccount'
|
|
1434
|
+
}
|
|
1435
|
+
};\n`
|
|
1436
|
+
);
|
|
1437
|
+
await writeFile(
|
|
1438
|
+
join(
|
|
1439
|
+
rootPath,
|
|
1440
|
+
'app/src/schemas/FlowSchemas/CreateBusinessToComAcc.js'
|
|
1441
|
+
),
|
|
1442
|
+
`export default {
|
|
1443
|
+
flowTag: 'createBusinessToComAcc',
|
|
1444
|
+
event: ['lambdaSyncInv', 'ownTopic'],
|
|
1445
|
+
objType: {
|
|
1446
|
+
objectType: 'comAcc',
|
|
1447
|
+
serviceTag: 'ComAcc'
|
|
1448
|
+
},
|
|
1449
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
1450
|
+
};\n`
|
|
1451
|
+
);
|
|
1452
|
+
|
|
1453
|
+
const result = await generate(rootPath, {
|
|
1454
|
+
serviceTag: 'BusinessService'
|
|
1455
|
+
});
|
|
1456
|
+
assert.ok(result);
|
|
1457
|
+
|
|
1458
|
+
const sourceDir = join(
|
|
1459
|
+
rootPath,
|
|
1460
|
+
'app/src/generatedCode/FlowSchemas/CreateBusinessToComAcc/source'
|
|
1461
|
+
);
|
|
1462
|
+
const mainPath = join(sourceDir, 'CreateBusinessToComAcc_Main.js');
|
|
1463
|
+
const mainContent = await readFile(mainPath, 'utf8');
|
|
1464
|
+
assert.match(mainContent, /checkRbacPermission/);
|
|
1465
|
+
assert.doesNotMatch(mainContent, /CreateBusinessToComAccRbacCallback/);
|
|
1466
|
+
|
|
1467
|
+
await assert.rejects(
|
|
1468
|
+
() =>
|
|
1469
|
+
readFile(
|
|
1470
|
+
join(sourceDir, 'CreateBusinessToComAccRbacCallback_Main.js'),
|
|
1471
|
+
'utf8'
|
|
1472
|
+
),
|
|
1473
|
+
{ code: 'ENOENT' }
|
|
1474
|
+
);
|
|
1475
|
+
await assert.rejects(
|
|
1476
|
+
() =>
|
|
1477
|
+
readFile(
|
|
1478
|
+
join(sourceDir, 'CreateBusinessToComAccRbacCallback_HdrSqs.js'),
|
|
1479
|
+
'utf8'
|
|
1480
|
+
),
|
|
1481
|
+
{ code: 'ENOENT' }
|
|
1482
|
+
);
|
|
1483
|
+
|
|
1484
|
+
const initialSetupConfigContent = await readFile(
|
|
1485
|
+
join(
|
|
1486
|
+
rootPath,
|
|
1487
|
+
'app/src/generatedCode/CoreFunctions/InitialSetupConfig.js'
|
|
1488
|
+
),
|
|
1489
|
+
'utf8'
|
|
1490
|
+
);
|
|
1491
|
+
assert.doesNotMatch(
|
|
1492
|
+
initialSetupConfigContent,
|
|
1493
|
+
/CreateBusinessToComAccRbacCallbackHdrSqs/
|
|
1494
|
+
);
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
test('skips checkRbacPermission in FlowEndpoint_Main when initiateBy is systemNoUser', async () => {
|
|
1498
|
+
const { generate } = await import('../src/generate.js');
|
|
1499
|
+
const rootPath = await createProject();
|
|
1500
|
+
await writeFile(
|
|
1501
|
+
join(rootPath, 'app/src/schemas/ObjectSchemas/ComAcc.js'),
|
|
1502
|
+
`export default {
|
|
1503
|
+
objectType: 'comAcc',
|
|
1504
|
+
belongTo: {
|
|
1505
|
+
objectType: 'user',
|
|
1506
|
+
serviceTag: 'UserAccount'
|
|
1507
|
+
}
|
|
1508
|
+
};\n`
|
|
1509
|
+
);
|
|
1510
|
+
await writeFile(
|
|
1511
|
+
join(
|
|
1512
|
+
rootPath,
|
|
1513
|
+
'app/src/schemas/FlowSchemas/SystemCronFlow.js'
|
|
1514
|
+
),
|
|
1515
|
+
`export default {
|
|
1516
|
+
flowTag: 'systemCronFlow',
|
|
1517
|
+
event: ['ownTopic'],
|
|
1518
|
+
initiateBy: 'systemNoUser',
|
|
1519
|
+
objType: {
|
|
1520
|
+
objectType: 'comAcc',
|
|
1521
|
+
serviceTag: 'ComAcc'
|
|
1522
|
+
},
|
|
1523
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
1524
|
+
};\n`
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
const result = await generate(rootPath, {
|
|
1528
|
+
serviceTag: 'BusinessService'
|
|
1529
|
+
});
|
|
1530
|
+
assert.ok(result);
|
|
1531
|
+
|
|
1532
|
+
const mainPath = join(
|
|
1533
|
+
rootPath,
|
|
1534
|
+
'app/src/generatedCode/FlowSchemas/SystemCronFlow/source/SystemCronFlow_Main.js'
|
|
1535
|
+
);
|
|
1536
|
+
const mainContent = await readFile(mainPath, 'utf8');
|
|
1537
|
+
assert.doesNotMatch(mainContent, /checkRbacPermission/);
|
|
1538
|
+
await parseAsEsm(mainPath);
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1541
|
+
|