-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Chris Schneider
authored and
Chris Schneider
committed
Jun 29, 2017
0 parents
commit 92b2249
Showing
13 changed files
with
628 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.DS_Store | ||
*.log | ||
.nyc_output/ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- '6.0' | ||
- '6.10' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2017 DieProduktMacher GmbH | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
Serverless Local Dev Server Plugin (Beta) | ||
======= | ||
|
||
[![Build Status](https://travis-ci.org/DieProduktMacher/serverless-local-dev-server.svg?branch=develop)](https://travis-ci.org/DieProduktMacher/serverless-local-dev-server) | ||
|
||
This plugin exposes your Alexa-Skill and HTTP functions as local HTTP endpoints, removing the need to deploy every change to AWS Lambda. You can connect these endpoints to Alexa or services like Messenger Bots via forwardhq, ngrok or any other forwarding tool. | ||
|
||
Supported features: | ||
|
||
* Expose `alexa-skill` and `http` events as HTTP endpoints | ||
* Environment variables | ||
* Very basic HTTP integration | ||
* Auto reload via nodemon (see *How To*) | ||
|
||
This package requires node >= 6.0 | ||
|
||
|
||
# How To | ||
|
||
### 1. Install the plugin | ||
|
||
```sh | ||
npm install serverless-local-dev-server --save-dev | ||
``` | ||
|
||
### 2. Add the plugin to your serverless configuration file | ||
|
||
*serverless.yml* configuration example: | ||
|
||
```yaml | ||
provider: | ||
name: aws | ||
runtime: nodejs6.10 | ||
|
||
functions: | ||
hello: | ||
handler: handler.hello | ||
events: | ||
- alexaSkill | ||
- http: GET / | ||
|
||
# Add serverless-local-dev-server to your plugins: | ||
plugins: | ||
- serverless-local-dev-server | ||
``` | ||
### 3. Start the server | ||
```sh | ||
serverless local-dev-server | ||
``` | ||
|
||
On default the server listens on port 5005. You can specify another one with the *--port* argument: | ||
|
||
```sh | ||
serverless local-dev-server --port 5000 | ||
``` | ||
|
||
To automatically restart the server when files change, you may use nodemon: | ||
|
||
```sh | ||
nodemon --exec "serverless local-dev-server" -e "js yml json" | ||
``` | ||
|
||
To see responses returned from Lambda and stack traces, prepend SLS_DEBUG=* | ||
|
||
```sh | ||
SLS_DEBUG=* serverless local-http-server | ||
``` | ||
|
||
### 4. For Alexa Skills | ||
|
||
#### 4.1 Share localhost with the internet | ||
|
||
For example with forwardhq: | ||
|
||
```sh | ||
forward 5005 | ||
``` | ||
|
||
#### 4.2 Configure AWS to use your HTTPS endpoint | ||
|
||
In the Configuration pane, select HTTPS as service endpoint type and specify the forwarded endpoint URL. | ||
|
||
As method for SSL Certificate validation select *My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority*. | ||
|
||
|
||
# License & Credits | ||
|
||
Licensed under the MIT license. | ||
|
||
Created and maintained by [DieProduktMacher](http://www.dieproduktmacher.com). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
{ | ||
"name": "serverless-local-http-server", | ||
"version": "0.1.0", | ||
"engines": { | ||
"node": ">=6.0" | ||
}, | ||
"description": "Develop Alexa-Skill and HTTP functions in Serverless without deploying to AWS", | ||
"author": "DieProduktMacher <www.dieproduktmacher.com>", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/DieProduktMacher/serverless-local-http-server" | ||
}, | ||
"keywords": [ | ||
"serverless", | ||
"serverless-plugin", | ||
"alexa", | ||
"alexa-skill", | ||
"http", | ||
"development", | ||
"dev", | ||
"local", | ||
"aws-lambda" | ||
], | ||
"main": "src/index.js", | ||
"scripts": { | ||
"test": "nyc mocha", | ||
"lint": "standard" | ||
}, | ||
"dependencies": { | ||
"body-parser": "^1.17.2", | ||
"express": "^4.15.3" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.0.0", | ||
"chai-as-promised": "^7.0.0", | ||
"mocha": "^3.4.2", | ||
"node-fetch": "^1.7.1", | ||
"nyc": "^10.3.2", | ||
"serverless": "^1.14.0", | ||
"sinon": "^2.3.2", | ||
"standard": "^10.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
'use strict' | ||
|
||
const Express = require('express') | ||
const BodyParser = require('body-parser') | ||
const path = require('path') | ||
const getEndpoints = require('./endpoints/get') | ||
|
||
class Server { | ||
constructor () { | ||
this.functions = [] | ||
this.defaultEnvironment = Object.assign({}, process.env) | ||
this.log = console.log | ||
} | ||
// Starts the server | ||
start (port) { | ||
if (this.functions.length === 0) { | ||
this.log('No Lambdas with Alexa-Skill or HTTP events found') | ||
return | ||
} | ||
this.app = Express() | ||
this.app.use(BodyParser.json()) | ||
this.functions.forEach(func => | ||
func.endpoints.forEach(endpoint => this._attachEndpoint(func, endpoint)) | ||
) | ||
this.app.listen(port, _ => { | ||
this.log(`Listening on port ${port} for requests 🚀`) | ||
this.log('----') | ||
this.functions.forEach(func => { | ||
this.log(`${func.name}:`) | ||
func.endpoints.forEach(endpoint => { | ||
this.log(` ${endpoint.method} http://localhost:${port}${endpoint.path}`) | ||
}) | ||
}) | ||
this.log('----') | ||
}) | ||
} | ||
// Sets functions, including endpoints, using the serverless config and service path | ||
setFunctions (serverlessConfig, servicePath) { | ||
this.functions = Object.keys(serverlessConfig.functions).map(name => { | ||
let functionConfig = serverlessConfig.functions[name] | ||
let handlerParts = functionConfig.handler.split('.') | ||
return { | ||
name: name, | ||
config: serverlessConfig.functions[name], | ||
handlerModulePath: path.join(servicePath, handlerParts[0]), | ||
handlerFunctionName: handlerParts[1], | ||
environment: Object.assign({}, serverlessConfig.provider.environment, functionConfig.environment) | ||
} | ||
}).map(func => | ||
Object.assign({}, func, { endpoints: getEndpoints(func) }) | ||
).filter(func => | ||
func.endpoints.length > 0 | ||
) | ||
} | ||
// Attaches HTTP endpoint to Express | ||
_attachEndpoint (func, endpoint) { | ||
// Validate method and path | ||
/* istanbul ignore next */ | ||
if (!endpoint.method || !endpoint.path) { | ||
return this.log(`Endpoint ${endpoint.type} for function ${func.name} has no method or path`) | ||
} | ||
// Add HTTP endpoint to Express | ||
this.app[endpoint.method.toLowerCase()](endpoint.path, (request, response) => { | ||
this.log(`${endpoint}`) | ||
// Execute Lambda with corresponding event, forward response to Express | ||
let lambdaEvent = endpoint.getLambdaEvent(request) | ||
this._executeLambdaHandler(func, lambdaEvent).then(result => { | ||
this.log(' ➡ Success') | ||
if (process.env.SLS_DEBUG) console.info(result) | ||
endpoint.handleLambdaSuccess(response, result) | ||
}).catch(error => { | ||
this.log(` ➡ Failure: ${error.message}`) | ||
if (process.env.SLS_DEBUG) console.error(error.stack) | ||
endpoint.handleLambdaFailure(response, error) | ||
}) | ||
}) | ||
} | ||
// Loads and executes the Lambda handler | ||
_executeLambdaHandler (func, event) { | ||
return new Promise((resolve, reject) => { | ||
// Load function and variables | ||
let handle = require(func.handlerModulePath)[func.handlerFunctionName] | ||
let context = { succeed: resolve, fail: reject } | ||
let callback = (error, result) => (!error) ? resolve(result) : reject(error) | ||
// Set new environment variables, execute handler function | ||
process.env = Object.assign({ IS_OFFLINE: true }, func.environment, this.defaultEnvironment) | ||
handle(event, context, callback) | ||
process.env = Object.assign({}, this.defaultEnvironment) | ||
}) | ||
} | ||
} | ||
|
||
module.exports = Server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict' | ||
|
||
const Endpoint = require('./Endpoint') | ||
|
||
class AlexaSkillEndpoint extends Endpoint { | ||
constructor (alexaSkillConfig, func) { | ||
super(alexaSkillConfig, func) | ||
this.name = func.name | ||
this.method = 'POST' | ||
this.path = `/alexa-skill/${this.name}` | ||
} | ||
getLambdaEvent (request) { | ||
// Pass-through | ||
return request.body | ||
} | ||
handleLambdaSuccess (response, result) { | ||
response.send(result) | ||
} | ||
toString () { | ||
return `Alexa-Skill: ${this.name}` | ||
} | ||
} | ||
|
||
module.exports = AlexaSkillEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
'use strict' | ||
|
||
class Endpoint { | ||
constructor (eventConfig, func) { | ||
this.method = 'GET' | ||
this.path = '' | ||
} | ||
/* istanbul ignore next */ | ||
getLambdaEvent (request) { | ||
return {} | ||
} | ||
/* istanbul ignore next */ | ||
handleLambdaSuccess (response, result) { | ||
response.sendStatus(204) | ||
} | ||
/* istanbul ignore next */ | ||
handleLambdaFailure (response, error) { | ||
response.sendStatus(500) | ||
} | ||
} | ||
|
||
module.exports = Endpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
'use strict' | ||
|
||
const path = require('path') | ||
const Endpoint = require('./Endpoint') | ||
|
||
class HttpEndpoint extends Endpoint { | ||
constructor (httpConfig, func) { | ||
super(httpConfig, func) | ||
if (typeof httpConfig === 'string') { | ||
let s = httpConfig.split(' ') | ||
httpConfig = { method: s[0], path: s[1] } | ||
} | ||
this.method = httpConfig.method | ||
this.resourcePath = httpConfig.path.replace(/\{([a-zA-Z_]+)\}/g, ':$1') | ||
this.path = path.join('/http', this.resourcePath) | ||
} | ||
getLambdaEvent (request) { | ||
return { | ||
httpMethod: request.method, | ||
body: JSON.stringify(request.body, null, ' '), | ||
queryStringParameters: request.query | ||
} | ||
} | ||
handleLambdaSuccess (response, result) { | ||
if (result.headers) { | ||
response.set(result.headers) | ||
} | ||
response.status(result.statusCode) | ||
response.send(result.body === 'object' ? JSON.stringify(result.body) : result.body) | ||
} | ||
toString () { | ||
return `HTTP: ${this.method} ${this.resourcePath}` | ||
} | ||
} | ||
|
||
module.exports = HttpEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use strict' | ||
|
||
const mappings = { | ||
'alexaSkill': require('./AlexaSkillEndpoint'), | ||
'http': require('./HttpEndpoint') | ||
} | ||
|
||
module.exports = (func) => { | ||
return func.config.events.map(event => { | ||
switch (typeof event) { | ||
case 'string': | ||
return { type: event, config: {} } | ||
case 'object': | ||
let type = Object.keys(event)[0] | ||
return { type: type, config: event[type] } | ||
/* istanbul ignore next */ | ||
default: | ||
return null | ||
} | ||
}).filter(_ => | ||
!!mappings[_.type] | ||
).map(_ => { | ||
let endpoint = new mappings[_.type](_.config, func) | ||
endpoint.type = _.type | ||
return endpoint | ||
}) | ||
} |
Oops, something went wrong.