diff --git a/package-lock.json b/package-lock.json index 27f69b0..8cd4139 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1011,16 +1011,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" }, "bin": { "browserslist": "cli.js" @@ -1086,10 +1086,14 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001205", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz", - "integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==", - "dev": true + "version": "1.0.30001237", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz", + "integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/caseless": { "version": "0.12.0", @@ -1382,9 +1386,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.703", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.703.tgz", - "integrity": "sha512-SVBVhNB+4zPL+rvtWLw7PZQkw/Eqj1HQZs22xtcqW36+xoifzEOEEDEpkxSMfB6RFeSIOcG00w6z5mSqLr1Y6w==", + "version": "1.3.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", + "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", "dev": true }, "node_modules/emoji-regex": { @@ -5372,16 +5376,16 @@ "dev": true }, "browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" } }, "buffer-from": { @@ -5425,9 +5429,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001205", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz", - "integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==", + "version": "1.0.30001237", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz", + "integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==", "dev": true }, "caseless": { @@ -5659,9 +5663,9 @@ } }, "electron-to-chromium": { - "version": "1.3.703", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.703.tgz", - "integrity": "sha512-SVBVhNB+4zPL+rvtWLw7PZQkw/Eqj1HQZs22xtcqW36+xoifzEOEEDEpkxSMfB6RFeSIOcG00w6z5mSqLr1Y6w==", + "version": "1.3.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", + "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", "dev": true }, "emoji-regex": { diff --git a/src/index.ts b/src/index.ts index 336c1ca..50e6f27 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ import * as UserFunction from "./utils/UserFunction"; LogPatch.patchConsole(); -export function run(appRoot: string, handler: string): void { +export async function run(appRoot: string, handler: string): Promise { if (!process.env.AWS_LAMBDA_RUNTIME_API) { throw new Error("Missing Runtime API Server configuration."); } @@ -50,7 +50,10 @@ export function run(appRoot: string, handler: string): void { BeforeExitListener.reset(); process.on("beforeExit", BeforeExitListener.invoke); - const handlerFunc = UserFunction.load(appRoot, handler) as HandlerFunction; + const handlerFunc = (await UserFunction.load( + appRoot, + handler + )) as HandlerFunction; const runtime = new Runtime(client, handlerFunc, errorCallbacks); runtime.scheduleIteration(); diff --git a/src/utils/UserFunction.ts b/src/utils/UserFunction.ts index cf39e07..8a2e97c 100644 --- a/src/utils/UserFunction.ts +++ b/src/utils/UserFunction.ts @@ -110,6 +110,19 @@ function _loadUserApp( } } +async function _initializeFunction(userApp: any): Promise { + try { + await userApp.initializeFunction(); + } catch (e) { + if (e instanceof TypeError) { + // initializeFunction lifecycle hook not implemented + return; + } else { + throw e; + } + } +} + function _throwIfInvalidHandler(fullHandlerString: string): void { if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) { throw new MalformedHandlerName( @@ -137,10 +150,10 @@ function _throwIfInvalidHandler(fullHandlerString: string): void { * for traversing up the filesystem '..') * Errors for scenarios known by the runtime, will be wrapped by Runtime.* errors. */ -export const load = function ( +export const load = async function ( appRoot: string, fullHandlerString: string -): HandlerFunction { +): Promise { _throwIfInvalidHandler(fullHandlerString); const [moduleRoot, moduleAndHandler] = _moduleRootAndHandler( @@ -149,6 +162,9 @@ export const load = function ( const [module, handlerPath] = _splitHandlerString(moduleAndHandler); const userApp = _loadUserApp(appRoot, moduleRoot, module); + + await _initializeFunction(userApp); + const handlerFunc = _resolveHandler(userApp, handlerPath); if (!handlerFunc) { diff --git a/test/unit/utils/UserFunction.test.ts b/test/unit/utils/UserFunction.test.ts new file mode 100644 index 0000000..aedd9a0 --- /dev/null +++ b/test/unit/utils/UserFunction.test.ts @@ -0,0 +1,44 @@ +/** Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ + +"use strict"; + +require("should"); +import { load } from "../../../src/utils/UserFunction"; + +describe("Invoking the load function", async() => { + it("should resolve promise when init hook function is present", async() => { + const handler = "InitPresent.handler" + const appRoot = "test/unit/utils/function"; + + const handlerFunc = (await load( + appRoot, + handler + )) as Function; + + handlerFunc.should.be.Function; + handlerFunc().should.be.true; + }); + it("should not fail when init hook function is absent", async() => { + const handler = "InitAbsent.handler" + const appRoot = "test/unit/utils/function"; + + const handlerFunc = (await load( + appRoot, + handler + )) as Function; + + handlerFunc.should.be.Function; + }); + it("should catch TypeError exception", async() => { + const handler = "InitThrowsTypeError.handler" + const appRoot = "test/unit/utils/function"; + + const handlerFunc = (await load( + appRoot, + handler + )) as Function; + + handlerFunc.should.be.Function; + handlerFunc().should.be.true; + }); +}); \ No newline at end of file diff --git a/test/unit/utils/function/InitAbsent.js b/test/unit/utils/function/InitAbsent.js new file mode 100644 index 0000000..d585cb5 --- /dev/null +++ b/test/unit/utils/function/InitAbsent.js @@ -0,0 +1,24 @@ +// console.log("******** enter the init block ********"); + +let resolved = false; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)).then(() => {resolved = true}); +} + +async function init() { + // console.log("******** enter initializeFunction hook ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); + // console.log("******** sleep for 20 ms... ********") + let p = await sleep(20); + // console.log("******** wake up ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); +} + +init(); + +exports.handler = async (event, context) => { + // console.log("******** enter the handler ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); + return ( resolved ? true: false ); +} diff --git a/test/unit/utils/function/InitPresent.js b/test/unit/utils/function/InitPresent.js new file mode 100644 index 0000000..6336607 --- /dev/null +++ b/test/unit/utils/function/InitPresent.js @@ -0,0 +1,22 @@ +// console.log("******** enter the init block ********"); + +let resolved = false; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)).then(() => {resolved = true}); +} + +exports.initializeFunction = async () => { + // console.log("******** enter initializeFunction hook ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); + // console.log("******** sleep for 20 ms... ********") + let p = await sleep(20); + // console.log("******** wake up ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); +} + +exports.handler = async (event, context) => { + // console.log("******** enter the handler ********"); + // console.log("******** Is promised resolved? " + resolved + " ********"); + return ( resolved ? true: false ); +} diff --git a/test/unit/utils/function/InitThrowsTypeError.js b/test/unit/utils/function/InitThrowsTypeError.js new file mode 100644 index 0000000..0ecd925 --- /dev/null +++ b/test/unit/utils/function/InitThrowsTypeError.js @@ -0,0 +1,7 @@ +exports.initializeFunction = async () => { + throw new TypeError; +} + +exports.handler = async (event, context) => { + return true; +}