Add alexa event source

Based on the great PR and plugin by @rajington 🙌
This commit is contained in:
Philipp Muens 2016-12-07 14:51:12 +01:00
parent 4ffa47f6cb
commit b5ddec70e2
8 changed files with 208 additions and 1 deletions

View File

@ -79,6 +79,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
<li><a href="./providers/aws/events/s3.md">S3</a></li>
<li><a href="./providers/aws/events/schedule.md">Schedule</a></li>
<li><a href="./providers/aws/events/sns.md">SNS</a></li>
<li><a href="./providers/aws/events/alexa.md">Alexa</a></li>
</ul>
</div>
</div>

View File

@ -0,0 +1,25 @@
<!--
title: Serverless Framework - AWS Lambda Events - Alexa
menuText: Alexa
menuOrder: 6
description: Setting up AWS Alexa Events with AWS Lambda via the Serverless Framework
layout: Doc
-->
<!-- DOCS-SITE-LINK:START automatically generated -->
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/alexa)
<!-- DOCS-SITE-LINK:END -->
# Alexa
## Event definition
This will enable your Lambda function to be called by an Alexa skill kit.
```yaml
functions:
mySkill:
handler: mySkill.handler
events:
- alexa: true
```

View File

@ -71,7 +71,7 @@ We're also using the term `normalizedName` or similar terms in this guide. This
|Lambda::Function | {normalizedFunctionName}LambdaFunction | HelloLambdaFunction |
|Lambda::Version | {normalizedFunctionName}LambdaVersion{sha256} | HelloLambdaVersionr3pgoTvv1xT4E4NiCL6JG02fl6vIyi7OS1aW0FwAI |
|Logs::LogGroup | {normalizedFunctionName}LogGroup | HelloLogGroup |
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index} </li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1 </li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li> |
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}</li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li><li>**Alexa**: {normalizedFunctionName}LambdaPermissionAlexa</li> </ul> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1</li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li><li>**Alexa**: HelloLambdaPermissionAlexa</li> </ul>|
|Events::Rule | {normalizedFuntionName}EventsRuleSchedule{SequentialID} | HelloEventsRuleSchedule1 |
|ApiGateway::RestApi | ApiGatewayRestApi | ApiGatewayRestApi |
|ApiGateway::Resource | ApiGatewayResource{normalizedPath} | ApiGatewayResourceUsers |

View File

@ -27,6 +27,7 @@
"./aws/deploy/compile/events/apiGateway/index.js",
"./aws/deploy/compile/events/sns/index.js",
"./aws/deploy/compile/events/stream/index.js",
"./aws/deploy/compile/events/alexa/index.js",
"./aws/deployFunction/index.js",
"./aws/deployList/index.js",
"./aws/invokeLocal/index.js"

View File

@ -0,0 +1,64 @@
'use strict';
const _ = require('lodash');
class AwsCompileAlexaEvents {
constructor(serverless) {
this.serverless = serverless;
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileAlexaEvents.bind(this),
};
}
compileAlexaEvents() {
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObj = this.serverless.service.getFunction(functionName);
if (functionObj.events) {
functionObj.events.forEach(event => {
if (event.alexa) {
if (typeof event.alexa === 'boolean' && event.alexa) {
const lambdaLogicalId = this.provider.naming
.getLambdaLogicalId(functionName);
const permissionTemplate = {
Type: 'AWS::Lambda::Permission',
Properties: {
FunctionName: {
'Fn::GetAtt': [
lambdaLogicalId,
'Arn',
],
},
Action: 'lambda:InvokeFunction',
Principal: 'alexa-appkit.amazon.com',
},
};
const lambdaPermissionLogicalId = this.provider.naming
.getLambdaAlexaPermissionLogicalId(functionName);
const permissionCloudForamtionResource = {
[lambdaPermissionLogicalId]: permissionTemplate,
};
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
permissionCloudForamtionResource);
} else {
const errorMessage = [
`Alexa event of function "${functionName}" is not a boolean.`,
' The correct syntax is: alexa: true.',
' Please check the docs for more info.',
].join('');
throw new this.serverless.classes.Error(errorMessage);
}
}
});
}
});
}
}
module.exports = AwsCompileAlexaEvents;

View File

@ -0,0 +1,105 @@
'use strict';
const expect = require('chai').expect;
const AwsProvider = require('../../../../provider/awsProvider');
const AwsCompileAlexaEvents = require('./index');
const Serverless = require('../../../../../../Serverless');
describe('AwsCompileAlexaEvents', () => {
let serverless;
let awsCompileAlexaEvents;
beforeEach(() => {
serverless = new Serverless();
serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} };
serverless.setProvider('aws', new AwsProvider(serverless));
awsCompileAlexaEvents = new AwsCompileAlexaEvents(serverless);
});
describe('#constructor()', () => {
it('should set the provider variable to an instance of AwsProvider', () =>
expect(awsCompileAlexaEvents.provider).to.be.instanceof(AwsProvider));
it('should should hook into the "deploy:compileEvents" hook', () =>
expect(awsCompileAlexaEvents.hooks['deploy:compileEvents']).to.not.equal(undefined));
});
describe('#compileAlexaEvents()', () => {
it('should throw an error if alexa event type is not a boolean', () => {
awsCompileAlexaEvents.serverless.service.functions = {
first: {
events: [
{
alexa: 42,
},
],
},
};
expect(() => awsCompileAlexaEvents.compileAlexaEvents()).to.throw(Error);
});
it('should create corresponding resources when event is given with value "true"', () => {
awsCompileAlexaEvents.serverless.service.functions = {
first: {
events: [
{
alexa: true,
},
],
},
};
awsCompileAlexaEvents.compileAlexaEvents();
expect(awsCompileAlexaEvents.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionAlexa.Type
).to.equal('AWS::Lambda::Permission');
expect(awsCompileAlexaEvents.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionAlexa.Properties.FunctionName
).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] });
expect(awsCompileAlexaEvents.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionAlexa.Properties.Action
).to.equal('lambda:InvokeFunction');
expect(awsCompileAlexaEvents.serverless.service
.provider.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionAlexa.Properties.Principal
).to.equal('alexa-appkit.amazon.com');
});
it('should not create corresponding resources when event is given with value "false"', () => {
awsCompileAlexaEvents.serverless.service.functions = {
first: {
events: [
{
alexa: false,
},
],
},
};
awsCompileAlexaEvents.compileAlexaEvents();
expect(
awsCompileAlexaEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
).to.deep.equal({});
});
it('should not create corresponding resources when alexa event is not given', () => {
awsCompileAlexaEvents.serverless.service.functions = {
first: {
events: [],
},
};
awsCompileAlexaEvents.compileAlexaEvents();
expect(
awsCompileAlexaEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
).to.deep.equal({});
});
});
});

View File

@ -227,4 +227,7 @@ module.exports = {
getLambdaApiGatewayPermissionLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionApiGateway`;
},
getLambdaAlexaPermissionLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexa`;
},
};

View File

@ -429,4 +429,12 @@ describe('#naming()', () => {
.to.equal('FunctionNameLambdaPermissionApiGateway');
});
});
describe('#getLambdaAlexaPermissionLogicalId()', () => {
it('should normalize the function name and append the standard suffix',
() => {
expect(sdk.naming.getLambdaAlexaPermissionLogicalId('functionName'))
.to.equal('FunctionNameLambdaPermissionAlexa');
});
});
});