Skip to content
32 changes: 24 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,56 @@
'use strict';

const packagePath = 'node_modules/serverless-offline-direct-lambda';
const handlerPath = `proxy.js`;
const handlerPath = 'proxy.js';

class ServerlessPlugin {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;

const boundStartHandler = this.startHandler.bind(this);

this.hooks = {
"before:offline:start:init": this.startHandler.bind(this),
'before:offline:start': boundStartHandler,
'before:offline:start:init': boundStartHandler,
};
}

startHandler() {
// Serverless Webpack overrides the location to its output directory. Set
// location to that directory.
let location = '';
try {
location = this.serverless.service.custom['serverless-offline'].location;
this.serverless.service.custom['serverless-offline'].location = '';
} catch (_) { }

location = `${this.serverless.config.servicePath}/${location}`;

this.serverless.cli.log('Running Serverless Offline with direct lambda support');

addProxies(this.serverless.service.functions);
addProxies(this.serverless.service.functions, location);
}
}

const addProxies = functionsObject => {
const addProxies = (functionsObject, location) => {
Object.keys(functionsObject).forEach(fn => {

// filter out functions with event config,
// leaving just those intended for direct lambda-to-lambda invocation
const functionObject = functionsObject[fn];
if (!functionObject.events || functionObject.events.length == 0) {
const pf = functionProxy(functionObject);
if (!functionObject.events ||
!functionObject.events.some((event) => Object.keys(event)[0] === 'http')) {
const pf = functionProxy(functionObject, location);
functionsObject[pf.name] = pf;
}
});
};

const functionProxy = functionBeingProxied => ({
const functionProxy = (functionBeingProxied, location) => ({
name: `${functionBeingProxied.name}_proxy`,
handler: `${packagePath}/proxy.handler`,
environment: functionBeingProxied.environment,
events: [
{
http: {
Expand All @@ -46,8 +61,9 @@ const functionProxy = functionBeingProxied => ({
template: {
'application/json': JSON.stringify(
{
location,
body: "$input.json('$')",
targetHandler : functionBeingProxied.handler,
body: "$input.json('$')"
}
)
}
Expand Down
50 changes: 33 additions & 17 deletions proxy.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
const serializeError = require('serialize-error');
const path = require('path');

async function handler(event, context) {
const { ClientContext, FunctionName, InvocationType, LogType, Payload } = event.body;

function handler(event, context, callback) {
// extract the path to the handler (relative to the project root)
// and the function to call on the handler
const [targetHandlerFile, targetHandlerFunction] = event.targetHandler.split(".");
const target = require('../../' + targetHandlerFile);

// call the target function
target[targetHandlerFunction](event.body, context, (error, response) => {
if (error) {
callback(null, {
StatusCode: 500,
FunctionError: 'Handled',
Payload: serializeError(error)
})
} else {
callback(null, {
StatusCode: 200,
Payload: JSON.stringify(response)
})
const [targetHandlerFile, targetHandlerFunction] = event.targetHandler.split('.');
const target = require(path.resolve(__dirname, '../..', event.location, targetHandlerFile));

const targetEvent = JSON.parse(Payload);
const targetContext = {
...context,
};

if (ClientContext) {
targetContext.clientContext = JSON.parse(Buffer.from(ClientContext, 'base64'));
}

const funcResult = new Promise((resolve, reject) => {
const result = target[targetHandlerFunction](targetEvent, targetContext, (error, response) => {
if (error) {
reject(error);
} else {
resolve(response);
}
});

if (result && typeof result.then === 'function' && typeof result.catch === 'function') {
result.then(resolve).catch(reject);
}
});

try {
return { StatusCode: 200, Payload: JSON.stringify(await funcResult) };
} catch (error) {
return { StatusCode: 500, FunctionError: 'Handled', Payload: serializeError(error) };
}
}

module.exports.handler = handler;