2021-05-10 13:07:44 +02:00

1669 lines
63 KiB
JavaScript

'use strict';
const expect = require('chai').expect;
const AwsProvider = require('../../../../../../../../lib/plugins/aws/provider');
const AwsCompileStreamEvents = require('../../../../../../../../lib/plugins/aws/package/compile/events/stream');
const Serverless = require('../../../../../../../../lib/Serverless');
const runServerless = require('../../../../../../../utils/run-serverless');
describe('AwsCompileStreamEvents', () => {
let serverless;
let awsCompileStreamEvents;
beforeEach(() => {
serverless = new Serverless();
serverless.service.provider.compiledCloudFormationTemplate = {
Resources: {
IamRoleLambdaExecution: {
Properties: {
Policies: [
{
PolicyDocument: {
Statement: [],
},
},
],
},
},
},
};
serverless.setProvider('aws', new AwsProvider(serverless));
awsCompileStreamEvents = new AwsCompileStreamEvents(serverless);
awsCompileStreamEvents.serverless.service.service = 'new-service';
});
describe('#constructor()', () => {
it('should set the provider variable to be an instance of AwsProvider', () =>
expect(awsCompileStreamEvents.provider).to.be.instanceof(AwsProvider));
});
describe('#compileStreamEvents()', () => {
it('should throw an error if the "consumer" property is not a string, object, or boolean', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
type: 'kinesis',
arn: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream',
consumer: 42,
},
},
],
},
};
});
it('should throw an error if stream event type is not a string or an object', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: 42,
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('should throw an error if the "arn" property is not given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: null,
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('should not throw error or merge role statements if default policy is not present', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role is set in function', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: 'arn:aws:iam::account:role/foo',
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.be.instanceof(Array);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn.length
).to.equal(0);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role name reference is set in function', () => {
const roleLogicalId = 'RoleLogicalId';
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: roleLogicalId,
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.include(roleLogicalId);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role reference is set in function', () => {
const roleLogicalId = 'RoleLogicalId';
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: { 'Fn::GetAtt': [roleLogicalId, 'Arn'] },
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.include(roleLogicalId);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role is set in provider', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
awsCompileStreamEvents.serverless.service.provider.role = 'arn:aws:iam::account:role/foo';
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.be.instanceof(Array);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn.length
).to.equal(0);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if IAM role is referenced from cloudformation parameters', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: { Ref: 'MyStreamRoleArn' },
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn.length
).to.equal(0);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if IAM role is imported', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: { 'Fn::ImportValue': 'ExportedRoleId' },
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn.length
).to.equal(0);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role reference is set in provider', () => {
const roleLogicalId = 'RoleLogicalId';
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
awsCompileStreamEvents.serverless.service.provider.role = {
'Fn::GetAtt': [roleLogicalId, 'Arn'],
};
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.include(roleLogicalId);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
it('should not throw error if custom IAM role name reference is set in provider', () => {
const roleLogicalId = 'RoleLogicalId';
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};
// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution =
null;
awsCompileStreamEvents.serverless.service.provider.role = roleLogicalId;
expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.include(roleLogicalId);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});
describe('when a DynamoDB stream ARN is given', () => {
it('should create event source mappings when a DynamoDB stream ARN is given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
batchSize: 1,
startingPosition: 'STARTING_POSITION_ONE',
enabled: false,
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/bar/stream/2',
batchWindow: 15,
maximumRetryAttempts: 4,
},
},
{
stream: 'arn:aws:dynamodb:region:account:table/baz/stream/3',
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/buzz/stream/4',
bisectBatchOnFunctionError: true,
batchWindow: 0,
maximumRecordAgeInSeconds: 120,
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/5',
destinations: {
onFailure: 'arn:aws:sns:region:account:snstopic',
},
},
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
// event 1
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Properties.BatchSize
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.batchSize
);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Properties.StartingPosition
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
.startingPosition
);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Properties.Enabled
).to.equal(false);
// event 2
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[1].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.MaximumBatchingWindowInSeconds
).to.equal(15);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.MaximumRetryAttempts
).to.equal(4);
// event 3
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[2].stream);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.BisectBatchOnFunctionError
).to.equal(undefined);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.MaximumRecordAgeInSeconds
).to.equal(undefined);
// event 4
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[3].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.BisectBatchOnFunctionError
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.MaximumRecordAgeInSeconds
).to.equal(120);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBuzz.Properties.MaximumBatchingWindowInSeconds
).to.equal(0);
// event 5
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFizz.Properties.DestinationConfig.OnFailure
.Destination
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.destinations
.onFailure
);
});
it('should allow specifying DynamoDB and Kinesis streams as CFN reference types', () => {
awsCompileStreamEvents.serverless.service.resources.Parameters = {
SomeDdbTableStreamArn: {
Type: 'String',
},
ForeignKinesisStreamArn: {
Type: 'String',
},
};
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: { 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] },
type: 'dynamodb',
},
},
{
stream: {
arn: { 'Fn::ImportValue': 'ForeignKinesis' },
type: 'kinesis',
},
},
{
stream: {
arn: {
'Fn::Join': [
':',
[
'arn',
'aws',
'kinesis',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'stream/MyStream',
],
],
},
type: 'kinesis',
},
},
{
stream: {
arn: { Ref: 'SomeDdbTableStreamArn' },
type: 'dynamodb',
},
},
{
stream: {
arn: { Ref: 'ForeignKinesisStreamArn' },
type: 'kinesis',
},
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
// dynamodb with Fn::GetAtt
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbSomeDdbTable.Properties.EventSourceArn
).to.deep.equal({ 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] });
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[0]
).to.deep.equal({
Action: [
'dynamodb:GetRecords',
'dynamodb:GetShardIterator',
'dynamodb:DescribeStream',
'dynamodb:ListStreams',
],
Effect: 'Allow',
Resource: [
{
'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'],
},
{
Ref: 'SomeDdbTableStreamArn',
},
],
});
// kinesis with Fn::ImportValue
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisForeignKinesis.Properties.EventSourceArn
).to.deep.equal({ 'Fn::ImportValue': 'ForeignKinesis' });
// kinesis with Fn::Join
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisMyStream.Properties.EventSourceArn
).to.deep.equal({
'Fn::Join': [
':',
[
'arn',
'aws',
'kinesis',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'stream/MyStream',
],
],
});
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1]
).to.deep.equal({
Effect: 'Allow',
Action: [
'kinesis:GetRecords',
'kinesis:GetShardIterator',
'kinesis:DescribeStream',
'kinesis:ListStreams',
],
Resource: [
{
'Fn::ImportValue': 'ForeignKinesis',
},
{
'Fn::Join': [
':',
[
'arn',
'aws',
'kinesis',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'stream/MyStream',
],
],
},
{
Ref: 'ForeignKinesisStreamArn',
},
],
});
});
it('should allow specifying OnFailure destinations as CFN reference types', () => {
awsCompileStreamEvents.serverless.service.resources.Parameters = {
SomeSNSArn: {
Type: 'String',
},
ForeignSQSArn: {
Type: 'String',
},
};
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
destinations: {
onFailure: {
arn: { 'Fn::GetAtt': ['SomeSNS', 'Arn'] },
type: 'sns',
},
},
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/bar/stream/1',
destinations: {
onFailure: {
arn: { 'Fn::ImportValue': 'ForeignSQS' },
type: 'sqs',
},
},
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/baz/stream/1',
destinations: {
onFailure: {
arn: {
'Fn::Join': [
':',
[
'arn',
'aws',
'sqs',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'MyQueue',
],
],
},
type: 'sqs',
},
},
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/buzz/stream/1',
destinations: {
onFailure: {
arn: { Ref: 'SomeSNSArn' },
type: 'sns',
},
},
},
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/1',
destinations: {
onFailure: {
arn: { Ref: 'ForeignSQSArn' },
type: 'sqs',
},
},
},
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
// sns with Fn::GetAtt
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbFoo.Properties.DestinationConfig.OnFailure
.Destination
).to.deep.equal({ 'Fn::GetAtt': ['SomeSNS', 'Arn'] });
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1]
).to.deep.equal({
Action: ['sns:Publish'],
Effect: 'Allow',
Resource: [
{
'Fn::GetAtt': ['SomeSNS', 'Arn'],
},
{
Ref: 'SomeSNSArn',
},
],
});
// sqs with Fn::ImportValue
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBar.Properties.DestinationConfig.OnFailure
.Destination
).to.deep.equal({ 'Fn::ImportValue': 'ForeignSQS' });
// sqs with Fn::Join
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingDynamodbBaz.Properties.DestinationConfig.OnFailure
.Destination
).to.deep.equal({
'Fn::Join': [
':',
[
'arn',
'aws',
'sqs',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'MyQueue',
],
],
});
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[2]
).to.deep.equal({
Effect: 'Allow',
Action: ['sqs:ListQueues', 'sqs:SendMessage'],
Resource: [
{
'Fn::ImportValue': 'ForeignSQS',
},
{
'Fn::Join': [
':',
[
'arn',
'aws',
'sqs',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'MyQueue',
],
],
},
{
Ref: 'ForeignSQSArn',
},
],
});
});
it('fails if Ref/dynamic stream ARN is used without defining it to the CF parameters', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: { Ref: 'SomeDdbTableStreamArn' },
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if Ref/dynamic onFailure ARN is used without defining it to the CF parameters', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/fizz/stream/1',
destinations: {
onFailure: {
arn: { Ref: 'ForeignSQSArn' },
},
},
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if Fn::GetAtt/dynamic stream ARN is used without a type', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: { 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'] },
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if Fn::GetAtt/dynamic onFailure ARN is used without a type', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
destinations: {
onFailure: {
arn: { 'Fn::GetAtt': ['SomeSNS', 'Arn'] },
},
},
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if onFailure ARN is given as a variable type other than string or object', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
destinations: {
onFailure: 3,
},
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if no arn key is given for a dynamic onFailure ARN', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
destinations: {
onFailure: {
notarn: ['SomeSNS', 'Arn'],
},
},
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('fails if destinations structure is wrong', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
destinations: {
notOnFailure: {
arn: {
'Fn::GetAtt': ['SomeSNS', 'Arn'],
'batchSize': 1,
},
},
},
},
},
],
},
};
expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});
it('should add the necessary IAM role statements', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
{
stream: {
arn: 'arn:aws:dynamodb:region:account:table/bar/stream/2',
destinations: {
onFailure: 'arn:aws:sns:region:account:snstopic',
},
},
},
],
},
};
const iamRoleStatements = [
{
Effect: 'Allow',
Action: [
'dynamodb:GetRecords',
'dynamodb:GetShardIterator',
'dynamodb:DescribeStream',
'dynamodb:ListStreams',
],
Resource: [
'arn:aws:dynamodb:region:account:table/foo/stream/1',
'arn:aws:dynamodb:region:account:table/bar/stream/2',
],
},
{
Effect: 'Allow',
Action: ['sns:Publish'],
Resource: ['arn:aws:sns:region:account:snstopic'],
},
];
awsCompileStreamEvents.compileStreamEvents();
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement
).to.deep.equal(iamRoleStatements);
});
});
describe('when a Kinesis stream ARN is given', () => {
it('should create event source mappings when a Kinesis stream ARN is given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/foo',
batchSize: 1,
startingPosition: 'STARTING_POSITION_ONE',
enabled: false,
parallelizationFactor: 10,
},
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/bar',
batchWindow: 15,
maximumRetryAttempts: 5,
},
},
{
stream: 'arn:aws:kinesis:region:account:stream/baz',
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/buzz',
bisectBatchOnFunctionError: true,
maximumRecordAgeInSeconds: 180,
},
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:table/fizz/stream/5',
destinations: {
onFailure: 'arn:aws:sns:region:account:snstopic',
},
},
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/abc',
consumer: true,
},
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/xyz',
consumer: 'arn:aws:kinesis:region:account:stream/xyz/consumer/foobar:1558544531',
},
},
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/def',
consumer: false,
},
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
// event 1
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Properties.BatchSize
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.batchSize
);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Properties.StartingPosition
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
.startingPosition
);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Properties.ParallelizationFactor
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[0].stream
.parallelizationFactor
);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFoo.Properties.Enabled
).to.equal(false);
// event 2
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[1].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.ParallelizationFactor
).to.equal(undefined);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.MaximumBatchingWindowInSeconds
).to.equal(15);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBar.Properties.MaximumRetryAttempts
).to.equal(5);
// event 3
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[2].stream);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.BisectBatchOnFunctionError
).to.equal(undefined);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBaz.Properties.MaximumRecordAgeInSeconds
).to.equal(undefined);
// event 4
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[3].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.ParallelizationFactor
).to.equal(undefined);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.BisectBatchOnFunctionError
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisBuzz.Properties.MaximumRecordAgeInSeconds
).to.equal(180);
// event 5
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisFizz.Properties.DestinationConfig.OnFailure
.Destination
).to.equal(
awsCompileStreamEvents.serverless.service.functions.first.events[4].stream.destinations
.onFailure
);
// event 6
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.DependsOn
).to.eql(['IamRoleLambdaExecution', 'FirstabcConsumerStreamConsumer']);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.Properties.EventSourceArn
).to.eql({ Ref: 'FirstabcConsumerStreamConsumer' });
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisAbc.Properties.Enabled
).to.equal(true);
// event 7
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.Properties.EventSourceArn
).to.equal('arn:aws:kinesis:region:account:stream/xyz/consumer/foobar:1558544531');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisXyz.Properties.Enabled
).to.equal(true);
// event 8
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Type
).to.equal('AWS::Lambda::EventSourceMapping');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.DependsOn
).to.include('IamRoleLambdaExecution');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.EventSourceArn
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[7].stream.arn);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.BatchSize
).to.equal(10);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.StartingPosition
).to.equal('TRIM_HORIZON');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.Enabled
).to.equal(true);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.BisectBatchOnFunctionError
).to.equal(undefined);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstEventSourceMappingKinesisDef.Properties.MaximumRecordAgeInSeconds
).to.equal(undefined);
});
it('should create stream consumer when a Kinesis stream with consumer "true" is given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: 'arn:aws:kinesis:region:account:stream/abc',
consumer: true,
},
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstabcConsumerStreamConsumer.Type
).to.equal('AWS::Kinesis::StreamConsumer');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstabcConsumerStreamConsumer.Properties.ConsumerName
).to.equal('firstabcConsumer');
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.FirstabcConsumerStreamConsumer.Properties.StreamARN
).to.equal(awsCompileStreamEvents.serverless.service.functions.first.events[0].stream.arn);
});
it('should add the necessary IAM role statements', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: 'arn:aws:kinesis:region:account:stream/foo',
},
{
stream: 'arn:aws:kinesis:region:account:stream/bar',
},
{
stream: {
type: 'kinesis',
arn: 'arn:aws:kinesis:region:account:stream/fizz',
consumer: true,
},
},
{
stream: {
type: 'kinesis',
arn: 'arn:aws:kinesis:region:account:stream/buzz',
consumer: 'arn:aws:kinesis:region:account:stream/buzz/consumer/abc:1558544531',
},
},
],
},
};
const iamRoleStatements = [
{
Effect: 'Allow',
Action: [
'kinesis:GetRecords',
'kinesis:GetShardIterator',
'kinesis:DescribeStream',
'kinesis:ListStreams',
],
Resource: [
'arn:aws:kinesis:region:account:stream/foo',
'arn:aws:kinesis:region:account:stream/bar',
],
},
{
Effect: 'Allow',
Action: [
'kinesis:GetRecords',
'kinesis:GetShardIterator',
'kinesis:DescribeStreamSummary',
'kinesis:ListShards',
],
Resource: [
'arn:aws:kinesis:region:account:stream/fizz',
'arn:aws:kinesis:region:account:stream/buzz',
],
},
{
Effect: 'Allow',
Action: ['kinesis:SubscribeToShard'],
Resource: [
{ Ref: 'FirstfizzConsumerStreamConsumer' },
'arn:aws:kinesis:region:account:stream/buzz/consumer/abc:1558544531',
],
},
];
awsCompileStreamEvents.compileStreamEvents();
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement
).to.deep.equal(iamRoleStatements);
});
});
it('should not create event source mapping when stream events are not given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [],
},
};
awsCompileStreamEvents.compileStreamEvents();
// should be 1 because we've mocked the IamRoleLambdaExecution above
expect(
Object.keys(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources
).length
).to.equal(1);
});
it('should not add the IAM role statements when stream events are not given', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [],
},
};
awsCompileStreamEvents.compileStreamEvents();
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement.length
).to.equal(0);
});
it('should remove all non-alphanumerics from stream names for the resource logical ids', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: 'arn:aws:kinesis:region:account:stream/some-long-name',
},
],
},
};
awsCompileStreamEvents.compileStreamEvents();
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
).to.have.any.keys('FirstEventSourceMappingKinesisSomelongname');
});
});
});
describe('AwsCompileStreamEvents #2', () => {
describe('with provisioned concurrency', () => {
let naming;
let eventSourceMappingResource;
before(async () => {
const { awsNaming, cfTemplate } = await runServerless({
fixture: 'function',
configExt: {
functions: {
foo: {
provisionedConcurrency: 1,
events: [{ stream: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream' }],
},
},
},
command: 'package',
});
naming = awsNaming;
const streamLogicalId = awsNaming.getStreamLogicalId('foo', 'kinesis', 'myStream');
eventSourceMappingResource = cfTemplate.Resources[streamLogicalId];
});
it('should reference provisioned alias', () => {
expect(eventSourceMappingResource.Properties.FunctionName).to.deep.equal({
'Fn::Join': [
':',
[
{
'Fn::GetAtt': ['FooLambdaFunction', 'Arn'],
},
'provisioned',
],
],
});
});
it('should depend on provisioned alias', () => {
const aliasLogicalId = naming.getLambdaProvisionedConcurrencyAliasLogicalId('foo');
expect(eventSourceMappingResource.DependsOn).to.include(aliasLogicalId);
});
});
describe('with custom checkpoint enabled', () => {
let eventSourceMappingResource;
before(async () => {
const { awsNaming, cfTemplate } = await runServerless({
fixture: 'function',
configExt: {
functions: {
foo: {
events: [
{
stream: {
arn: 'arn:aws:kinesis:us-east-1:123456789012:stream/myStream',
functionResponseType: 'ReportBatchItemFailures',
},
},
],
},
},
},
command: 'package',
});
const streamLogicalId = awsNaming.getStreamLogicalId('foo', 'kinesis', 'myStream');
eventSourceMappingResource = cfTemplate.Resources[streamLogicalId];
});
it('should use functionResponseTypes', () => {
expect(eventSourceMappingResource.Properties.FunctionResponseTypes).to.include.members([
'ReportBatchItemFailures',
]);
});
});
});