Skip to content

Commit d3703e1

Browse files
authored
Merge pull request #245 from ChristopheBougere/asl-validator
feat: asl validation
2 parents e30421e + 3390c13 commit d3703e1

File tree

6 files changed

+195
-21
lines changed

6 files changed

+195
-21
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This is the Serverless Framework plugin for AWS Step Functions.
1414
- [CloudWatch Alarms](#cloudwatch-alarms)
1515
- [CloudWatch Notifications](#cloudwatch-notifications)
1616
- [Blue-Green deployments](#blue-green-deployment)
17+
- [Pre-deployment validation](#pre-deployment-validation)
1718
- [Current Gotcha](#current-gotcha)
1819
- [Events](#events)
1920
- [API Gateway](#api-gateway)
@@ -135,6 +136,7 @@ stepFunctions:
135136
activities:
136137
- myTask
137138
- yourTask
139+
validate: true # enable pre-deployment definition validation (disabled by default)
138140
139141
plugins:
140142
- serverless-step-functions
@@ -334,6 +336,15 @@ stepFunctions:
334336
...
335337
```
336338

339+
### Pre-deployment validation
340+
341+
By default, your state machine definition will be validated during deployment by StepFunctions. This can be cumbersome when developing because you have to upload your service for every typo in your definition. In order to go faster, you can enable pre-deployment validation using [asl-validator](https://www.npmjs.com/package/asl-validator) which should detect most of the issues (like a missing state property).
342+
343+
```yaml
344+
stepFunctions:
345+
validate: true
346+
```
347+
337348
## Current Gotcha
338349

339350
Please keep this gotcha in mind if you want to reference the `name` from the `resources` section. To generate Logical ID for CloudFormation, the plugin transforms the specified name in serverless.yml based on the following scheme.

lib/deploy/stepFunctions/compileStateMachines.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const _ = require('lodash');
44
const Joi = require('@hapi/joi');
5+
const aslValidator = require('asl-validator');
56
const Chance = require('chance');
67
const BbPromise = require('bluebird');
78
const schema = require('./compileStateMachines.schema');
@@ -80,6 +81,18 @@ module.exports = {
8081
}
8182

8283
if (stateMachineObj.definition) {
84+
if (this.serverless.service.stepFunctions.validate) {
85+
const { isValid, errors } = aslValidator(stateMachineObj.definition);
86+
if (isValid) {
87+
this.serverless.cli.consoleLog(`✓ State machine "${stateMachineName}" definition is valid`);
88+
} else {
89+
const errorMessage = [
90+
`✕ State machine "${stateMachineName}" definition is invalid:`,
91+
JSON.stringify(errors),
92+
].join('\n');
93+
throw new this.serverless.classes.Error(errorMessage);
94+
}
95+
}
8396
if (typeof stateMachineObj.definition === 'string') {
8497
DefinitionString = JSON.stringify(stateMachineObj.definition)
8598
.replace(/\\n|\\r|\\n\\r/g, '');

lib/deploy/stepFunctions/compileStateMachines.test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ describe('#compileStateMachines', () => {
1111

1212
beforeEach(() => {
1313
serverless = new Serverless();
14+
serverless.cli = {
15+
consoleLog: () => {},
16+
};
1417
serverless.servicePath = true;
1518
serverless.service.service = 'step-functions';
1619
serverless.service.provider.compiledCloudFormationTemplate = {
@@ -1024,4 +1027,85 @@ describe('#compileStateMachines', () => {
10241027
expect(topicArn).to.deep.equal({ Ref: 'MyTopic' });
10251028
});
10261029
});
1030+
1031+
it('should not validate definition if not enabled', () => {
1032+
serverless.service.stepFunctions = {
1033+
stateMachines: {
1034+
myStateMachine1: {
1035+
definition: {
1036+
StartAt: 'Start',
1037+
States: {
1038+
Start: {
1039+
Type: 'Inexistant type',
1040+
End: true,
1041+
},
1042+
},
1043+
},
1044+
},
1045+
},
1046+
validate: false,
1047+
};
1048+
// Definition is invalid, but should succeed because validate=false
1049+
serverlessStepFunctions.compileStateMachines();
1050+
});
1051+
1052+
it('should validate definition and pass', () => {
1053+
serverless.service.stepFunctions = {
1054+
stateMachines: {
1055+
myStateMachine1: {
1056+
definition: {
1057+
StartAt: 'GetAttResource',
1058+
States: {
1059+
GetAttResource: {
1060+
Type: 'Task',
1061+
Resource: {
1062+
'Fn::GetAtt': [
1063+
'lambda-name_GetAtt',
1064+
'Arn',
1065+
],
1066+
},
1067+
Next: 'RefResource',
1068+
},
1069+
RefResource: {
1070+
Type: 'Task',
1071+
Resource: {
1072+
Ref: 'lambda-name_Ref',
1073+
},
1074+
Next: 'ArnResource',
1075+
},
1076+
ArnResource: {
1077+
Type: 'Task',
1078+
Resource: 'arn:aws:lambda:region-1:1234567890:function:lambda-name_Arn',
1079+
End: true,
1080+
},
1081+
},
1082+
},
1083+
},
1084+
},
1085+
validate: true,
1086+
};
1087+
// Definition is valid, should succeed
1088+
serverlessStepFunctions.compileStateMachines();
1089+
});
1090+
1091+
it('should validate definition and fail', () => {
1092+
serverless.service.stepFunctions = {
1093+
stateMachines: {
1094+
myStateMachine1: {
1095+
definition: {
1096+
StartAt: 'Start',
1097+
States: {
1098+
Start: {
1099+
Type: 'Inexistant type',
1100+
End: true,
1101+
},
1102+
},
1103+
},
1104+
},
1105+
},
1106+
validate: true,
1107+
};
1108+
// Definition is invalid and validate=true, should throw
1109+
expect(() => serverlessStepFunctions.compileStateMachines()).to.throw(Error);
1110+
});
10271111
});

lib/yamlParser.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ module.exports = {
1717
.parse(serverlessYmlPath)
1818
.then(serverlessFileParam => this.serverless.variables.populateObject(serverlessFileParam)
1919
.then((parsedObject) => {
20-
this.serverless.service.stepFunctions = {};
20+
this.serverless.service.stepFunctions = {
21+
validate: parsedObject.stepFunctions ? parsedObject.stepFunctions.validate : false,
22+
};
2123
this.serverless.service.stepFunctions.stateMachines = parsedObject.stepFunctions
2224
&& parsedObject.stepFunctions.stateMachines
2325
? parsedObject.stepFunctions.stateMachines : {};

package-lock.json

Lines changed: 83 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
},
4343
"dependencies": {
4444
"@hapi/joi": "^15.0.2",
45+
"asl-validator": "^1.4.0",
4546
"aws-sdk": "^2.282.1",
4647
"bluebird": "^3.4.0",
4748
"chalk": "^1.1.1",

0 commit comments

Comments
 (0)