From ac83a0f3242489762f3df0ef02001fed64110b69 Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Thu, 5 Jul 2018 16:10:43 +0200 Subject: [PATCH 1/9] Fixes usage with serverless-webpack and serverless-offline hooks --- index.js | 23 +++++++++++++++-------- proxy.js | 17 +++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index d83ed22..d3c7fdc 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ 'use strict'; const packagePath = 'node_modules/serverless-offline-direct-lambda'; -const handlerPath = `proxy.js`; +const handlerPath = 'proxy.js'; class ServerlessPlugin { constructor(serverless, options) { @@ -9,31 +9,37 @@ class ServerlessPlugin { this.options = options; this.hooks = { - "before:offline:start:init": this.startHandler.bind(this), + 'before:offline:start': this.startHandler.bind(this), }; } startHandler() { + let location = ''; + try { + location = this.serverless.service.custom['serverless-offline'].location; + this.serverless.service.custom['serverless-offline'].location = ''; + } catch (_) { } + 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) => event === '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`, events: [ @@ -46,8 +52,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..6bb0fec 100644 --- a/proxy.js +++ b/proxy.js @@ -1,24 +1,25 @@ const serializeError = require('serialize-error'); +const path = require('path'); -function handler(event, context, callback) { +async 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); + const [targetHandlerFile, targetHandlerFunction] = event.targetHandler.split('.'); + const target = require(path.resolve(__dirname, '../..', event.location, targetHandlerFile)); // call the target function - target[targetHandlerFunction](event.body, context, (error, response) => { + return target[targetHandlerFunction](event.body, context, (error, response) => { if (error) { callback(null, { StatusCode: 500, FunctionError: 'Handled', - Payload: serializeError(error) - }) + Payload: serializeError(error), + }); } else { callback(null, { StatusCode: 200, - Payload: JSON.stringify(response) - }) + Payload: JSON.stringify(response), + }); } }); } From 38f5c952e1133a54dbbdd1277d6ca2b6c55d84c2 Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Fri, 6 Jul 2018 14:12:26 +0200 Subject: [PATCH 2/9] Further mimics a real Lambda invocation where body contains the invocation parameters --- proxy.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/proxy.js b/proxy.js index 6bb0fec..5bb7258 100644 --- a/proxy.js +++ b/proxy.js @@ -2,13 +2,21 @@ const serializeError = require('serialize-error'); const path = require('path'); async function handler(event, context, callback) { + const { ClientContext, FunctionName, InvocationType, LogType, Payload } = event.body; + // 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(path.resolve(__dirname, '../..', event.location, targetHandlerFile)); + const targetEvent = JSON.parse(Payload); + const targetContext = { + ...context, + clientContext: JSON.parse(Buffer.from(ClientContext, 'base64')), + }; + // call the target function - return target[targetHandlerFunction](event.body, context, (error, response) => { + return target[targetHandlerFunction](targetEvent, targetContext, (error, response) => { if (error) { callback(null, { StatusCode: 500, From b256cc01f059c2539cffb5f302e55de34a338e4a Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Fri, 6 Jul 2018 15:56:52 +0200 Subject: [PATCH 3/9] Forwards the proxied function's environment --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index d3c7fdc..1a2933e 100644 --- a/index.js +++ b/index.js @@ -42,6 +42,7 @@ const addProxies = (functionsObject, location) => { const functionProxy = (functionBeingProxied, location) => ({ name: `${functionBeingProxied.name}_proxy`, handler: `${packagePath}/proxy.handler`, + environment: functionBeingProxied.environment, events: [ { http: { From e62f8972c1a35c48cfdc64e70bfbfcdc73a421e0 Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Thu, 12 Jul 2018 12:58:09 +0200 Subject: [PATCH 4/9] Moves away from callback support internally in favor of promises --- proxy.js | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/proxy.js b/proxy.js index 5bb7258..0c7d204 100644 --- a/proxy.js +++ b/proxy.js @@ -1,7 +1,7 @@ const serializeError = require('serialize-error'); const path = require('path'); -async function handler(event, context, callback) { +export async function handler(event, context) { const { ClientContext, FunctionName, InvocationType, LogType, Payload } = event.body; // extract the path to the handler (relative to the project root) @@ -15,21 +15,23 @@ async function handler(event, context, callback) { clientContext: JSON.parse(Buffer.from(ClientContext, 'base64')), }; - // call the target function - return target[targetHandlerFunction](targetEvent, targetContext, (error, response) => { - if (error) { - callback(null, { - StatusCode: 500, - FunctionError: 'Handled', - Payload: serializeError(error), - }); - } else { - callback(null, { - StatusCode: 200, - Payload: JSON.stringify(response), - }); + 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); } }); -} -module.exports.handler = handler; + try { + return { StatusCode: 200, Payload: JSON.stringify(await funcResult) }; + } catch (error) { + return { StatusCode: 500, FunctionError: 'Handled', Payload: serializeError(error) }; + } +} From 8198790d7cf854d86dea3b7a002d4a182b04375e Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Thu, 12 Jul 2018 13:05:31 +0200 Subject: [PATCH 5/9] Reverts back to module.exports temporarily to comply with Node.js 8.10 --- proxy.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy.js b/proxy.js index 0c7d204..7b03ef6 100644 --- a/proxy.js +++ b/proxy.js @@ -1,7 +1,7 @@ const serializeError = require('serialize-error'); const path = require('path'); -export async function handler(event, context) { +async function handler(event, context) { const { ClientContext, FunctionName, InvocationType, LogType, Payload } = event.body; // extract the path to the handler (relative to the project root) @@ -35,3 +35,5 @@ export async function handler(event, context) { return { StatusCode: 500, FunctionError: 'Handled', Payload: serializeError(error) }; } } + +module.exports.handler = handler; From 55d02b031941c95641ef1d80e4da6e8695b8231d Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Wed, 15 Aug 2018 13:44:41 +0200 Subject: [PATCH 6/9] Assures ClientContext is set before writing clientContext to the proxied event --- proxy.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proxy.js b/proxy.js index 7b03ef6..e0f6dfb 100644 --- a/proxy.js +++ b/proxy.js @@ -12,9 +12,12 @@ async function handler(event, context) { const targetEvent = JSON.parse(Payload); const targetContext = { ...context, - clientContext: JSON.parse(Buffer.from(ClientContext, 'base64')), }; + 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) { From b083f17dff92a87159f109e84665e8e6ed19af2f Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Wed, 15 Aug 2018 14:19:31 +0200 Subject: [PATCH 7/9] Listens for `before:offline:start` and `before:offline:start:init` to trigger (once) for both `sls offline` and `sls offline start` --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 1a2933e..e07e63e 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,11 @@ class ServerlessPlugin { this.serverless = serverless; this.options = options; + const boundStartHandler = this.startHandler.bind(this); + this.hooks = { - 'before:offline:start': this.startHandler.bind(this), + 'before:offline:start': boundStartHandler, + 'before:offline:start:init': boundStartHandler, }; } From 6de8921de6927c7ec800f74335e6b7b04555d793 Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Wed, 15 Aug 2018 14:20:57 +0200 Subject: [PATCH 8/9] Fixes http filter logic for proxied functions --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index e07e63e..8327e0d 100644 --- a/index.js +++ b/index.js @@ -35,7 +35,8 @@ const addProxies = (functionsObject, location) => { // 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.some((event) => event === 'http')) { + if (!functionObject.events || + !functionObject.events.some((event) => Object.keys(event)[0] === 'http')) { const pf = functionProxy(functionObject, location); functionsObject[pf.name] = pf; } From cfc80b3a69a33095014fb7b0321ac58ec9f5f2a9 Mon Sep 17 00:00:00 2001 From: Martin Jesper Low Madsen Date: Sat, 13 Oct 2018 16:08:31 +0200 Subject: [PATCH 9/9] Update location to support `yarn link` --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index 8327e0d..391f9f9 100644 --- a/index.js +++ b/index.js @@ -17,12 +17,16 @@ class ServerlessPlugin { } 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, location);