diff --git a/index.js b/index.js index d83ed22..391f9f9 100644 --- a/index.js +++ b/index.js @@ -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: { @@ -46,8 +61,9 @@ const functionProxy = functionBeingProxied => ({ template: { 'application/json': JSON.stringify( { + location, + body: "$input.json('$')", targetHandler : functionBeingProxied.handler, - body: "$input.json('$')" } ) } diff --git a/proxy.js b/proxy.js index 680ae31..e0f6dfb 100644 --- a/proxy.js +++ b/proxy.js @@ -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;