From 16a6ea2b08b8ba66f80b58edd8c8815cf831e294 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 4 Apr 2022 19:23:26 +1000 Subject: [PATCH] fix(runtime-handler): fixes async handler functions thrownin g errors When a Twilio Function deemed an async function throws an error, the handler doesn't catch it and instead throws an unhandled promise rejection error. Also, when constructing the context and the checks for valid account sids and auth tokens in the getTwilioClient function, we say that it should print the error. However, passing the logger to the context serializes and deserialises it, since it is being passed to another process, and and it is no longer an instance of Logger. This causes another error, trying to call on the logger's error function that no longer exists. So, this PR does 2 things, it checks the handler function to see if it is an async function and calls it with await so that it can catch the error. And it sets shouldPrintMessage to true only if there is a logger object that has an error function. --- .../src/dev-runtime/internal/functionRunner.ts | 8 ++++++-- packages/runtime-handler/src/dev-runtime/route.ts | 13 +++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/runtime-handler/src/dev-runtime/internal/functionRunner.ts b/packages/runtime-handler/src/dev-runtime/internal/functionRunner.ts index 10a7d944..f8c20e01 100644 --- a/packages/runtime-handler/src/dev-runtime/internal/functionRunner.ts +++ b/packages/runtime-handler/src/dev-runtime/internal/functionRunner.ts @@ -63,7 +63,7 @@ const handleSuccess = (responseObject?: string | number | boolean | object) => { process.on( 'message', - ({ functionPath, event, config, path }: FunctionRunnerOptions) => { + async ({ functionPath, event, config, path }: FunctionRunnerOptions) => { try { setRoutes(config.routes); constructGlobalScope(config); @@ -102,7 +102,11 @@ process.on( `Could not find a "handler" function in file ${functionPath}` ); } - handler(context, event, callback); + if (handler.constructor.name === 'AsyncFunction') { + await handler(context, event, callback); + } else { + handler(context, event, callback); + } } catch (err) { if (process.send) { process.send({ err: serializeError(err) }); diff --git a/packages/runtime-handler/src/dev-runtime/route.ts b/packages/runtime-handler/src/dev-runtime/route.ts index 008329b8..d511cf74 100644 --- a/packages/runtime-handler/src/dev-runtime/route.ts +++ b/packages/runtime-handler/src/dev-runtime/route.ts @@ -26,7 +26,7 @@ import { import { Reply } from './internal/functionRunner'; import { Response } from './internal/response'; import * as Runtime from './internal/runtime'; -import { ServerConfig } from './types'; +import { LoggerInstance, ServerConfig } from './types'; import debug from './utils/debug'; import { wrapErrorInHtml } from './utils/error-html'; import { getCodeLocation } from './utils/getCodeLocation'; @@ -157,13 +157,13 @@ export function constructContext( }> { function getTwilioClient(opts?: TwilioClientOptions): TwilioClient { checkForValidAccountSid(env.ACCOUNT_SID, { - shouldPrintMessage: true, + shouldPrintMessage: logger ? !!logger.error : false, shouldThrowError: true, functionName: 'context.getTwilioClient()', logger: logger, }); checkForValidAuthToken(env.AUTH_TOKEN, { - shouldPrintMessage: true, + shouldPrintMessage: logger ? !!logger.error : false, shouldThrowError: true, functionName: 'context.getTwilioClient()', logger: logger, @@ -221,12 +221,13 @@ export function handleError( err: Error | string | object, req: ExpressRequest, res: ExpressResponse, - functionFilePath?: string + functionFilePath?: string, + logger?: LoggerInstance ) { res.status(500); if (isError(err)) { const cleanedupError = cleanUpStackTrace(err); - + logger?.error(cleanedupError.toString()); if (req.useragent && (req.useragent.isDesktop || req.useragent.isMobile)) { res.type('text/html'); res.send(wrapErrorInHtml(cleanedupError, functionFilePath)); @@ -315,7 +316,7 @@ export function functionPathToRoute( } if (err) { const error = deserializeError(err); - handleError(error, req, res, functionPath); + handleError(error, req, res, functionPath, config.logger); } if (reply) { res.status(reply.statusCode);