Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ stepFunctions:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${opt:stage}-hello
End: true
dependsOn: CustomIamRole
hellostepfunc2:
definition:
StartAt: HelloWorld2
Expand All @@ -55,6 +56,10 @@ stepFunctions:
Type: Task
Resource: arn:aws:states:#{AWS::Region}:#{AWS::AccountId}:activity:myTask
End: true
dependsOn:
- DynamoDBTable
- KinesisStream
- CUstomIamRole
activities:
- myTask
- yourTask
Expand Down Expand Up @@ -110,6 +115,23 @@ plugins:

You can then `Ref: SendMessageStateMachine` in various parts of CloudFormation or serverless.yml

#### Depending on another logical id
If your state machine depends on another resource defined in your `serverless.yml` then you can add a `dependsOn` field to the state machine `definition`. This would add the `DependsOn`clause to the generated CloudFormation template.

This `dependsOn` field can be either a string, or an array of strings.

```yaml
stepFunctions:
stateMachines:
myStateMachine:
dependsOn: myDB

myOtherStateMachine:
dependsOn:
- myOtherDB
- myStream
```

#### Current Gotcha
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.

Expand Down Expand Up @@ -329,11 +351,11 @@ stepFunctions:
events:
- http:
path: /users
...
...
authorizer:
# Provide both type and authorizerId
type: COGNITO_USER_POOLS # TOKEN, CUSTOM or COGNITO_USER_POOLS, same as AWS Cloudformation documentation
authorizerId:
authorizerId:
Ref: ApiGatewayAuthorizer # or hard-code Authorizer ID
```

Expand Down Expand Up @@ -581,7 +603,7 @@ stepFunctions:
state:
- pending
definition:
...
...
```

## Specifying a Name
Expand Down Expand Up @@ -654,7 +676,7 @@ resources:
Resources:
StateMachineRole:
Type: AWS::IAM::Role
Properties:
Properties:
...
```

Expand Down
2 changes: 1 addition & 1 deletion lib/deploy/stepFunctions/compileIamRole.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function consolidatePermissionsByAction(permissions) {
.mapValues(perms => {
// find the unique resources
let resources = _.uniqWith(_.flatMap(perms, p => p.resource), _.isEqual);
if (resources.includes('*')) {
if (_.includes(resources, '*')) {
resources = '*';
}

Expand Down
21 changes: 19 additions & 2 deletions lib/deploy/stepFunctions/compileStateMachines.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
const stateMachineObj = this.getStateMachine(stateMachineName);
let DefinitionString;
let RoleArn;
let DependsOn;
let DependsOn = [];

if (stateMachineObj.definition) {
if (typeof stateMachineObj.definition === 'string') {
Expand Down Expand Up @@ -63,7 +63,24 @@ module.exports = {
'Arn',
],
};
DependsOn = 'IamRoleStateMachineExecution';
DependsOn.push('IamRoleStateMachineExecution');
}

if (stateMachineObj.dependsOn) {
const dependsOn = stateMachineObj.dependsOn;

if (_.isArray(dependsOn) && _.every(dependsOn, _.isString)) {
DependsOn = _.concat(DependsOn, dependsOn);
} else if (_.isString(dependsOn)) {
DependsOn.push(dependsOn);
} else {
const errorMessage = [
`dependsOn property in stateMachine "${stateMachineName}" is neither a string`,
' nor an array of strings',
].join('');
throw new this.serverless.classes
.Error(errorMessage);
}
}

const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName,
Expand Down
90 changes: 84 additions & 6 deletions lib/deploy/stepFunctions/compileStateMachines.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ describe('#compileStateMachines', () => {
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Outputs
.StateMachineBeta1Arn.Value.Ref
Expand Down Expand Up @@ -119,11 +119,11 @@ describe('#compileStateMachines', () => {
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.MyStateMachine1StepFunctionsStateMachine.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.MyStateMachine2StepFunctionsStateMachine.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Outputs
.MyStateMachine1StepFunctionsStateMachineArn.Value.Ref
Expand Down Expand Up @@ -176,11 +176,11 @@ describe('#compileStateMachines', () => {
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.DependsOn
).to.equal('IamRoleStateMachineExecution');
).to.deep.eq(['IamRoleStateMachineExecution']);
});

it('should create corresponding resources when definition and role property are given', () => {
Expand Down Expand Up @@ -405,4 +405,82 @@ describe('#compileStateMachines', () => {

expect(actual).to.equal(JSON.stringify(definition, undefined, 2));
});

it('should add dependsOn resources', () => {
serverless.service.stepFunctions = {
stateMachines: {
myStateMachine1: {
definition: 'definition1',
name: 'stateMachineBeta1',
dependsOn: 'DynamoDBTable',
},
myStateMachine2: {
definition: 'definition2',
name: 'stateMachineBeta2',
dependsOn: [
'DynamoDBTable',
'KinesisStream',
],
},
},
};

serverlessStepFunctions.compileStateMachines();
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.Type
).to.equal('AWS::StepFunctions::StateMachine');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.Type
).to.equal('AWS::StepFunctions::StateMachine');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.Properties.DefinitionString
).to.equal('"definition1"');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.Properties.DefinitionString
).to.equal('"definition2"');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.Properties.RoleArn['Fn::GetAtt'][0]
).to.equal('IamRoleStateMachineExecution');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.Properties.RoleArn['Fn::GetAtt'][0]
).to.equal('IamRoleStateMachineExecution');
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta1.DependsOn
).to.deep.eq(['IamRoleStateMachineExecution', 'DynamoDBTable']);
expect(serverlessStepFunctions.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.StateMachineBeta2.DependsOn
).to.deep.eq(['IamRoleStateMachineExecution', 'DynamoDBTable', 'KinesisStream']);
});

it('should throw error when dependsOn property is neither string nor [string]', () => {
serverless.service.stepFunctions = {
stateMachines: {
myStateMachine1: {
definition: 'definition1',
name: 'stateMachineBeta1',
dependsOn: { Ref: 'ss' },
},
},
};
expect(() => serverlessStepFunctions.compileStateMachines()).to.throw(Error);

serverless.service.stepFunctions = {
stateMachines: {
myStateMachine1: {
definition: 'definition1',
name: 'stateMachineBeta1',
dependsOn: [{ Ref: 'ss' }],
},
},
};
expect(() => serverlessStepFunctions.compileStateMachines()).to.throw(Error);
});
});