lambda-tools
Version:
Scripts for working with AWS Lambda backed microservices
122 lines (99 loc) • 3.26 kB
JavaScript
// Wrapper for a Lambda function executing in a different process
;
const _ = require('lodash');
function sendError(pid, err, callback) {
const message = err.message || err;
console.error(message);
if (err.stack) {
console.error(err.stack);
}
process.send({ result: message, type: 'error' }, null, callback);
}
function sendResult(pid, result, callback) {
process.send({ result: _.isString(result) ? result : JSON.stringify(result), type: 'result' }, null, callback);
}
function exit(error, result) {
if (error) {
sendError(process.pid, error, function() {
process.exit(1);
});
return;
}
sendResult(process.pid, result, function() {
process.exit(0);
});
}
function parseArgs(args) {
return args.reduce(function(ret, val, id) {
if (id === 2) {
ret.path = val;
} else if (id === 3) {
ret.handler = val;
} else if (id === 4) {
ret.event = JSON.parse(val);
} else if (id === 5) {
ret.context = JSON.parse(val);
} else if (id === 6) {
ret.timeout = val;
}
return ret;
}, { });
}
function wrapper(path, handler, evt, context, timeout) {
handler = handler || 'handler';
evt = evt || {};
let finished = false;
// Timeout monitoring
const now = new Date().getTime();
const deadline = now + timeout;
// Listen to premature exits
process.on('beforeExit', function() {
if (!finished) {
context.fail(new Error('Process exited without completing request'));
}
});
// Create a context object to pass along to the Lambda function
context = _.assign(context, {
done: function(error, result) {
exit(error, result);
},
succeed: function(result) {
exit(null, result);
},
fail: function(error) {
exit(error, null);
},
getRemainingTimeInMillis: function() {
return deadline - (new Date()).getTime();
},
callbackWaitsForEmptyEventLoop: true
});
try {
const fn = require(path)[handler];
// Detect if it uses the callback pattern or not
if (fn.length === 3) {
// Callback is used, in our case we will just send out
// the result, but we will not forcefully
// exit the process. This should be similar to how Lambda does it,
// where it naturally then let's the process terminate
fn(evt, context, function(err, data) {
finished = true;
if (!context.callbackWaitsForEmptyEventLoop) {
// Exit the process as well
exit(err, data);
} else {
if (err) sendError(process.pid, err);
sendResult(process.pid, data);
}
});
} else {
fn(evt, context);
}
} catch(err) {
sendError(process.pid, err, function() {
process.exit(1);
});
}
}
const args = parseArgs(process.argv);
wrapper(args.path, args.handler, args.event, args.context, args.timeout);