Skip to content

Commit 69dea38

Browse files
authored
Merge branch 'master' into inputTransformer
2 parents 4b68246 + a4531c1 commit 69dea38

35 files changed

+336
-332
lines changed

.github/workflows/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: CI
22

3-
on: push
3+
on: [push, pull_request]
44

55
jobs:
66
test:

CHANGELOG.md

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.md

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
This is the Serverless Framework plugin for AWS Step Functions.
66

7+
## Requirement
8+
Serverless Framework v2.32.0 or later is required.
9+
710
## TOC
811

912
- [Install](#install)
@@ -48,6 +51,7 @@ This is the Serverless Framework plugin for AWS Step Functions.
4851
- [Specifying a RoleArn](#specifying-a-rolearn)
4952
- [Specifying a custom CloudWatch EventBus](#specifying-a-custom-cloudwatch-eventbus)
5053
- [Specifying a custom EventBridge EventBus](#specifying-a-custom-eventbridge-eventbus)
54+
- [Specifying a DeadLetterQueue](#specifying-a-deadletterqueue)
5155
- [Tags](#tags)
5256
- [Commands](#commands)
5357
- [deploy](#deploy)
@@ -164,7 +168,7 @@ stepFunctions:
164168
dependsOn:
165169
- DynamoDBTable
166170
- KinesisStream
167-
- CUstomIamRole
171+
- CustomIamRole
168172
tags:
169173
Team: Atlantis
170174
activities:
@@ -1166,6 +1170,73 @@ stepFunctions:
11661170
...
11671171
```
11681172

1173+
#### Specifying a DeadLetterQueue
1174+
1175+
You can configure a target queue to send dead-letter queue events to:
1176+
1177+
```yml
1178+
stepFunctions:
1179+
stateMachines:
1180+
exampleEventBridgeEventStartsMachine:
1181+
events:
1182+
- eventBridge:
1183+
eventBusName: 'my-custom-event-bus'
1184+
event:
1185+
source:
1186+
- "my.custom.source"
1187+
detail-type:
1188+
- "My Event Type"
1189+
detail:
1190+
state:
1191+
- pending
1192+
deadLetterConfig: 'arn:aws:sqs:us-east-1:012345678910:my-dlq' # SQS Arn
1193+
definition:
1194+
...
1195+
```
1196+
##### Important point
1197+
Don't forget to [Grant permissions to the dead-letter queue](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rule-dlq.html#eb-dlq-perms), to do that you may need to have the `ARN` of the generated `EventBridge Rule`.
1198+
1199+
In order to get the `ARN` you can use [intrinsic functions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html) against the `logicalId`, this plugin generates `logicalIds` following this format:
1200+
```ts
1201+
`${StateMachineName}EventsRuleCloudWatchEvent${index}`
1202+
```
1203+
Given this example 👇
1204+
```yml
1205+
stepFunctions:
1206+
stateMachines:
1207+
hellostepfunc1: # <---- StateMachineName
1208+
events:
1209+
- eventBridge:
1210+
eventBusName: 'my-custom-event-bus'
1211+
event:
1212+
source:
1213+
- "my.custom.source"
1214+
- eventBridge:
1215+
eventBusName: 'my-custom-event-bus'
1216+
event:
1217+
source:
1218+
- "my.custom.source"
1219+
deadLetterConfig: 'arn:aws:sqs:us-east-1:012345678910:my-dlq'
1220+
name: myStateMachine
1221+
definition:
1222+
Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
1223+
StartAt: HelloWorld1
1224+
States:
1225+
HelloWorld1:
1226+
Type: Task
1227+
Resource:
1228+
Fn::GetAtt: [hello, Arn]
1229+
End: true
1230+
```
1231+
Then
1232+
```yaml
1233+
# to get the Arn of the 1st EventBridge rule
1234+
!GetAtt Hellostepfunc1EventsRuleCloudWatchEvent1.Arn
1235+
1236+
# to get the Arn of the 2nd EventBridge rule
1237+
!GetAtt Hellostepfunc1EventsRuleCloudWatchEvent2.Arn
1238+
```
1239+
11691240
## Tags
11701241

11711242
You can specify tags on each state machine. Additionally any global tags (specified under `provider` section in your `serverless.yml`) would be merged in as well.

lib/deploy/events/apiGateway/apiKeys.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ const BbPromise = require('bluebird');
55

66
module.exports = {
77
compileApiKeys() {
8-
if (this.serverless.service.provider.apiKeys) {
9-
if (!Array.isArray(this.serverless.service.provider.apiKeys)) {
8+
const apiKeys = _.get(this.serverless.service.provider.apiGateway, 'apiKeys')
9+
|| this.serverless.service.provider.apiKeys;
10+
if (apiKeys) {
11+
if (!Array.isArray(apiKeys)) {
1012
throw new this.serverless.classes.Error('apiKeys property must be an array');
1113
}
1214

13-
_.forEach(this.serverless.service.provider.apiKeys, (apiKey, i) => {
15+
_.forEach(apiKeys, (apiKey, i) => {
1416
const apiKeyNumber = i + 1;
1517

1618
if (typeof apiKey !== 'string') {

lib/deploy/events/apiGateway/apiKeys.test.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#methods()', () => {
@@ -16,7 +16,8 @@ describe('#methods()', () => {
1616
region: 'us-east-1',
1717
};
1818
serverless.setProvider('aws', new AwsProvider(serverless));
19-
serverless.service.provider.apiKeys = ['1234567890'];
19+
if (!serverless.service.provider.apiGateway) serverless.service.provider.apiGateway = {};
20+
serverless.service.provider.apiGateway.apiKeys = ['1234567890'];
2021
serverless.service.provider.compiledCloudFormationTemplate = {
2122
Resources: {},
2223
};
@@ -79,12 +80,12 @@ describe('#methods()', () => {
7980
}));
8081

8182
it('throw error if apiKey property is not an array', () => {
82-
serverlessStepFunctions.serverless.service.provider.apiKeys = 2;
83+
serverlessStepFunctions.serverless.service.provider.apiGateway.apiKeys = 2;
8384
expect(() => serverlessStepFunctions.compileApiKeys()).to.throw(Error);
8485
});
8586

8687
it('throw error if an apiKey is not a string', () => {
87-
serverlessStepFunctions.serverless.service.provider.apiKeys = [2];
88+
serverlessStepFunctions.serverless.service.provider.apiGateway.apiKeys = [2];
8889
expect(() => serverlessStepFunctions.compileApiKeys()).to.throw(Error);
8990
});
9091
});

lib/deploy/events/apiGateway/authorizers.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#compileAuthorizers()', () => {

lib/deploy/events/apiGateway/cors.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#compileCors()', () => {

lib/deploy/events/apiGateway/deployment.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#compileDeployment()', () => {

lib/deploy/events/apiGateway/endpointInfo.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const expect = require('chai').expect;
44
const sinon = require('sinon');
55
const BbPromise = require('bluebird');
66
const Serverless = require('serverless/lib/Serverless');
7-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
7+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
88
const ServerlessStepFunctions = require('./../../../index');
99

1010

lib/deploy/events/apiGateway/iamRole.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#compileHttpIamRole()', () => {

lib/deploy/events/apiGateway/lambdaPermissions.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ const BbPromise = require('bluebird');
2424
const getFunctionLogicalId = (uri) => {
2525
const parts = uri['Fn::Join'][1];
2626
const functionRef = parts.find(x => _.has(x, 'Fn::GetAtt'));
27-
return functionRef['Fn::GetAtt'][0];
27+
return _.get(functionRef, "['Fn::GetAtt'][0]", parts[parts.length - 2]);
2828
};
2929

30+
const ARNRegex = /^arn:[^:\n]*:[^:\n]*:[^:\n]*:[^:\n]*:(?<resourceName>[^:/]*)[:/]?.*$/;
31+
3032
const getLambdaPermission = logicalId => ({
3133
Type: 'AWS::Lambda::Permission',
3234
Properties: {
33-
FunctionName: {
35+
FunctionName: ARNRegex.test(logicalId) ? logicalId : {
3436
'Fn::GetAtt': [logicalId, 'Arn'],
3537
},
3638
Action: 'lambda:InvokeFunction',
@@ -54,7 +56,14 @@ module.exports = {
5456
}
5557

5658
const lambdaPermissions = _.zipObject(
57-
funcLogicalIds.map(id => `${id}LambdaPermission`),
59+
funcLogicalIds.map((id) => {
60+
if (ARNRegex.test(id)) {
61+
const { groups: { resourceName } } = id.match(ARNRegex);
62+
return `${resourceName}LambdaPermission`;
63+
}
64+
65+
return `${id}LambdaPermission`;
66+
}),
5867
funcLogicalIds.map(getLambdaPermission),
5968
);
6069

lib/deploy/events/apiGateway/lambdaPermissions.test.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const _ = require('lodash');
44
const expect = require('chai').expect;
55
const Serverless = require('serverless/lib/Serverless');
6-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
6+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
77
const ServerlessStepFunctions = require('./../../../index');
88

99
describe('#compileHttpLambdaPermissions()', () => {
@@ -152,4 +152,55 @@ describe('#compileHttpLambdaPermissions()', () => {
152152
});
153153
});
154154
});
155+
156+
it('should create a Lambda Permission resource when there is an ARN authorizer', () => {
157+
serverlessStepFunctions.serverless.service.provider
158+
.compiledCloudFormationTemplate.Resources.HelloApiGatewayAuthorizer = {
159+
Type: 'AWS::ApiGateway::Authorizer',
160+
Properties: {
161+
AuthorizerResultTtlInSeconds: 300,
162+
IdentitySource: 'method.request.header.Authorization',
163+
Name: 'hello',
164+
RestApiId: {
165+
Ref: 'ApiGatewayRestApi',
166+
},
167+
AuthorizerUri: {
168+
'Fn::Join': [
169+
'',
170+
[
171+
'arn:',
172+
{
173+
Ref: 'AWS::Partition',
174+
},
175+
':apigateway:',
176+
{
177+
Ref: 'AWS::Region',
178+
},
179+
':lambda:path/2015-03-31/functions/',
180+
'arn:aws:lambda:us-east-1:000000000000:function:remote-authorizer',
181+
'/invocations',
182+
],
183+
],
184+
},
185+
Type: 'TOKEN',
186+
},
187+
};
188+
189+
serverlessStepFunctions.compileHttpLambdaPermissions().then(() => {
190+
const resources = serverlessStepFunctions.serverless.service.provider
191+
.compiledCloudFormationTemplate.Resources;
192+
const lambdaPermissions = _.values(resources).filter(x => x.Type === 'AWS::Lambda::Permission');
193+
expect(lambdaPermissions).to.have.lengthOf(1);
194+
expect(lambdaPermissions[0]).to.deep.eq({
195+
Type: 'AWS::Lambda::Permission',
196+
Properties: {
197+
FunctionName: 'arn:aws:lambda:us-east-1:000000000000:function:remote-authorizer',
198+
Action: 'lambda:InvokeFunction',
199+
Principal: {
200+
'Fn::Sub': 'apigateway.${AWS::URLSuffix}',
201+
},
202+
},
203+
});
204+
});
205+
});
155206
});

lib/deploy/events/apiGateway/methods.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#methods()', () => {

lib/deploy/events/apiGateway/resources.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const expect = require('chai').expect;
44
const Serverless = require('serverless/lib/Serverless');
5-
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider');
5+
const AwsProvider = require('serverless/lib/plugins/aws/provider');
66
const ServerlessStepFunctions = require('./../../../index');
77

88
describe('#compileResources()', () => {

lib/deploy/events/apiGateway/restApi.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,13 @@ module.exports = {
4343
},
4444
});
4545

46-
if (!_.isEmpty(this.serverless.service.provider.resourcePolicy)) {
46+
const resourcePolicy = _.get(this.serverless.service.provider.apiGateway, 'resourcePolicy')
47+
|| this.serverless.service.provider.resourcePolicy;
48+
49+
if (!_.isEmpty(resourcePolicy)) {
4750
const policy = {
4851
Version: '2012-10-17',
49-
Statement: this.serverless.service.provider.resourcePolicy,
52+
Statement: resourcePolicy,
5053
};
5154
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate
5255
.Resources[this.apiGatewayRestApiLogicalId].Properties, {

0 commit comments

Comments
 (0)