Skip to content

Commit

Permalink
Merge pull request #52 from ashiina/develop
Browse files Browse the repository at this point in the history
Release 1.2.0
  • Loading branch information
ashiina committed Nov 23, 2016
2 parents cc92fea + 95db7b0 commit b64ab82
Show file tree
Hide file tree
Showing 18 changed files with 538 additions and 324 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
@@ -1,9 +1,9 @@
language: node_js
node_js:
- "1.8"
- "2.5"
- "3.3"
- "4.4"
- "4"
- "6"
- "node"
sudo: false
#cache:
# directories:
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
@@ -1,6 +1,15 @@
# ChangeLog

## 1.0.0 (2016/6/xx)
## 1.2.0 (2016/11/23)
* Added mock functionality
* Dropped Node.js v0.1, v0.12 suport

## 1.1.0 (2016/9/15)
* The default behavior of lambda-local now does not forcefully call the callback function (`-c` option).
* Added AWS region option `-r`. Defaults to `us-east-1`.
* Added AWS profile name option `-p`.

## 1.0.0 (2016/6/10)
* lambda-local can now be imported as a node module, and be executed from other node.js programs

## 0.0.10 (2016/5/29)
Expand Down
59 changes: 21 additions & 38 deletions README.md
Expand Up @@ -25,32 +25,10 @@ lambda-local -l index.js -h handler -e event-samples/s3-put.js

### In another node.js script

You can also use Lambda local directly in a script. For instance, it is interesting in a [mocha][1] test suite in combination with [istanbull][2] in order to get test coverage.

```js
const lambdaLocal = require('lambda-local');

var jsonPayload = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3'
}

lambdaLocal.execute({
event: jsonPayload,
lambdaPath: path.join(__dirname, 'path/to/index.js'),
profilePath: '~/.aws/credentials',
profileName: 'default',
timeoutMs: 3000,
callback: function(err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
}
});
```
You can also use Lambda local directly in a script. For instance, it is interesting in a [MochaJS][1] test suite in order to get test coverage.

