@izara_project/izara-core-generate-service-code 1.0.87 → 1.0.89
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/flowValidator.js +25 -0
- package/src/compile/loadSchemas.js +199 -10
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
- package/src/emit/crud/crudActionPlan.js +105 -6
- package/src/emit/crud/emitCrudApplicationArtifacts.js +63 -3
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
- package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +121 -0
- package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +108 -0
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +140 -340
- package/src/emit/crud/templates/CreateLogicalLib.ejs +338 -82
- package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +90 -0
- package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
- package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +99 -0
- package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +151 -179
- package/src/emit/crud/templates/DeleteLogicalLib.ejs +244 -27
- package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
- package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +112 -0
- package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +166 -185
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +123 -3
- package/src/emit/custom/customFlows.js +3 -1
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
- package/src/emit/resource/emitResourceArtifacts.js +119 -59
- package/test/crudApplicationArtifacts.test.js +876 -0
- package/test/dynamicCrudFlowSchemas.test.js +10 -5
- package/test/dynamicRbacFlowSchemas.test.js +24 -2
- package/test/graphDrivenFlowSchema.test.js +36 -0
- package/test/resourceArtifacts.test.js +271 -12
|
@@ -142,7 +142,7 @@ test('generates a direct Complete step for RBAC-only Update flows', async () =>
|
|
|
142
142
|
assert.equal(update.flowSteps[1].properties.length, 0);
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
-
test('generates CreateNodeCallback
|
|
145
|
+
test('generates CreateNodeCallback for Create when RBAC and Graph are present', async () => {
|
|
146
146
|
const rootPath = await createProject({
|
|
147
147
|
generatedBy: 'user',
|
|
148
148
|
});
|
|
@@ -160,8 +160,14 @@ test('generates CreateNodeCallback and Complete for Create when RBAC and Graph a
|
|
|
160
160
|
const create = await readGeneratedFlow(rootPath, 'Create');
|
|
161
161
|
assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
|
|
162
162
|
'Start',
|
|
163
|
-
'CreateNodeCallback'
|
|
164
|
-
|
|
163
|
+
'CreateNodeCallback'
|
|
164
|
+
]);
|
|
165
|
+
assert.deepEqual(create.flowSteps[1].nextSteps, [
|
|
166
|
+
{
|
|
167
|
+
targetType: 'lambdaSyncInv',
|
|
168
|
+
serviceTag: 'Rbac',
|
|
169
|
+
functionName: 'ConfirmUsageHdrInv'
|
|
170
|
+
}
|
|
165
171
|
]);
|
|
166
172
|
});
|
|
167
173
|
|
|
@@ -185,8 +191,7 @@ test('uses CreateNodeCallback step when Graph storage is present with sync RBAC'
|
|
|
185
191
|
const create = await readGeneratedFlow(rootPath, 'Create');
|
|
186
192
|
assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
|
|
187
193
|
'Start',
|
|
188
|
-
'CreateNodeCallback'
|
|
189
|
-
'Complete'
|
|
194
|
+
'CreateNodeCallback'
|
|
190
195
|
]);
|
|
191
196
|
assert.deepEqual(create.flowSteps[1].properties, [
|
|
192
197
|
'returnValue',
|
|
@@ -69,6 +69,27 @@ test('generates shared and object-specific RBAC flow schemas', async () => {
|
|
|
69
69
|
});
|
|
70
70
|
assert.deepEqual(user.event, ['ownTopic']);
|
|
71
71
|
assert.equal(user.steps, undefined);
|
|
72
|
+
|
|
73
|
+
const confirmUsage = await readGeneratedFlow(
|
|
74
|
+
rootPath,
|
|
75
|
+
'ConfirmUsage',
|
|
76
|
+
'FlowLimit'
|
|
77
|
+
);
|
|
78
|
+
const cancelUsage = await readGeneratedFlow(
|
|
79
|
+
rootPath,
|
|
80
|
+
'CancelUsage',
|
|
81
|
+
'FlowLimit'
|
|
82
|
+
);
|
|
83
|
+
assert.deepEqual(confirmUsage.event, [
|
|
84
|
+
'ownTopic',
|
|
85
|
+
'lambdaSyncInv',
|
|
86
|
+
'lambdaSyncApi'
|
|
87
|
+
]);
|
|
88
|
+
assert.deepEqual(cancelUsage.event, [
|
|
89
|
+
'ownTopic',
|
|
90
|
+
'lambdaSyncInv',
|
|
91
|
+
'lambdaSyncApi'
|
|
92
|
+
]);
|
|
72
93
|
});
|
|
73
94
|
|
|
74
95
|
test('removes RBAC schemas when the last RBAC object is removed', async () => {
|
|
@@ -144,10 +165,11 @@ async function createProject({ hasRbac = false } = {}) {
|
|
|
144
165
|
return rootPath;
|
|
145
166
|
}
|
|
146
167
|
|
|
147
|
-
async function readGeneratedFlow(rootPath, flowTag) {
|
|
168
|
+
async function readGeneratedFlow(rootPath, flowTag, folder = 'FlowRbac') {
|
|
148
169
|
const filePath = join(
|
|
149
170
|
rootPath,
|
|
150
|
-
'app/src/schemas/FlowSchemas/generateCode
|
|
171
|
+
'app/src/schemas/FlowSchemas/generateCode',
|
|
172
|
+
folder,
|
|
151
173
|
`${flowTag}.js`
|
|
152
174
|
);
|
|
153
175
|
const module = await import(
|
|
@@ -255,3 +255,39 @@ test('validateFlowSchema rejects lambdaSyncInv nextSteps missing functionName',
|
|
|
255
255
|
/lambdaSyncInv target requires non-empty 'functionName'/
|
|
256
256
|
);
|
|
257
257
|
});
|
|
258
|
+
|
|
259
|
+
test('validateFlowSchema passes for mixed sync and async events with single Start step', () => {
|
|
260
|
+
const mixedSingleStepSchema = {
|
|
261
|
+
flowTag: 'ConfirmUsage',
|
|
262
|
+
event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi'],
|
|
263
|
+
flowSteps: [
|
|
264
|
+
{
|
|
265
|
+
stepName: 'Start'
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
assert.equal(validateFlowSchema(mixedSingleStepSchema), true);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('validateFlowSchema rejects flow with synchronous events having multiple flowSteps', () => {
|
|
274
|
+
const mixedMultiStepSchema = {
|
|
275
|
+
flowTag: 'ConfirmUsage',
|
|
276
|
+
event: ['ownTopic', 'lambdaSyncInv'],
|
|
277
|
+
flowSteps: [
|
|
278
|
+
{
|
|
279
|
+
stepName: 'Start',
|
|
280
|
+
nextSteps: [{ targetType: 'ownTopic', stepName: 'StepTwo' }]
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
stepName: 'StepTwo',
|
|
284
|
+
previousSteps: [{ sourceType: 'ownTopic', stepName: 'Start' }]
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
assert.throws(
|
|
290
|
+
() => validateFlowSchema(mixedMultiStepSchema),
|
|
291
|
+
/Flow 'ConfirmUsage' with synchronous events .* must only define a single entry step 'Start'/
|
|
292
|
+
);
|
|
293
|
+
});
|
|
@@ -76,11 +76,23 @@ test('emits queues, topics, tables, and IAM roles from schema data', async () =>
|
|
|
76
76
|
assert.match(dynamoYaml, /OrdersTable:/);
|
|
77
77
|
assert.match(dynamoYaml, /#<#OrdersTableSetting#>/);
|
|
78
78
|
assert.match(dynamoYaml, /#<#\/OrdersTableSetting#>/);
|
|
79
|
-
assert.match(
|
|
80
|
-
|
|
79
|
+
assert.match(
|
|
80
|
+
dynamoYaml,
|
|
81
|
+
/RolesTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}Roles/
|
|
82
|
+
);
|
|
83
|
+
assert.match(
|
|
84
|
+
dynamoYaml,
|
|
85
|
+
/TargetRolesTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+AttributeName: identifiersConcat/
|
|
86
|
+
);
|
|
81
87
|
assert.doesNotMatch(dynamoYaml, /RoleLimitTable:/);
|
|
82
|
-
assert.match(
|
|
83
|
-
|
|
88
|
+
assert.match(
|
|
89
|
+
dynamoYaml,
|
|
90
|
+
/RunningTotalUsageTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}RunningTotalUsage/
|
|
91
|
+
);
|
|
92
|
+
assert.match(
|
|
93
|
+
dynamoYaml,
|
|
94
|
+
/RunningTotalReservedUsageTable:\s+Type: 'AWS::DynamoDB::Table'[\s\S]+TableName: \${self:custom\.iz_resourcePrefix}RunningTotalReservedUsage/
|
|
95
|
+
);
|
|
84
96
|
const iam = await readFile(
|
|
85
97
|
join(
|
|
86
98
|
rootPath,
|
|
@@ -151,6 +163,7 @@ test('emits queues, topics, tables, and IAM roles from schema data', async () =>
|
|
|
151
163
|
const procFindDataRole = roleBlock(iam, 'ProcFindDataRole');
|
|
152
164
|
assert.match(procFindDataRole, /dynamodb:UpdateItem/);
|
|
153
165
|
assert.match(procFindDataRole, /dynamodb:GetItem/);
|
|
166
|
+
assert.match(procFindDataRole, /dynamodb:PutItem/);
|
|
154
167
|
assert.match(procFindDataRole, /dynamodb:Query/);
|
|
155
168
|
assert.match(procFindDataRole, /sns:Publish/);
|
|
156
169
|
assert.match(procFindDataRole, /FindData_Out/);
|
|
@@ -191,6 +204,38 @@ test('rejects deprecated FlowHook schemas', async () => {
|
|
|
191
204
|
);
|
|
192
205
|
});
|
|
193
206
|
|
|
207
|
+
test('rejects invalid generateCodeTag configuration', async () => {
|
|
208
|
+
const rootPath = await createProject();
|
|
209
|
+
const flowDirectory = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
210
|
+
await writeFile(
|
|
211
|
+
join(flowDirectory, 'InvalidTag.js'),
|
|
212
|
+
"export default { flowTag: 'invalidHook', event: ['ownTopic'], " +
|
|
213
|
+
"generatedCodeConfig: { generateCodeTag: ['invalidAction'], " +
|
|
214
|
+
"codeHookTag: 'before' } };\n"
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
await assert.rejects(
|
|
218
|
+
generateResourceArtifacts(rootPath),
|
|
219
|
+
/generatedCodeConfig\.generateCodeTag contains invalid tag 'invalidAction'/
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('rejects empty generateCodeTag array configuration', async () => {
|
|
224
|
+
const rootPath = await createProject();
|
|
225
|
+
const flowDirectory = join(rootPath, 'app/src/schemas/FlowSchemas');
|
|
226
|
+
await writeFile(
|
|
227
|
+
join(flowDirectory, 'EmptyTag.js'),
|
|
228
|
+
"export default { flowTag: 'emptyHook', event: ['ownTopic'], " +
|
|
229
|
+
"generatedCodeConfig: { generateCodeTag: [], " +
|
|
230
|
+
"codeHookTag: 'before' } };\n"
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
await assert.rejects(
|
|
234
|
+
generateResourceArtifacts(rootPath),
|
|
235
|
+
/generatedCodeConfig\.generateCodeTag array must not be empty/
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
194
239
|
test('emits the shared WebSocket completion queue and output-topic subscriptions', async () => {
|
|
195
240
|
const rootPath = await createProject();
|
|
196
241
|
await generateResourceArtifacts(rootPath);
|
|
@@ -290,21 +335,21 @@ test('emits DynamoDB table with number sortKey attribute type when field type is
|
|
|
290
335
|
const rootPath = await createProject();
|
|
291
336
|
await writeFile(
|
|
292
337
|
join(rootPath, 'app/src/schemas/ObjectSchemas/CartOrder.js'),
|
|
293
|
-
|
|
338
|
+
'export default { ' +
|
|
294
339
|
"objectType: 'cartOrder', " +
|
|
295
340
|
"generatedBy: 'user', " +
|
|
296
|
-
|
|
341
|
+
'fieldNames: { ' +
|
|
297
342
|
" cartId: { type: 'string' }, " +
|
|
298
343
|
" sortId: { type: 'number' } " +
|
|
299
|
-
|
|
300
|
-
|
|
344
|
+
'}, ' +
|
|
345
|
+
'identifiers: [ ' +
|
|
301
346
|
" { type: 'partitionKey', fieldName: 'cartId' }, " +
|
|
302
347
|
" { type: 'sortKey', fieldName: 'sortId' } " +
|
|
303
|
-
|
|
304
|
-
|
|
348
|
+
'], ' +
|
|
349
|
+
'storageResources: { ' +
|
|
305
350
|
" main: { storageType: 'dynamoDB', tableName: 'CartOrders' } " +
|
|
306
|
-
|
|
307
|
-
|
|
351
|
+
'} ' +
|
|
352
|
+
'};\n'
|
|
308
353
|
);
|
|
309
354
|
|
|
310
355
|
await generateResourceArtifacts(rootPath);
|
|
@@ -369,6 +414,220 @@ test('emits CreateNodeCallbackHdrSqs in FlowObjectsRole when object has graph an
|
|
|
369
414
|
assert.match(role, /RolePermissions/);
|
|
370
415
|
assert.match(role, /ValidatorRbac_In/);
|
|
371
416
|
assert.match(role, /Rbac_\$\{self:custom\.iz_stage\}_ValidatorRbac_In/);
|
|
417
|
+
assert.match(role, /ConfirmUsageHdrInv/);
|
|
418
|
+
assert.match(role, /CancelUsageHdrInv/);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test('emits CreateAfterHookCallbackHdrSqs in SQS resources and FlowObjectsRole when flow has after hook with ownTopic', async () => {
|
|
422
|
+
const rootPath = await createProject();
|
|
423
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
424
|
+
await writeFile(
|
|
425
|
+
join(schemas, 'FlowSchemas/NotifyAfterCreate.js'),
|
|
426
|
+
"export default { flowTag: 'notifyAfterCreate', event: ['ownTopic'], generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'after', invocationType: 'ownTopic', objType: { objectType: 'order' } } };\n"
|
|
427
|
+
);
|
|
428
|
+
await generateResourceArtifacts(rootPath);
|
|
429
|
+
|
|
430
|
+
const crudResources = await readFile(
|
|
431
|
+
join(
|
|
432
|
+
rootPath,
|
|
433
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-crud.yml'
|
|
434
|
+
),
|
|
435
|
+
'utf8'
|
|
436
|
+
);
|
|
437
|
+
assert.match(crudResources, /^ CreateAfterHookCallbackHdrSqs:/m);
|
|
438
|
+
assert.match(crudResources, /^ CreateAfterHookCallbackHdrSqsDLQ:/m);
|
|
439
|
+
|
|
440
|
+
const iam = await readFile(
|
|
441
|
+
join(
|
|
442
|
+
rootPath,
|
|
443
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
444
|
+
),
|
|
445
|
+
'utf8'
|
|
446
|
+
);
|
|
447
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
448
|
+
assert.match(role, /CreateAfterHookCallbackHdrSqs/);
|
|
449
|
+
assert.match(role, /CreateAfterHookCallbackHdrSqsDLQ/);
|
|
450
|
+
assert.match(role, /NotifyAfterCreate_In/);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test('emits CreateBeforeHookCallbackHdrSqs in SQS resources and FlowObjectsRole when flow has before hook with ownTopic', async () => {
|
|
454
|
+
const rootPath = await createProject();
|
|
455
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
456
|
+
await writeFile(
|
|
457
|
+
join(schemas, 'FlowSchemas/ValidateBeforeCreate.js'),
|
|
458
|
+
"export default { flowTag: 'validateBeforeCreate', event: ['ownTopic'], generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'before', invocationType: 'ownTopic' }, objType: { objectType: 'order' } };\n"
|
|
459
|
+
);
|
|
460
|
+
await generateResourceArtifacts(rootPath);
|
|
461
|
+
|
|
462
|
+
const crudResources = await readFile(
|
|
463
|
+
join(
|
|
464
|
+
rootPath,
|
|
465
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-crud.yml'
|
|
466
|
+
),
|
|
467
|
+
'utf8'
|
|
468
|
+
);
|
|
469
|
+
assert.match(crudResources, /^ CreateBeforeHookCallbackHdrSqs:/m);
|
|
470
|
+
assert.match(crudResources, /^ CreateBeforeHookCallbackHdrSqsDLQ:/m);
|
|
471
|
+
|
|
472
|
+
const iam = await readFile(
|
|
473
|
+
join(
|
|
474
|
+
rootPath,
|
|
475
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
476
|
+
),
|
|
477
|
+
'utf8'
|
|
478
|
+
);
|
|
479
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
480
|
+
assert.match(role, /CreateBeforeHookCallbackHdrSqs/);
|
|
481
|
+
assert.match(role, /CreateBeforeHookCallbackHdrSqsDLQ/);
|
|
482
|
+
assert.match(role, /ValidateBeforeCreate_In/);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
test('emits UpdateBeforeHookCallbackHdrSqs in SQS resources and FlowObjectsRole when flow has before hook with ownTopic', async () => {
|
|
486
|
+
const rootPath = await createProject();
|
|
487
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
488
|
+
await writeFile(
|
|
489
|
+
join(schemas, 'FlowSchemas/ValidateBeforeUpdate.js'),
|
|
490
|
+
"export default { flowTag: 'validateBeforeUpdate', event: ['ownTopic'], generatedCodeConfig: { generateCodeTag: 'update', codeHookTag: 'before', invocationType: 'ownTopic' }, objType: { objectType: 'order' } };\n"
|
|
491
|
+
);
|
|
492
|
+
await generateResourceArtifacts(rootPath);
|
|
493
|
+
|
|
494
|
+
const crudResources = await readFile(
|
|
495
|
+
join(
|
|
496
|
+
rootPath,
|
|
497
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-crud.yml'
|
|
498
|
+
),
|
|
499
|
+
'utf8'
|
|
500
|
+
);
|
|
501
|
+
assert.match(crudResources, /^ UpdateBeforeHookCallbackHdrSqs:/m);
|
|
502
|
+
assert.match(crudResources, /^ UpdateBeforeHookCallbackHdrSqsDLQ:/m);
|
|
503
|
+
|
|
504
|
+
const iam = await readFile(
|
|
505
|
+
join(
|
|
506
|
+
rootPath,
|
|
507
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
508
|
+
),
|
|
509
|
+
'utf8'
|
|
510
|
+
);
|
|
511
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
512
|
+
assert.match(role, /UpdateBeforeHookCallbackHdrSqs/);
|
|
513
|
+
assert.match(role, /UpdateBeforeHookCallbackHdrSqsDLQ/);
|
|
514
|
+
assert.match(role, /ValidateBeforeUpdate_In/);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test('emits DeleteBeforeHookCallbackHdrSqs in SQS resources and FlowObjectsRole when flow has before hook with ownTopic', async () => {
|
|
518
|
+
const rootPath = await createProject();
|
|
519
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
520
|
+
await writeFile(
|
|
521
|
+
join(schemas, 'FlowSchemas/ValidateBeforeDelete.js'),
|
|
522
|
+
"export default { flowTag: 'validateBeforeDelete', event: ['ownTopic'], generatedCodeConfig: { generateCodeTag: 'delete', codeHookTag: 'before', invocationType: 'ownTopic' }, objType: { objectType: 'order' } };\n"
|
|
523
|
+
);
|
|
524
|
+
await generateResourceArtifacts(rootPath);
|
|
525
|
+
|
|
526
|
+
const crudResources = await readFile(
|
|
527
|
+
join(
|
|
528
|
+
rootPath,
|
|
529
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-crud.yml'
|
|
530
|
+
),
|
|
531
|
+
'utf8'
|
|
532
|
+
);
|
|
533
|
+
assert.match(crudResources, /^ DeleteBeforeHookCallbackHdrSqs:/m);
|
|
534
|
+
assert.match(crudResources, /^ DeleteBeforeHookCallbackHdrSqsDLQ:/m);
|
|
535
|
+
|
|
536
|
+
const iam = await readFile(
|
|
537
|
+
join(
|
|
538
|
+
rootPath,
|
|
539
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
540
|
+
),
|
|
541
|
+
'utf8'
|
|
542
|
+
);
|
|
543
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
544
|
+
assert.match(role, /DeleteBeforeHookCallbackHdrSqs/);
|
|
545
|
+
assert.match(role, /DeleteBeforeHookCallbackHdrSqsDLQ/);
|
|
546
|
+
assert.match(role, /ValidateBeforeDelete_In/);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
test('emits lambda:InvokeFunction in FlowObjectsRole when flow has before hook with lambdaSyncInv for update and delete', async () => {
|
|
550
|
+
const rootPath = await createProject();
|
|
551
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
552
|
+
await writeFile(
|
|
553
|
+
join(schemas, 'FlowSchemas/CheckBeforeUpdate.js'),
|
|
554
|
+
"export default { flowTag: 'checkBeforeUpdate', event: ['lambdaSyncInv'], generatedCodeConfig: { generateCodeTag: 'update', codeHookTag: 'before', invocationType: 'lambdaSyncInv' }, objType: { objectType: 'order' } };\n"
|
|
555
|
+
);
|
|
556
|
+
await writeFile(
|
|
557
|
+
join(schemas, 'FlowSchemas/CheckBeforeDelete.js'),
|
|
558
|
+
"export default { flowTag: 'checkBeforeDelete', event: ['lambdaSyncInv'], generatedCodeConfig: { generateCodeTag: 'delete', codeHookTag: 'before', invocationType: 'lambdaSyncInv' }, objType: { objectType: 'order' } };\n"
|
|
559
|
+
);
|
|
560
|
+
await generateResourceArtifacts(rootPath);
|
|
561
|
+
|
|
562
|
+
const iam = await readFile(
|
|
563
|
+
join(
|
|
564
|
+
rootPath,
|
|
565
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
566
|
+
),
|
|
567
|
+
'utf8'
|
|
568
|
+
);
|
|
569
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
570
|
+
assert.match(role, /lambda:InvokeFunction/);
|
|
571
|
+
assert.match(role, /CheckBeforeUpdate/);
|
|
572
|
+
assert.match(role, /CheckBeforeDelete/);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test('emits lambda:InvokeFunction in FlowObjectsRole when flow has after hook with lambdaSyncInv', async () => {
|
|
576
|
+
const rootPath = await createProject();
|
|
577
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
578
|
+
await writeFile(
|
|
579
|
+
join(schemas, 'FlowSchemas/AuditAfterCreate.js'),
|
|
580
|
+
"export default { flowTag: 'auditAfterCreate', event: ['lambdaSyncInv'], generatedCodeConfig: { generateCodeTag: 'create', codeHookTag: 'after', invocationType: 'lambdaSyncInv', objType: { objectType: 'order' } } };\n"
|
|
581
|
+
);
|
|
582
|
+
await generateResourceArtifacts(rootPath);
|
|
583
|
+
|
|
584
|
+
const iam = await readFile(
|
|
585
|
+
join(
|
|
586
|
+
rootPath,
|
|
587
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
588
|
+
),
|
|
589
|
+
'utf8'
|
|
590
|
+
);
|
|
591
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
592
|
+
assert.match(role, /lambda:InvokeFunction/);
|
|
593
|
+
assert.match(role, /AuditAfterCreate/);
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
test('emits SQS queues and deduplicated IAM policies when generateCodeTag is an array for multiple actions', async () => {
|
|
597
|
+
const rootPath = await createProject();
|
|
598
|
+
const schemas = join(rootPath, 'app/src/schemas');
|
|
599
|
+
await writeFile(
|
|
600
|
+
join(schemas, 'FlowSchemas/NotifyMultiAfter.js'),
|
|
601
|
+
"export default { flowTag: 'notifyMultiAfter', event: ['ownTopic'], " +
|
|
602
|
+
"generatedCodeConfig: { generateCodeTag: ['create', 'update'], " +
|
|
603
|
+
"codeHookTag: 'after', invocationType: 'ownTopic', " +
|
|
604
|
+
"objType: { objectType: 'order' } } };\n"
|
|
605
|
+
);
|
|
606
|
+
await generateResourceArtifacts(rootPath);
|
|
607
|
+
|
|
608
|
+
const crudResources = await readFile(
|
|
609
|
+
join(
|
|
610
|
+
rootPath,
|
|
611
|
+
'resource/sls_yaml/generatedCode/source/generated-sns-sqs-crud.yml'
|
|
612
|
+
),
|
|
613
|
+
'utf8'
|
|
614
|
+
);
|
|
615
|
+
assert.match(crudResources, /^ CreateAfterHookCallbackHdrSqs:/m);
|
|
616
|
+
assert.match(crudResources, /^ UpdateAfterHookCallbackHdrSqs:/m);
|
|
617
|
+
|
|
618
|
+
const iam = await readFile(
|
|
619
|
+
join(
|
|
620
|
+
rootPath,
|
|
621
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
622
|
+
),
|
|
623
|
+
'utf8'
|
|
624
|
+
);
|
|
625
|
+
const role = roleBlock(iam, 'FlowObjectsRole');
|
|
626
|
+
assert.match(role, /CreateAfterHookCallbackHdrSqs/);
|
|
627
|
+
assert.match(role, /UpdateAfterHookCallbackHdrSqs/);
|
|
628
|
+
|
|
629
|
+
const notifyTopicMatches = role.match(/NotifyMultiAfter_In/g);
|
|
630
|
+
assert.strictEqual(notifyTopicMatches?.length, 1);
|
|
372
631
|
});
|
|
373
632
|
|
|
374
633
|
function roleBlock(iam, roleName) {
|