lambda-local
Advanced tools
Comparing version 1.4.8 to 1.5.0
# ChangeLog | ||
## 1.5.0 (2018/06/06) | ||
* Make context an object | ||
* Support multi-threading & nested calls | ||
* Context is regenerated on each run (e.g. awsRequestId will change) | ||
## 1.4.8 (2018/05/26) | ||
@@ -4,0 +9,0 @@ * Read default AWS config files |
@@ -12,48 +12,43 @@ 'use strict'; | ||
var logger, | ||
unmute, | ||
verboseLevel, | ||
finalCallback; | ||
function Context() { | ||
this.logger = null; | ||
this.unmute = null; | ||
this.verboseLevel = null; | ||
this.finalCallback = null; | ||
/* | ||
* doneStatus & postDone were minimum; probably defined internally in Lambda. | ||
*/ | ||
this.doneStatus = false; | ||
this.postDone = function(error, message) {}; | ||
/* | ||
* Used to determine the getRemainingTimeInMillis() | ||
*/ | ||
this.startTime; | ||
this.timeout; | ||
/* Internal usage: timer handle */ | ||
this._timeout; | ||
/* | ||
* Context object properties | ||
*/ | ||
this.callbackWaitsForEmptyEventLoop = true; | ||
this.functionName = ''; | ||
this.functionVersion = '1'; | ||
this.invokedFunctionArn = 'a'; | ||
this.memoryLimitInMB = 1; | ||
this.awsRequestId = ''; | ||
this.logGroupName = 'a'; | ||
this.logStreamName = null; | ||
this.identity = null; | ||
this.clientContext = null; | ||
/* | ||
* doneStatus & postDone were minimum; probably defined internally in Lambda. | ||
*/ | ||
var doneStatus = false; | ||
var postDone = function(error, message) {}; | ||
/* | ||
* callback function called after done | ||
*/ | ||
this.callback = function(result) { | ||
return result; | ||
}; | ||
} | ||
/* | ||
* Used to determine the getRemainingTimeInMillis() | ||
*/ | ||
var startTime; | ||
var timeout; | ||
function Context() {} | ||
/* | ||
* exports | ||
*/ | ||
module.exports = Context; | ||
/* | ||
* Context object properties | ||
*/ | ||
Context.callbackWaitsForEmptyEventLoop = true; | ||
Context.functionName = ''; | ||
Context.functionVersion = '1'; | ||
Context.invokedFunctionArn = 'a'; | ||
Context.memoryLimitInMB = 1; | ||
Context.awsRequestId = ''; | ||
Context.logGroupName = 'a'; | ||
Context.logStreamName = null; | ||
Context.identity = null; | ||
Context.clientContext = null; | ||
/* | ||
* callback function called after done | ||
*/ | ||
Context.callback = function(result) { | ||
return result; | ||
}; | ||
/* | ||
* create random invokeid. | ||
@@ -63,3 +58,3 @@ * Assuming that invokeid follows the format: | ||
*/ | ||
Context.createInvokeId = (function() { | ||
const createInvokeId = function() { | ||
return [ | ||
@@ -72,29 +67,29 @@ utils.generateRandomHex(8), | ||
].join('-'); | ||
})(); | ||
} | ||
/* | ||
* Context initialization. | ||
* Called from lambda-local | ||
* Called from lambdalocal.js | ||
*/ | ||
Context._initialize = function(options) { | ||
Context.prototype._initialize = function(options) { | ||
/* set time */ | ||
startTime = new Date().getTime(); | ||
timeout = options.timeoutMs; | ||
this.startTime = new Date().getTime(); | ||
this.timeout = options.timeoutMs; | ||
logger = options.logger; | ||
verboseLevel = options.verboseLevel; | ||
finalCallback = options.finalCallback; | ||
this.logger = options.logger; | ||
this.verboseLevel = options.verboseLevel; | ||
this.finalCallback = options.finalCallback; | ||
/* set function name */ | ||
Context.functionName = options.functionName; | ||
this.functionName = options.functionName; | ||
/* set requestid */ | ||
Context.awsRequestId = options.awsRequestId; | ||
this.awsRequestId = createInvokeId(); | ||
/* Set callbackWaitsForEmptyEventLoop */ | ||
Context.callbackWaitsForEmptyEventLoop = options.callbackWaitsForEmptyEventLoop; | ||
this.callbackWaitsForEmptyEventLoop = options.callbackWaitsForEmptyEventLoop; | ||
logger.log('info', 'START RequestId: ' + Context.awsRequestId); | ||
if (verboseLevel < 3){ | ||
unmute = mute(); | ||
this.logger.log('info', 'START RequestId: ' + this.awsRequestId); | ||
if (this.verboseLevel < 3){ | ||
this.unmute = mute(); | ||
} | ||
@@ -104,13 +99,47 @@ return; | ||
/* Internal usage: timer handle */ | ||
Context._timeout = null; | ||
/* | ||
* Timeout initialization. | ||
* Called from lambdalocal.js | ||
*/ | ||
Context.prototype._init_timeout = function(){ | ||
/* Handling timeout */ | ||
this._timeout = setTimeout(function() { | ||
throw new utils.TimeoutError('Task timed out after ' + (this.timeout / 1000).toFixed(2) + ' seconds'); | ||
}, this.timeout); | ||
} | ||
/* | ||
* Util function used in lambdalocal.js to get parameters for the handler | ||
*/ | ||
Context.prototype.generate_context = function(){ | ||
//https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html | ||
var ctx = { | ||
//FUNCTIONS | ||
done: this.done.bind(this), | ||
fail: this.fail.bind(this), | ||
succeed: this.succeed.bind(this), | ||
getRemainingTimeInMillis: this.getRemainingTimeInMillis.bind(this), | ||
//VARS | ||
callbackWaitsForEmptyEventLoop: this.callbackWaitsForEmptyEventLoop, | ||
functionName: this.functionName, | ||
functionVersion: this.functionVersion, | ||
invokedFunctionArn: this.invokedFunctionArn, | ||
memoryLimitInMB: this.memoryLimitInMB, | ||
awsRequestId: this.awsRequestId, | ||
logGroupName: this.logGroupName, | ||
logStreamName: this.logStreamName, | ||
identity: this.identity, | ||
clientContext: this.clientContext | ||
}; | ||
return ctx; | ||
} | ||
/* | ||
* This `done` method is directly extracted from source. | ||
*/ | ||
Context.done = function(err, message) { | ||
clearTimeout(Context._timeout); | ||
if(unmute != null) { | ||
unmute(); | ||
unmute = null; | ||
Context.prototype.done = function(err, message) { | ||
clearTimeout(this._timeout); | ||
if(this.unmute != null) { | ||
this.unmute(); | ||
this.unmute = null; | ||
} | ||
@@ -131,20 +160,20 @@ | ||
if (err !== null && typeof err !== 'undefined') { | ||
logger.log('error', 'End - Error'); | ||
logger.log('error', '------'); | ||
utils.outputJSON(err, verboseLevel == 1 ? console : logger, 'error'); | ||
logger.log('error', '------'); | ||
this.logger.log('error', 'End - Error'); | ||
this.logger.log('error', '------'); | ||
utils.outputJSON(err, this.verboseLevel == 1 ? console : this.logger, 'error'); | ||
this.logger.log('error', '------'); | ||
} else { | ||
logger.log('info', 'End - Message'); | ||
logger.log('info', '------'); | ||
utils.outputJSON(message, verboseLevel == 1 ? console : logger, 'info'); | ||
logger.log('info', '------'); | ||
this.logger.log('info', 'End - Message'); | ||
this.logger.log('info', '------'); | ||
utils.outputJSON(message, this.verboseLevel == 1 ? console : this.logger, 'info'); | ||
this.logger.log('info', '------'); | ||
} | ||
this.callback(err, message); | ||
/* | ||
# TODO | ||
The following method should only be called if 'Context.callbackWaitsForEmptyEventLoop' is False | ||
The following method should only be called if 'this.callbackWaitsForEmptyEventLoop' is False | ||
Otherwise, it should wait for an empty loop then call it. | ||
*/ | ||
Context.callback(err, message); | ||
finalCallback(); | ||
}; | ||
this.finalCallback(); | ||
} | ||
@@ -154,4 +183,4 @@ /* | ||
*/ | ||
Context.fail = function(err) { | ||
Context.done(err); | ||
Context.prototype.fail = function(err) { | ||
this.done(err); | ||
}; | ||
@@ -162,4 +191,4 @@ | ||
*/ | ||
Context.succeed = function(message) { | ||
Context.done(null, message); | ||
Context.prototype.succeed = function(message) { | ||
this.done(null, message); | ||
}; | ||
@@ -170,5 +199,10 @@ | ||
*/ | ||
Context.getRemainingTimeInMillis = function() { | ||
Context.prototype.getRemainingTimeInMillis = function() { | ||
var now = new Date().getTime(); | ||
return (timeout + startTime - now); | ||
return (this.timeout + this.startTime - now); | ||
}; | ||
/* | ||
* exports | ||
*/ | ||
module.exports = Context; |
@@ -14,2 +14,3 @@ 'use strict'; | ||
const utils = require('./utils.js'); | ||
const Context = require('./context.js'); | ||
@@ -131,6 +132,5 @@ var _setLogger = function(_logger){ | ||
// load context | ||
var context = require('./context.js'); | ||
var context = new Context(); | ||
context._initialize({ | ||
functionName: lambdaHandler, | ||
awsRequestId: context.createInvokeId, | ||
timeoutMs: timeoutMs, | ||
@@ -151,6 +151,8 @@ callbackWaitsForEmptyEventLoop: callbackWaitsForEmptyEventLoop, | ||
}); | ||
if(callback) context.callback = callback; | ||
var ctx = context.generate_context(); | ||
try { | ||
if(callback) context.callback = callback; | ||
// load lambda function | ||
@@ -166,18 +168,17 @@ if (!(lambdaFunc)){ | ||
// Handling timeout | ||
context._timeout = setTimeout(function() { | ||
throw new utils.TimeoutError('Task timed out after ' + (timeoutMs / 1000).toFixed(2) + ' seconds'); | ||
}, timeoutMs); | ||
//start timeout | ||
context._init_timeout(); | ||
// execute lambda function | ||
var result = lambdaFunc[lambdaHandler](event, context, context.done); | ||
var result = lambdaFunc[lambdaHandler](event, ctx, ctx.done); | ||
if (result) { | ||
if (result.then) { | ||
result.then(context.succeed, context.fail); | ||
result.then(ctx.succeed, ctx.fail); | ||
} else { | ||
context.succeed(result); | ||
ctx.succeed(result); | ||
} | ||
} | ||
} catch(err){ | ||
context.fail(err); | ||
console.log(err); | ||
ctx.fail(err); | ||
} | ||
@@ -184,0 +185,0 @@ }; |
{ | ||
"name": "lambda-local", | ||
"version": "1.4.8", | ||
"version": "1.5.0", | ||
"description": "Commandline tool to run Lambda functions on your local machine.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
141
test/test.js
@@ -33,4 +33,9 @@ "use strict"; | ||
function process_outputs(r){ | ||
r.output = r.output.toString('utf8').slice(1, -1); | ||
r.stderr = r.stderr.toString('utf8').slice(1, -1); | ||
} | ||
function get_node_major_version(){ | ||
return parseInt(process.version[1]); | ||
return parseInt(process.version.slice(1).split('.')[0]); | ||
} | ||
@@ -83,2 +88,4 @@ | ||
var firstawsRequestId; | ||
describe("- Testing lambdalocal.js", function () { | ||
@@ -114,2 +121,3 @@ describe("* Basic Run", function () { | ||
}); | ||
describe("# Environment Variables", function () { | ||
@@ -134,2 +142,30 @@ it("should return correct environment variables", function () { | ||
describe("# LambdaLocal", function () { | ||
it("should return correct testvar", function () { | ||
assert.equal(done.result, "testvar"); | ||
}); | ||
}); | ||
describe("# Context object", function () { | ||
it("should contain initialized functionName", function () { | ||
assert.equal(done.context.functionName, functionName); | ||
}); | ||
it("should contain initialized awsRequestId", function () { | ||
assert.equal(done.context.awsRequestId.length, 36); | ||
firstawsRequestId = done.context.awsRequestId; | ||
}); | ||
it("should contain initialized getRemainingTimeInMillis", function () { | ||
assert.isAtMost(done.context.getRemainingTimeInMillis(), timeoutMs); | ||
}); | ||
it("should contain done function", function () { | ||
assert.isDefined(done.context.done); | ||
}); | ||
it("should contain succeed function", function () { | ||
assert.isDefined(done.context.succeed); | ||
}); | ||
it("should contain fail function", function () { | ||
assert.isDefined(done.context.fail); | ||
}); | ||
}); | ||
describe("# Environment Variables (destroy)", function () { | ||
@@ -163,2 +199,5 @@ var done, err; | ||
}); | ||
it("should contain an awsRequestId different from the first one", function () { | ||
assert.notEqual(done.context.awsRequestId, firstawsRequestId); | ||
}); | ||
}); | ||
@@ -175,28 +214,2 @@ | ||
describe("# LambdaLocal", function () { | ||
it("should return correct testvar", function () { | ||
assert.equal(done.result, "testvar"); | ||
}); | ||
}); | ||
describe("# Context object", function () { | ||
it("should contain initialized functionName", function () { | ||
assert.equal(done.context.functionName, functionName); | ||
}); | ||
it("should contain initialized awsRequestId", function () { | ||
assert.equal(done.context.awsRequestId.length, 36); | ||
}); | ||
it("should contain initialized getRemainingTimeInMillis", function () { | ||
assert.isAtMost(done.context.getRemainingTimeInMillis(), timeoutMs); | ||
}); | ||
it("should contain done function", function () { | ||
assert.isDefined(done.context.done); | ||
}); | ||
it("should contain succeed function", function () { | ||
assert.isDefined(done.context.succeed); | ||
}); | ||
it("should contain fail function", function () { | ||
assert.isDefined(done.context.fail); | ||
}); | ||
}); | ||
describe("* Mocked function", function () { | ||
@@ -271,2 +284,32 @@ var done, err; | ||
}); | ||
describe('* Nested Run', function () { | ||
it("Should handle nested calls properly (context singleton)", function (cb) { | ||
var lambdalocal = require("../lib/lambdalocal.js"); | ||
lambdalocal.setLogger(winston); | ||
lambdalocal.execute({ | ||
event: require(path.join(__dirname, "./events/test-event.js")), | ||
lambdaPath: path.join(__dirname, "./functs/test-func.js"), | ||
lambdaHandler: functionName, | ||
callbackWaitsForEmptyEventLoop: false, | ||
timeoutMs: timeoutMs, | ||
callback: function (err, done) { | ||
assert.equal(done.result, "testvar"); | ||
//Second run | ||
lambdalocal.execute({ | ||
event: require(path.join(__dirname, "./events/test-event.js")), | ||
lambdaPath: path.join(__dirname, "./functs/test-func.js"), | ||
lambdaHandler: functionName, | ||
callbackWaitsForEmptyEventLoop: false, | ||
timeoutMs: timeoutMs, | ||
callback: function (_err, _done) { | ||
assert.equal(_done.result, "testvar"); | ||
cb(); | ||
}, | ||
verboseLevel: 0 | ||
}); | ||
}, | ||
verboseLevel: 0 | ||
}); | ||
}); | ||
}); | ||
if (get_node_major_version() >= 2){ | ||
@@ -354,5 +397,6 @@ describe('* Promised Run', function () { | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 0); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.stderr.toString('utf8')); | ||
console.log(r.output); | ||
console.log(r.stderr); | ||
}); | ||
@@ -363,5 +407,6 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 0); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.stderr.toString('utf8')); | ||
console.log(r.output); | ||
console.log(r.stderr); | ||
}); | ||
@@ -372,5 +417,6 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 0); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.stderr.toString('utf8')); | ||
console.log(r.output); | ||
console.log(r.stderr); | ||
}); | ||
@@ -383,4 +429,5 @@ }); | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -391,4 +438,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -399,4 +447,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -407,4 +456,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -415,4 +465,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -423,4 +474,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -431,4 +483,5 @@ | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 1); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
}); | ||
@@ -441,4 +494,5 @@ }); | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 0); | ||
console.log(r.output.toString('utf8')); | ||
console.log(r.output); | ||
//test included in test-func-env.js | ||
@@ -464,9 +518,10 @@ assert.equal(!("TEST_HUBID" in process.env), true); | ||
describe("* Verbose test", function () { | ||
it("should end normally", function () { | ||
it("should have no output", function () { | ||
var command = get_shell("node ../bin/lambda-local -l ./functs/test-func-print.js -e ./events/test-event.js -v 0"); | ||
var r = spawnSync(command[0], command[1]); | ||
process_outputs(r); | ||
assert.equal(r.status, 0); | ||
assert.equal(r.output.toString('utf8'), ",,") | ||
assert.equal(r.output, "") | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
73961
1512
54