├── .gitignore ├── LICENSE ├── README.md ├── bin └── run-local-lambda.js ├── example ├── event.json ├── lambda-error.js └── lambda.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .DS_Store 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 29 | node_modules 30 | 31 | # Optional npm cache directory 32 | .npm 33 | 34 | # Optional REPL history 35 | .node_repl_history 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Peter Sbarski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # run-local-lambda 2 | > An npm module to help you run and test Lambda functions locally 3 | 4 | This module has been designed to be run by npm as part of a test script. It is a replacement for similar grunt/gulp Lambda plugins and is useful for developers wishing to use npm for everything. 5 | 6 | * This module allows you to run and test Lambda functions on your computer or in a continuous integration setting. 7 | * You can pass in any event data JSON object to simulate a Lambda event. 8 | * The context object is taken care off for you by the module. 9 | 10 | ## Getting Started 11 | This module is designed to be run by npm to facilitate testing of Lambda functions. To install it run: 12 | 13 | ```shell 14 | npm install run-local-lambda --save-dev 15 | ``` 16 | 17 | Your Lambda function should have a package.json which you can modify to add a test script like so: 18 | 19 | ```js 20 | "scripts": { 21 | "test": "run-local-lambda --file index.js --event tests/event.json --timeout 3" 22 | } 23 | ``` 24 | 25 | Finally, you can invoke your test by simply running: 26 | 27 | ```shell 28 | npm test 29 | ``` 30 | 31 | ## Global Installation 32 | 33 | You can also install this module globally and run it from the command line: 34 | 35 | ```shell 36 | npm install -g run-local-lambda 37 | ``` 38 | 39 | 40 | To run your Lambda function, invoke the following: 41 | ```shell 42 | run-local-lambda --file index.js --event event.json 43 | ``` 44 | 45 | ## Overview 46 | ### Parameters 47 | This module accepts the following parameters which are all optional. 48 | 49 | * --file [lambda file name] - Lambda function file name. Default: index.js 50 | * --event [event file name] - Event data file name. Default: event.json 51 | * --handler [handler name] - Lambda function handler. Default: handler 52 | * --timeout [timeout seconds] - The timeout in seconds. Default: 3 53 | 54 | ### Context 55 | The context object provides the following public methods: 56 | * context.succeed(Object result) 57 | * context.fail(Error error) 58 | * context.done(Error error, Object result) 59 | * context.getRemainingTimeInMillis() 60 | 61 | Please note that the implementation of these methods are approximations to enable Lambda functions to execute. 62 | See [AWS docs](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html) for more information. 63 | 64 | ### Event 65 | The event data file can be provided using the --event parameter. An event is just a JSON object such as: 66 | 67 | ```js 68 | { 69 | "Records":[ 70 | { 71 | "eventVersion":"2.0", 72 | "eventSource":"aws:s3", 73 | "awsRegion":"us-west-2", 74 | "eventTime":"1970-01-01T00:00:00.000Z", 75 | "eventName":"ObjectCreated:Put", 76 | "userIdentity":{ 77 | "principalId":"AIDAJDPLRKLG7UEXAMPLE" 78 | }, 79 | "requestParameters":{ 80 | "sourceIPAddress":"127.0.0.1" 81 | }, 82 | "responseElements":{ 83 | "x-amz-request-id":"C3D13FE58DE4C810", 84 | "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" 85 | }, 86 | "s3":{ 87 | "s3SchemaVersion":"1.0", 88 | "configurationId":"testConfigRule", 89 | "bucket":{ 90 | "name":"sourcebucket", 91 | "ownerIdentity":{ 92 | "principalId":"A3NL1KOZZKExample" 93 | }, 94 | "arn":"arn:aws:s3:::sourcebucket" 95 | }, 96 | "object":{ 97 | "key":"HappyFace.jpg", 98 | "size":1024, 99 | "eTag":"d41d8cd98f00b204e9800998ecf8427e", 100 | "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko" 101 | } 102 | } 103 | } 104 | ] 105 | } 106 | ``` 107 | 108 | See this [AWS documentation](http://docs.aws.amazon.com/lambda/latest/dg/with-s3-example-upload-deployment-pkg.html) for more information on testing Lambda functions manually. 109 | 110 | ## Contributing 111 | There is no style guide so please try to follow the existing coding style. Please supply unit tests for any or modified functionality. Any and all PRs will be warmly welcomed. 112 | 113 | This module has been created in part to support [Serverless Architectures on AWS](https://www.manning.com/books/serverless-architectures-on-aws?a_aid=serverless-architectures-on-aws&a_bid=145280de) 114 | 115 | ## Release History 116 | ### 1.1.1 117 | Updated runner to handle the Error object correctly 118 | 119 | ### 1.1.0 120 | Updated to work with node.js 4.3 update of Lambda 121 | 122 | ### 1.0.0 123 | Initial Release 124 | -------------------------------------------------------------------------------- /bin/run-local-lambda.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | /* 4 | * The MIT License (MIT) 5 | * Copyright (c) 2016 Peter Sbarski 6 | * 7 | * 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: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * 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. 12 | * 13 | */ 14 | 15 | 'use strict'; 16 | 17 | (function(){ 18 | 19 | var path = require('path'); 20 | 21 | var settings = { 22 | file : 'index.js', 23 | event : 'event.json', 24 | timeout : 3000, 25 | handler : 'handler' 26 | }; 27 | 28 | /* 29 | * Q: Why not use commander to parse command line arguments? A. To reduce dependencies. 30 | * 31 | * Command Line Arguments: 32 | * --file, -f #Name of the file containing the Lambda function (Default: index.js) 33 | * --event, -e #Name of the file containing the event object (Default: event.json) 34 | * --timeout, -t #The timeout in seconds (Default: 3 seconds) 35 | * --handler, -h #Name of the handler function to invoke (Default: handler.exports) 36 | * 37 | * E.g. node run-local-lambda (providing index.js and event.json are in the current directory) 38 | * E.g. node run-local-lambda --file index.js --event event.json --timeout 3 --memory 128 --handler handler 39 | */ 40 | var processArguments = function(settings) { 41 | process.argv.forEach(function(argument, index, array){ 42 | switch (argument){ 43 | case '--file': { 44 | settings.file = array[index+1]; 45 | break; 46 | } 47 | 48 | case '--event': { 49 | settings.event = array[index+1]; 50 | break; 51 | } 52 | 53 | case '--timeout': { 54 | settings.timeout = parseInt(array[index+1], 10) * 1000; //convert seconds to milliseconds 55 | break; 56 | } 57 | 58 | case '--handler': { 59 | settings.handler = array[index+1]; 60 | break; 61 | } 62 | } 63 | }); 64 | } 65 | 66 | var createContext = function(timeout){ 67 | var startTime = new Date(); 68 | 69 | var succeed = function(result){ 70 | if (result !== undefined){ 71 | console.log(JSON.stringify(result)); 72 | } 73 | 74 | process.exit(); 75 | } 76 | 77 | var fail = function(error){ 78 | if (error !== undefined){ 79 | console.log(error); 80 | } 81 | 82 | process.exit(1); 83 | } 84 | 85 | var done = function(error, result){ 86 | if (error === null) { 87 | succeed(result); 88 | } else { 89 | fail(error); 90 | } 91 | } 92 | 93 | var getRemainingTimeInMillis = function(){ //Returns the approximate remaining execution time (before timeout occurs) 94 | var currentTime = new Date(); 95 | return settings.timeout - (currentTime - startTime); 96 | } 97 | 98 | var context = { 99 | getRemainingTimeInMillis: getRemainingTimeInMillis, 100 | succeed: succeed, 101 | fail: fail, 102 | done: done 103 | } 104 | 105 | return context; 106 | } 107 | 108 | var callback = function(error, result) { 109 | if (error === undefined || error === null) { 110 | if (result !== undefined && result !== null) { 111 | console.log(JSON.stringify(result)); 112 | } 113 | } else { 114 | if (error instanceof Error) { 115 | console.log({ 116 | 'errorMessage' : error.message, 117 | 'errorType' : error.name, 118 | 'stack' : error.stack 119 | }); 120 | } else { 121 | console.log('errorMessage: ' + error); 122 | } 123 | } 124 | 125 | process.exit(); 126 | } 127 | 128 | processArguments(settings); 129 | 130 | var event = require(path.resolve(settings.event, '.')); 131 | var context = createContext(settings.timeout); 132 | 133 | var lambda = require(path.resolve(settings.file, '.')); 134 | 135 | var execute = function(){ 136 | setTimeout(function(){ 137 | console.log('The function timed out after ' + settings.timeout + ' seconds'); 138 | process.exit(); 139 | }, settings.timeout); 140 | 141 | lambda[settings.handler](event, context, callback) 142 | }; 143 | 144 | execute(); 145 | })(); 146 | -------------------------------------------------------------------------------- /example/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records":[ 3 | { 4 | "eventVersion":"2.0", 5 | "eventSource":"aws:s3", 6 | "awsRegion":"us-east-1", 7 | "eventTime":"2016-12-11T00:00:00.000Z", 8 | "eventName":"ObjectCreated:Put", 9 | "userIdentity":{ 10 | "principalId":"A3MCB9FEJCFJSY" 11 | }, 12 | "requestParameters":{ 13 | "sourceIPAddress":"127.0.0.1" 14 | }, 15 | "responseElements":{ 16 | "x-amz-request-id":"3966C864F562A6A0", 17 | "x-amz-id-2":"2radsa8X4nKpba7KbgVurmc7rwe/SDoYLFid6MZKn18Nocpe3Ofwo5TJ+uJCnkf/" 18 | }, 19 | "s3":{ 20 | "s3SchemaVersion":"1.0", 21 | "configurationId":"Video Upload", 22 | "bucket":{ 23 | "name":"serverless-video-upload", 24 | "ownerIdentity":{ 25 | "principalId":"A3MCB9FEJCFJSY" 26 | }, 27 | "arn":"arn:aws:s3:::serverless-video-upload" 28 | }, 29 | "object":{ 30 | "key":"my video.mp4", 31 | "size":2236480, 32 | "eTag":"ddb7a52094d2079a27ac44f83ca669e9", 33 | "sequencer": "005686091F4FFF1565" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /example/lambda-error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by petersbarski on 30/12/2015. 3 | */ 4 | 'use strict'; 5 | 6 | exports.handler = function(event, context, callback){ 7 | console.log(event); 8 | 9 | callback(new Error("this is an error!!")); 10 | }; 11 | -------------------------------------------------------------------------------- /example/lambda.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by petersbarski on 30/12/2015. 3 | */ 4 | 'use strict'; 5 | 6 | exports.handler = function(event, context, callback){ 7 | console.log(event); 8 | 9 | callback(null, "Success!"); 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-local-lambda", 3 | "version": "1.1.1", 4 | "description": "An npm module to help you run and test Lambda functions locally", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/sbarski/run-local-lambda.git" 8 | }, 9 | "bin":{ 10 | "run-local-lambda": "bin/run-local-lambda.js" 11 | }, 12 | "keywords": [ 13 | "lambda", 14 | "local", 15 | "aws", 16 | "function", 17 | "run" 18 | ], 19 | "author": "Peter Sbarski", 20 | "license": "The MIT License", 21 | "bugs": { 22 | "url": "https://github.com/sbarski/run-local-lambda/issues" 23 | }, 24 | "homepage": "https://github.com/sbarski/run-local-lambda" 25 | } 26 | --------------------------------------------------------------------------------