See [API](#api) for more infos

## About

Expand All @@ -60,8 +38,9 @@ lambdaLocal.execute({
* -h, --handler <handler name> (optional) Lambda function handler name. Default is "handler".
* -t, --timeout <timeout> (optional) Seconds until lambda function timeout. Default is 3 seconds.
* -n, --no-force-callback (optional) Force the function to stop after having called the handler function even if context.done/succeed/fail was not called.
* -p, --profile <aws profile name> (optional) Read the AWS profile to get the credentials from file name.
* -p, --profile-path <aws profile name> (optional) Read the specified AWS credentials file.
* -r, --region <aws region> (optional) Sets the AWS region, defaults to us-east-1.
* -P, --profile-path <aws profile name> (optional) Read the specified AWS credentials file.
* -p, --profile <aws profile name> (optional) Use with **-P**: Read the AWS profile of the file.

### Event data
Event sample data are placed in `event-samples` folder - feel free to use the files in here, or create your own event data.
Expand All @@ -85,27 +64,31 @@ http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cl

## API

### LambdLocal
### LambdaLocal

#### `execute(options)`

Executes a lambda given the `options` object where keys are:
- `event` - requested event as a json object,
- `lambdaPath` - requested path to the lambda function,
- `profilePath` - optional path to your AWS credentials file
- `profileName` - optional aws profile name
- `lambdaFunc` - pass the lambda function. You cannot use it at the same time as lambdaPath,
- `profilePath` - optional, path to your AWS credentials file
- `profileName` - optional, aws profile name. Must be used with
- `lambdaHandler` - optional handler name, default to `handler`
- `region` - optional AWS region, default to `us-east-1`
- `callbackWaitsForEmptyEventLoop` - optional, default to `true` which forces the function to stop after having called the handler function even if context.done/succeed/fail was not called.
- `timeoutMs` - optional timeout, default to 3000 ms
- `region` - optional, AWS region, default to `us-east-1`
- `callbackWaitsForEmptyEventLoop` - optional, default to `true`. Setting it to `false` will call the callback when your code do, before finishing lambda-local.
- `timeoutMs` - optional, timeout, default to 3000 ms
- `mute` - optional, allows to mute console.log calls in the lambda function, default false
- `callback` - optional lambda third parameter [callback][3]
- `callback` - optional, lambda third parameter [callback][1]

#### `setLogger(logger)`

If you are using [winston](https://www.npmjs.com/package/winston), this pass a winston logger instead of the console.

#### [Samples](REQUIRE_SAMPLES.md)

## License

This library is released under the MIT license.

[1]: https://mochajs.org/
[2]: http://gotwarlost.github.io/istanbul/
[3]: http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

[1]: http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
85 changes: 85 additions & 0 deletions REQUIRE_SAMPLES.md
@@ -0,0 +1,85 @@
## Samples for API

#### Basic: In another node.js script

```js
const lambdaLocal = require('lambda-local');

var jsonPayload = {
'key': 1,
'another_key': "Some text"
}

lambdaLocal.execute({
event: jsonPayload,
lambdaPath: path.join(__dirname, 'path_to_index.js'),
profilePath: '~/.aws/credentials',
profileName: 'default',
timeoutMs: 3000,
callback: function(err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
}
});
```

### Use lambda-local to Mock

You can use Lambda local to mock your lambda then run it, using [MochaJS][1] and [SinonJS][2]

In this sample, we assume that you got a test function like this:
```js
/*
* Lambda function used to test mocking.
*/
exports.getData = function getData(){
return "WrongData";
}
exports.handler = function(event, context) {
context.done(null, exports.getData());
};
```

Then you will be able to use in your test.js mocha file, something like:

```js

//An empty event
var jsonPayload = {

}

var done, err;
before(function (cb) {
var lambdalocal = require('lambda-local');
lambdalocal.setLogger(your_winston_logger);
var lambdaFunc = require("path_to_test-function.js");
//For instance, this will replace the getData content
sinon.mock(lambdaFunc).expects("getData").returns("MockedData");
//see on sinonjs page for more options
lambdalocal.execute({
event: jsonPayload,
lambdaFunc: lambdaFunc, //We are directly passing the lambda function
lambdaHandler: "handler",
callbackWaitsForEmptyEventLoop: true,
timeoutMs: 3000,
callback: function (_err, _done) { //We are storing the results and finishing the before() call => one lambda local call for multiple tests
err = _err;
done = _done;
cb();
}
});
describe("Your first test", function () {
it("should return mocked value", function () {
assert.equal(done, "MockedData");
});
});
... Other tests
});
```

[1]: https://mochajs.org/
[2]: http://sinonjs.org/
162 changes: 86 additions & 76 deletions bin/lambda-local
Expand Up @@ -6,91 +6,101 @@
* Local executor for Amazon Lambda function
*/
(function() {
var lambdaLocal = require('../lib/lambdalocal.js'),
utils = require('../lib/utils.js');

// process opts
var program = require('commander');
program
.option('-l, --lambda-path <lambda index path>', '(required) Lambda function file name.')
.option('-e, --event-path <path>', '(required) Event data file name.')
.option('-h, --handler <handler name>',
'(optional) Lambda function handler name. Default is \'handler\'.')
.option('-t, --timeout <timeout seconds>',
'(optional) Seconds until lambda function timeout. Default is 3 seconds.')
.option('-n, --no-force-callback',
'(optional) Force the function to stop after having called the handler function' +
' even if context.done/succeed/fail was not called.')
.option('-r, --region <aws region>',
'(optional) default set to us-east-1')
.option('-p, --profile <aws profile name>',
'(optional) Read the AWS profile to get the credentials from profile name')
.option('-P, --profile-path <aws credentials path>',
'(optional) Read the specified AWS credentials file')
.parse(process.argv);
var winston = require('winston');
var logger = new winston.Logger({
level: 'info',
transports: [
new (winston.transports.Console)({handleExceptions: true, json: false, colorize: true})
]
});

var eventPath = program.eventPath,
lambdaPath = program.lambdaPath,
lambdaHandler = program.handler,
profilePath = program.profilePath,
profileName = program.profile,
region = program.region,
callbackWaitsForEmptyEventLoop = program.noForceCallback;
var lambdaLocal = require('../lib/lambdalocal.js'),
utils = require('../lib/utils.js');
lambdaLocal.setLogger(logger);

if (!lambdaPath || !eventPath) {
program.help();
}
// process opts
var program = require('commander');
program
.option('-l, --lambda-path <lambda index path>', '(required) Lambda function file name.')
.option('-e, --event-path <path>', '(required) Event data file name.')
.option('-h, --handler <handler name>',
'(optional) Lambda function handler name. Default is \'handler\'.')
.option('-t, --timeout <timeout seconds>',
'(optional) Seconds until lambda function timeout. Default is 3 seconds.')
.option('-n, --no-force-callback',
'(optional) Force the function to stop after having called the handler function' +
' even if context.done/succeed/fail was not called.')
.option('-r, --region <aws region>',
'(optional) default set to us-east-1')
.option('-p, --profile <aws profile name>',
'(optional) Read the AWS profile to get the credentials from profile name')
.option('-P, --profile-path <aws credentials path>',
'(optional) Read the specified AWS credentials file')
.parse(process.argv);

// default handler name
if (!lambdaHandler) {
lambdaHandler = 'handler';
}
var eventPath = program.eventPath,
lambdaPath = program.lambdaPath,
lambdaHandler = program.handler,
profilePath = program.profilePath,
profileName = program.profile,
region = program.region,
callbackWaitsForEmptyEventLoop = program.noForceCallback;

//default callbackWaitsForEmptyEventLoop
if (!callbackWaitsForEmptyEventLoop) {
callbackWaitsForEmptyEventLoop = false;
} else {
callbackWaitsForEmptyEventLoop = true;
}
if (!lambdaPath || !eventPath) {
program.help();
}

// timeout milliseconds
var timeoutMs;
if (program.timeout) {
timeoutMs = program.timeout * 1000;
} else {
timeoutMs = 3000;
}
// default handler name
if (!lambdaHandler) {
lambdaHandler = 'handler';
}

var event = require(utils.getAbsolutePath(eventPath));
//default callbackWaitsForEmptyEventLoop
if (!callbackWaitsForEmptyEventLoop) {
callbackWaitsForEmptyEventLoop = false;
} else {
callbackWaitsForEmptyEventLoop = true;
}

// execute
lambdaLocal.execute({
event: event,
lambdaPath: lambdaPath,
lambdaHandler: lambdaHandler,
profilePath: profilePath,
profileName: profileName,
region: region,
callbackWaitsForEmptyEventLoop: callbackWaitsForEmptyEventLoop,
timeoutMs: timeoutMs,
callback: function(err /*, data */) { //data unused
console.log('-----');
if (err === null) {
console.log('lambda-local successfully complete.');
process.exit(0);
} else {
console.log('lambda-local failed.');
// Finish the process
process.exit(1);
}
// timeout milliseconds
var timeoutMs;
if (program.timeout) {
timeoutMs = program.timeout * 1000;
} else {
timeoutMs = 3000;
}
});

// Handling timeout
setTimeout(function() {
console.log('-----');
console.log('Task timed out after ' + (timeoutMs / 1000).toFixed(2) + ' seconds');
process.exit();
}, timeoutMs);
var event = require(utils.getAbsolutePath(eventPath));

// execute
lambdaLocal.execute({
event: event,
lambdaPath: lambdaPath,
lambdaHandler: lambdaHandler,
profilePath: profilePath,
profileName: profileName,
region: region,
callbackWaitsForEmptyEventLoop: callbackWaitsForEmptyEventLoop,
timeoutMs: timeoutMs,
callback: function(err /*, data */) { //data unused
logger.log('info', '-----');
if (err === null) {
logger.log('info', 'lambda-local successfully complete.');
process.exit(0);
} else {
logger.log('error', 'lambda-local failed.');
// Finish the process
process.exit(1);
}
}
});

// Handling timeout
setTimeout(function() {
logger.log('error', '-----');
logger.log('error', 'Task timed out after ' + (timeoutMs / 1000).toFixed(2) + ' seconds');
process.exit();
}, timeoutMs);

})();
1 change: 0 additions & 1 deletion lib/.tern-port

This file was deleted.

0 comments on commit b64ab82

Please sign in to comment.