From 597416acf9f287258425bf021ff5e032bccb5d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 15:49:41 -0300 Subject: [PATCH 01/12] feat: error handler middleware --- packages/wallet-service/src/api/addresses.ts | 7 +++-- packages/wallet-service/src/api/auth.ts | 7 +++-- packages/wallet-service/src/api/balances.ts | 4 ++- .../wallet-service/src/api/fullnodeProxy.ts | 10 +++++-- .../wallet-service/src/api/healthcheck.ts | 3 +- .../src/api/middlewares/errorHandler.ts | 28 +++++++++++++++++++ packages/wallet-service/src/api/miners.ts | 4 ++- .../wallet-service/src/api/newAddresses.ts | 4 ++- .../wallet-service/src/api/pushRegister.ts | 4 ++- .../wallet-service/src/api/pushUnregister.ts | 4 ++- packages/wallet-service/src/api/pushUpdate.ts | 4 ++- packages/wallet-service/src/api/tokens.ts | 7 +++-- .../wallet-service/src/api/totalSupply.ts | 4 ++- packages/wallet-service/src/api/txById.ts | 4 ++- packages/wallet-service/src/api/txOutputs.ts | 7 +++-- .../src/api/txProposalCreate.ts | 4 ++- .../src/api/txProposalDestroy.ts | 4 ++- .../wallet-service/src/api/txProposalSend.ts | 4 ++- packages/wallet-service/src/api/txhistory.ts | 4 ++- packages/wallet-service/src/api/version.ts | 4 ++- packages/wallet-service/src/api/wallet.ts | 10 +++++-- packages/wallet-service/src/schemas.ts | 4 +-- 22 files changed, 105 insertions(+), 30 deletions(-) create mode 100644 packages/wallet-service/src/api/middlewares/errorHandler.ts diff --git a/packages/wallet-service/src/api/addresses.ts b/packages/wallet-service/src/api/addresses.ts index 2edc0de6..a9e31839 100644 --- a/packages/wallet-service/src/api/addresses.ts +++ b/packages/wallet-service/src/api/addresses.ts @@ -21,6 +21,7 @@ import { closeDbConnection, getDbConnection } from '@src/utils'; import { walletIdProxyHandler } from '@src/commons'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -104,7 +105,8 @@ export const checkMine: APIGatewayProxyHandler = middy(walletIdProxyHandler(asyn addresses: addressBelongMap, }), }; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); /* * Get the addresses of a wallet, allowing an index filter @@ -169,4 +171,5 @@ export const get: APIGatewayProxyHandler = middy( return response; }), ).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/auth.ts b/packages/wallet-service/src/api/auth.ts index cb9baeb0..f97bdfc7 100644 --- a/packages/wallet-service/src/api/auth.ts +++ b/packages/wallet-service/src/api/auth.ts @@ -31,6 +31,7 @@ import cors from '@middy/http-cors'; import createDefaultLogger from '@src/logger'; import { Logger } from 'winston'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const EXPIRATION_TIME_IN_SECONDS = 1800; @@ -153,7 +154,8 @@ export const tokenHandler: APIGatewayProxyHandler = middy(async (event) => { statusCode: 200, body: JSON.stringify({ success: true, token }), }; -}).use(cors()); +}).use(cors()) + .use(errorHandler()); /** * Generates a aws policy document to allow/deny access to the resource @@ -233,4 +235,5 @@ export const bearerAuthorizer: APIGatewayTokenAuthorizerHandler = middy(async (e } return _generatePolicy(walletId, 'Deny', event.methodArn, logger); -}).use(cors()); +}).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/balances.ts b/packages/wallet-service/src/api/balances.ts index 1be3f92f..bed929cf 100644 --- a/packages/wallet-service/src/api/balances.ts +++ b/packages/wallet-service/src/api/balances.ts @@ -22,6 +22,7 @@ import { import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi from 'joi'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -81,4 +82,5 @@ export const get: APIGatewayProxyHandler = middy(walletIdProxyHandler(async (wal body: JSON.stringify({ success: true, balances }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/fullnodeProxy.ts b/packages/wallet-service/src/api/fullnodeProxy.ts index 907b667f..6aebc7e5 100644 --- a/packages/wallet-service/src/api/fullnodeProxy.ts +++ b/packages/wallet-service/src/api/fullnodeProxy.ts @@ -26,6 +26,7 @@ import { GetTxByIdParams, ParamValidationResult, } from '@src/types'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -70,7 +71,8 @@ export const getTransactionById: APIGatewayProxyHandler = middy(walletIdProxyHan statusCode: 200, body: JSON.stringify(transaction), }; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); /* * Get confirmation data for a tx from the fullnode @@ -96,7 +98,8 @@ export const getConfirmationData: APIGatewayProxyHandler = middy(walletIdProxyHa statusCode: 200, body: JSON.stringify(confirmationData), }; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); /* * Makes graphviz queries on the fullnode @@ -134,4 +137,5 @@ export const queryGraphvizNeighbours: APIGatewayProxyHandler = middy( body: JSON.stringify(graphVizData), }; }), -).use(cors()); +).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/healthcheck.ts b/packages/wallet-service/src/api/healthcheck.ts index babad766..30f9b4ec 100644 --- a/packages/wallet-service/src/api/healthcheck.ts +++ b/packages/wallet-service/src/api/healthcheck.ts @@ -13,6 +13,7 @@ import { closeDbConnection, getDbConnection } from '@src/utils'; import { APIGatewayProxyHandler } from 'aws-lambda'; import { getRedisClient, ping } from '@src/redis'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -141,4 +142,4 @@ export const getHealthcheck: APIGatewayProxyHandler = middy(async (event) => { statusCode: response.getHttpStatusCode(), body: response.toJson(), }; -}); +}).use(errorHandler()); diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts new file mode 100644 index 00000000..0357dce8 --- /dev/null +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const errorHandler = () => { + const onError = async (request) => { + if (process.env.NODE_ENV === 'production') { + request.response = request.response ?? {} + request.response.statusCode = 500; + request.response.body = "Internal Server Error"; + return request.response; + } + + request.response = request.response ?? {} + request.response.statusCode = 500; + request.response.body = request.error?.message || String(request.error); + return request.response; + } + + return { + onError + } +} + +export default errorHandler; diff --git a/packages/wallet-service/src/api/miners.ts b/packages/wallet-service/src/api/miners.ts index c80d4499..7abefa1f 100644 --- a/packages/wallet-service/src/api/miners.ts +++ b/packages/wallet-service/src/api/miners.ts @@ -15,6 +15,7 @@ import { closeDbConnection, getDbConnection } from '@src/utils'; import { Miner } from '@src/types'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -37,4 +38,5 @@ export const onMinersListRequest: APIGatewayProxyHandler = middy(async () => { miners: minersList, }), }; -}).use(cors()); +}).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/newAddresses.ts b/packages/wallet-service/src/api/newAddresses.ts index 5514812f..3158ff9a 100644 --- a/packages/wallet-service/src/api/newAddresses.ts +++ b/packages/wallet-service/src/api/newAddresses.ts @@ -19,6 +19,7 @@ import { closeDbConnection, getDbConnection } from '@src/utils'; import { walletIdProxyHandler } from '@src/commons'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -47,4 +48,5 @@ export const get: APIGatewayProxyHandler = middy(walletIdProxyHandler(async (wal body: JSON.stringify({ success: true, addresses }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/pushRegister.ts b/packages/wallet-service/src/api/pushRegister.ts index 1e6bed77..db4b78df 100644 --- a/packages/wallet-service/src/api/pushRegister.ts +++ b/packages/wallet-service/src/api/pushRegister.ts @@ -15,6 +15,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi, { ValidationError } from 'joi'; import { PushRegister } from '@src/types'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -80,4 +81,5 @@ export const register: APIGatewayProxyHandler = middy(walletIdProxyHandler(async }; })) .use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/pushUnregister.ts b/packages/wallet-service/src/api/pushUnregister.ts index 267bbd87..4c7f3569 100644 --- a/packages/wallet-service/src/api/pushUnregister.ts +++ b/packages/wallet-service/src/api/pushUnregister.ts @@ -15,6 +15,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi, { ValidationError } from 'joi'; import { PushDelete } from '@src/types'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -55,4 +56,5 @@ export const unregister: APIGatewayProxyHandler = middy(walletIdProxyHandler(asy body: JSON.stringify({ success: true }), }; })) - .use(cors()); + .use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/pushUpdate.ts b/packages/wallet-service/src/api/pushUpdate.ts index d37d165c..18dc7e9f 100644 --- a/packages/wallet-service/src/api/pushUpdate.ts +++ b/packages/wallet-service/src/api/pushUpdate.ts @@ -15,6 +15,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi, { ValidationError } from 'joi'; import { PushUpdate } from '@src/types'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -75,4 +76,5 @@ export const update: APIGatewayProxyHandler = middy(walletIdProxyHandler(async ( body: JSON.stringify({ success: true }), }; })) - .use(cors()); + .use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/tokens.ts b/packages/wallet-service/src/api/tokens.ts index 3bd891aa..87d14367 100644 --- a/packages/wallet-service/src/api/tokens.ts +++ b/packages/wallet-service/src/api/tokens.ts @@ -18,6 +18,7 @@ import Joi from 'joi'; import { constants } from '@hathor/wallet-lib'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -37,7 +38,8 @@ export const get = middy(walletIdProxyHandler(async (walletId) => { }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); const getTokenDetailsParamsSchema = Joi.object({ token_id: txIdJoiValidator.required(), @@ -112,4 +114,5 @@ export const getTokenDetails = middy(walletIdProxyHandler(async (walletId, event }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/totalSupply.ts b/packages/wallet-service/src/api/totalSupply.ts index 5698bb2d..6088c2c1 100644 --- a/packages/wallet-service/src/api/totalSupply.ts +++ b/packages/wallet-service/src/api/totalSupply.ts @@ -18,6 +18,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import hathorLib from '@hathor/wallet-lib'; import Joi from 'joi'; +import errorHandler from '@src/api/middlewares/errorHandler'; const htrToken = hathorLib.constants.NATIVE_TOKEN_UID; const mysql = getDbConnection(); @@ -61,4 +62,5 @@ export const onTotalSupplyRequest: APIGatewayProxyHandler = middy(async (event) totalSupply, }), }; -}).use(cors()); +}).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/txById.ts b/packages/wallet-service/src/api/txById.ts index 7e6f25af..98592637 100644 --- a/packages/wallet-service/src/api/txById.ts +++ b/packages/wallet-service/src/api/txById.ts @@ -15,6 +15,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi, { ValidationError } from 'joi'; import { TxByIdRequest } from '@src/types'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -59,4 +60,5 @@ export const get: APIGatewayProxyHandler = middy(walletIdProxyHandler(async (wal }; })) .use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/txOutputs.ts b/packages/wallet-service/src/api/txOutputs.ts index 50af388e..55844981 100644 --- a/packages/wallet-service/src/api/txOutputs.ts +++ b/packages/wallet-service/src/api/txOutputs.ts @@ -19,6 +19,7 @@ import { getDbConnection } from '@src/utils'; import { constants } from '@hathor/wallet-lib'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -92,7 +93,8 @@ export const getFilteredUtxos = middy(walletIdProxyHandler(async (walletId, even } return response; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); /* * Filter tx_outputs @@ -131,7 +133,8 @@ export const getFilteredTxOutputs = middy(walletIdProxyHandler(async (walletId, } return _getFilteredTxOutputs(walletId, value); -})).use(cors()); +})).use(cors()) + .use(errorHandler()); const _getFilteredTxOutputs = async (walletId: string, filters: IFilterTxOutput) => { const walletAddresses = await getWalletAddresses(mysql, walletId); diff --git a/packages/wallet-service/src/api/txProposalCreate.ts b/packages/wallet-service/src/api/txProposalCreate.ts index 8b40abeb..b65c8dfe 100644 --- a/packages/wallet-service/src/api/txProposalCreate.ts +++ b/packages/wallet-service/src/api/txProposalCreate.ts @@ -31,6 +31,7 @@ import cors from '@middy/http-cors'; import { constants, Network, Transaction, helpersUtils } from '@hathor/wallet-lib'; import { getFullnodeData } from '@src/nodeConfig'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -143,7 +144,8 @@ export const create = middy(walletIdProxyHandler(async (walletId, event) => { inputs: retInputs, }), }; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); /** * Confirm that all inputs requested by the user have been fetched. diff --git a/packages/wallet-service/src/api/txProposalDestroy.ts b/packages/wallet-service/src/api/txProposalDestroy.ts index 6ef42edd..89975598 100644 --- a/packages/wallet-service/src/api/txProposalDestroy.ts +++ b/packages/wallet-service/src/api/txProposalDestroy.ts @@ -14,6 +14,7 @@ import { closeDbConnection, getDbConnection, getUnixTimestamp } from '@src/utils import { closeDbAndGetError } from '@src/api/utils'; import middy from '@middy/core'; import cors from '@middy/http-cors'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -67,4 +68,5 @@ export const destroy: APIGatewayProxyHandler = middy(walletIdProxyHandler(async txProposalId, }), }; -})).use(cors()); +})).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/txProposalSend.ts b/packages/wallet-service/src/api/txProposalSend.ts index 8925dd98..0ce4cf23 100644 --- a/packages/wallet-service/src/api/txProposalSend.ts +++ b/packages/wallet-service/src/api/txProposalSend.ts @@ -29,6 +29,7 @@ import { closeDbAndGetError } from '@src/api/utils'; import middy from '@middy/core'; import cors from '@middy/http-cors'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -145,4 +146,5 @@ export const send: APIGatewayProxyHandler = middy(walletIdProxyHandler(async (wa txHex, }); } -})).use(cors()); +})).use(cors()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/txhistory.ts b/packages/wallet-service/src/api/txhistory.ts index ddeebae5..4c9a0c15 100644 --- a/packages/wallet-service/src/api/txhistory.ts +++ b/packages/wallet-service/src/api/txhistory.ts @@ -20,6 +20,7 @@ import middy from '@middy/core'; import cors from '@middy/http-cors'; import Joi from 'joi'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const MAX_COUNT = config.txHistoryMaxCount; const htrToken = hathorLib.constants.NATIVE_TOKEN_UID; @@ -87,4 +88,5 @@ export const get = middy(walletIdProxyHandler(async (walletId, event) => { body: JSON.stringify({ success: true, history, skip, count }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/version.ts b/packages/wallet-service/src/api/version.ts index 342c5580..73eb66e0 100644 --- a/packages/wallet-service/src/api/version.ts +++ b/packages/wallet-service/src/api/version.ts @@ -14,6 +14,7 @@ import { } from '@src/utils'; import { warmupMiddleware } from '@src/api/utils'; import { getRawFullnodeData } from '@src/nodeConfig' +import errorHandler from '@src/api/middlewares/errorHandler'; import middy from '@middy/core'; import cors from '@middy/http-cors'; @@ -37,4 +38,5 @@ export const get: APIGatewayProxyHandler = middy(async () => { }), }; }).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); diff --git a/packages/wallet-service/src/api/wallet.ts b/packages/wallet-service/src/api/wallet.ts index 96cdc938..73fdeb88 100644 --- a/packages/wallet-service/src/api/wallet.ts +++ b/packages/wallet-service/src/api/wallet.ts @@ -41,6 +41,7 @@ import createDefaultLogger from '@src/logger'; import { Severity } from '@wallet-service/common/src/types'; import { addAlert } from '@wallet-service/common/src/utils/alerting.utils'; import config from '@src/config'; +import errorHandler from '@src/api/middlewares/errorHandler'; const mysql = getDbConnection(); @@ -64,7 +65,8 @@ export const get: APIGatewayProxyHandler = middy(walletIdProxyHandler(async (wal body: JSON.stringify({ success: true, status }), }; })).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); // If the env requires to validate the first address // then we must set the firstAddress field as required @@ -243,7 +245,8 @@ export const changeAuthXpub: APIGatewayProxyHandler = middy(async (event) => { status: updatedWallet, }), }; -}).use(cors()); +}).use(cors()) + .use(errorHandler()); /* * Load a wallet. First checks if the wallet doesn't exist already and then call another @@ -368,7 +371,8 @@ export const load: APIGatewayProxyHandler = middy(async (event) => { body: JSON.stringify({ success: true, status: wallet }), }; }).use(cors()) - .use(warmupMiddleware()); + .use(warmupMiddleware()) + .use(errorHandler()); interface LoadEvent { source?: string; diff --git a/packages/wallet-service/src/schemas.ts b/packages/wallet-service/src/schemas.ts index 042ba759..e5b80575 100644 --- a/packages/wallet-service/src/schemas.ts +++ b/packages/wallet-service/src/schemas.ts @@ -19,7 +19,7 @@ export const FullnodeVersionSchema = Joi.object({ reward_spend_min_blocks: Joi.number().integer().positive().required(), max_number_inputs: Joi.number().integer().positive().required(), max_number_outputs: Joi.number().integer().positive().required(), - decimal_places: Joi.number().integer().positive().required(), + decimal_places: Joi.number().integer().positive(), genesis_block_hash: Joi.string().min(1).required(), genesis_tx1_hash: Joi.string().hex().length(64).required(), genesis_tx2_hash: Joi.string().hex().length(64).required(), @@ -44,7 +44,7 @@ export const EnvironmentConfigSchema = Joi.object({ dbPass: Joi.string().required(), dbPort: Joi.number().required(), redisUrl: Joi.string().required(), - redisPassword: Joi.string(), + redisPassword: Joi.string().allow(''), authSecret: Joi.string().required(), walletServiceLambdaEndpoint: Joi.string().required(), pushNotificationEnabled: Joi.boolean().required(), From dcacb18d6690e94c71846f67d596835af68c06ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 15:55:39 -0300 Subject: [PATCH 02/12] feat: add error on logs --- .../src/api/middlewares/errorHandler.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index 0357dce8..e3d44a64 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -5,8 +5,14 @@ * LICENSE file in the root directory of this source tree. */ +import createDefaultLogger from "@src/logger" + +const logger = createDefaultLogger(); + const errorHandler = () => { const onError = async (request) => { + logger.error(request.error); + if (process.env.NODE_ENV === 'production') { request.response = request.response ?? {} request.response.statusCode = 500; @@ -19,10 +25,8 @@ const errorHandler = () => { request.response.body = request.error?.message || String(request.error); return request.response; } - - return { - onError - } + + return { onError }; } export default errorHandler; From d86890507bd2b00c0a86058711682b4ab2313bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 16:13:36 -0300 Subject: [PATCH 03/12] feat: improve error logs and typing --- .../src/api/middlewares/errorHandler.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index e3d44a64..50bc468d 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -5,23 +5,29 @@ * LICENSE file in the root directory of this source tree. */ +import middy from '@middy/core' +import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda' import createDefaultLogger from "@src/logger" const logger = createDefaultLogger(); -const errorHandler = () => { - const onError = async (request) => { - logger.error(request.error); +const defaultResponse: APIGatewayProxyResult = { statusCode: 500, body: "Internal Server Error" }; +const errorHandler = (): middy.MiddlewareObj => { + const onError: middy.MiddlewareFn = async (request) => { + logger.error(`[${request.context.functionName}] Unhandled error on ${request.event.path}: ${request.error}`); + + // Initialize response with default values if it hasn't been done yet. + request.response = request.response ?? {...defaultResponse}; + // Force the status code to 500 since this is an unhandled error + request.response.statusCode = 500; + + // In production, we do not want to expose the error message to the user. if (process.env.NODE_ENV === 'production') { - request.response = request.response ?? {} - request.response.statusCode = 500; request.response.body = "Internal Server Error"; return request.response; } - request.response = request.response ?? {} - request.response.statusCode = 500; request.response.body = request.error?.message || String(request.error); return request.response; } From 893290b7f71f3d2360b87d92d583cbfce1d72757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 16:30:16 -0300 Subject: [PATCH 04/12] feat: optional chaining --- packages/wallet-service/src/api/middlewares/errorHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index 50bc468d..3a58d377 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -15,7 +15,7 @@ const defaultResponse: APIGatewayProxyResult = { statusCode: 500, body: "Interna const errorHandler = (): middy.MiddlewareObj => { const onError: middy.MiddlewareFn = async (request) => { - logger.error(`[${request.context.functionName}] Unhandled error on ${request.event.path}: ${request.error}`); + logger.error(`[${request.context?.functionName}] Unhandled error on ${request.event?.path}: ${request.error}`); // Initialize response with default values if it hasn't been done yet. request.response = request.response ?? {...defaultResponse}; From 26178f74cbc33537e8ecd8772bd8a3e1b2ec10c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 21:05:52 -0300 Subject: [PATCH 05/12] tests: improve coverage --- .../tests/api.middlewares.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 packages/wallet-service/tests/api.middlewares.test.ts diff --git a/packages/wallet-service/tests/api.middlewares.test.ts b/packages/wallet-service/tests/api.middlewares.test.ts new file mode 100644 index 00000000..9cbb1c95 --- /dev/null +++ b/packages/wallet-service/tests/api.middlewares.test.ts @@ -0,0 +1,23 @@ +import middy from '@middy/core' +import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda' +import errorHandler from '@src/api/middlewares/errorHandler'; + +test('errorHandler should return an error response', () => { + expect.hasAssertions(); + const middleware = errorHandler(); + const request: middy.Request = { + event: { + path: '/test', + } as APIGatewayProxyEvent, + context: { + functionName: 'testFunction', + } as middy.Request['context'], + error: new Error('Boom'), + response: { statusCode: 200, body: 'ok' }, + internal: {}, + }; + + const response = middleware.onError(request); + expect(response.statusCode).toBe(500); + expect(response.body).toBe('Boom'); +}); From 6ab148f87ce8afd6e258d6f90a67a37f2d7e1b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 21:29:17 -0300 Subject: [PATCH 06/12] tests: await promise --- packages/wallet-service/tests/api.middlewares.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wallet-service/tests/api.middlewares.test.ts b/packages/wallet-service/tests/api.middlewares.test.ts index 9cbb1c95..66b37f06 100644 --- a/packages/wallet-service/tests/api.middlewares.test.ts +++ b/packages/wallet-service/tests/api.middlewares.test.ts @@ -2,7 +2,7 @@ import middy from '@middy/core' import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda' import errorHandler from '@src/api/middlewares/errorHandler'; -test('errorHandler should return an error response', () => { +test('errorHandler should return an error response', async () => { expect.hasAssertions(); const middleware = errorHandler(); const request: middy.Request = { @@ -17,7 +17,7 @@ test('errorHandler should return an error response', () => { internal: {}, }; - const response = middleware.onError(request); + const response = await middleware.onError(request); expect(response.statusCode).toBe(500); expect(response.body).toBe('Boom'); }); From b02f74e90bc7287cd0d33967a4299a41e51302b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 21:41:20 -0300 Subject: [PATCH 07/12] test: increase coverage --- packages/wallet-service/tests/commons.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/wallet-service/tests/commons.test.ts b/packages/wallet-service/tests/commons.test.ts index 9e551a59..5ecb8e8a 100644 --- a/packages/wallet-service/tests/commons.test.ts +++ b/packages/wallet-service/tests/commons.test.ts @@ -7,6 +7,7 @@ import { unlockTimelockedUtxos, searchForLatestValidBlock, getWalletBalancesForTx, + getTokenListFromInputsAndOutputs, } from '@src/commons'; import { Authorities, @@ -15,6 +16,7 @@ import { DbTxOutput, Block, FullNodeApiVersionResponse, + TxOutputWithIndex, } from '@src/types'; import fullnode from '@src/fullnode'; import { @@ -1169,3 +1171,20 @@ describe('getWalletBalancesForTx', () => { }); }); }); + +test('getTokenListFromInputsAndOutputs', () => { + expect.hasAssertions(); + + expect(getTokenListFromInputsAndOutputs([], [])).toStrictEqual([]); + + expect(getTokenListFromInputsAndOutputs( + [ + { token: 'A' } as TxInput, + { token: 'B' } as TxInput, + ], + [ + { token: 'A' } as TxOutputWithIndex, + { token: 'C' } as TxOutputWithIndex, + ], + )).toEqual(expect.arrayContaining(['A', 'B', 'C'])); +}); From 4958b4b5ea2ba11df67edc65a53d149cb21ad61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Thu, 3 Apr 2025 21:55:48 -0300 Subject: [PATCH 08/12] chore: remove untestable method from coverage --- packages/wallet-service/src/ws/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wallet-service/src/ws/utils.ts b/packages/wallet-service/src/ws/utils.ts index 1c28dc33..85ccd3f3 100644 --- a/packages/wallet-service/src/ws/utils.ts +++ b/packages/wallet-service/src/ws/utils.ts @@ -103,6 +103,7 @@ export const sendMessageToClient = async ( } }; +/* istanbul ignore next */ export const disconnectClient = async ( client: RedisClient, connInfo: WsConnectionInfo, From 524c088326e03afa421c06facbd4cd94d0f01150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Mon, 7 Apr 2025 12:29:48 -0300 Subject: [PATCH 09/12] chore: ignore dead code --- packages/wallet-service/src/commons.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wallet-service/src/commons.ts b/packages/wallet-service/src/commons.ts index 341de854..a469e149 100644 --- a/packages/wallet-service/src/commons.ts +++ b/packages/wallet-service/src/commons.ts @@ -463,6 +463,7 @@ export const validateAddressBalances = async (mysql: ServerlessMysql, addresses: * * @returns The new best block height */ +/* istanbul ignore next */ export const handleReorg = async (mysql: ServerlessMysql, logger: Logger): Promise => { const { height } = await searchForLatestValidBlock(mysql); const currentHeight = await getLatestHeight(mysql); From b26f13cc4d26269b46e1804d37064f905b638cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Mon, 7 Apr 2025 13:41:56 -0300 Subject: [PATCH 10/12] chore: use status code table instead of raw value --- packages/wallet-service/src/api/middlewares/errorHandler.ts | 6 ++++-- packages/wallet-service/src/ws/utils.ts | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index 3a58d377..1625b552 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -8,10 +8,12 @@ import middy from '@middy/core' import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda' import createDefaultLogger from "@src/logger" +import { STATUS_CODE_TABLE } from '../utils'; +import { ApiError } from '../errors'; const logger = createDefaultLogger(); -const defaultResponse: APIGatewayProxyResult = { statusCode: 500, body: "Internal Server Error" }; +const defaultResponse: APIGatewayProxyResult = { statusCode: STATUS_CODE_TABLE[ApiError.UNKNOWN_ERROR], body: "Internal Server Error" }; const errorHandler = (): middy.MiddlewareObj => { const onError: middy.MiddlewareFn = async (request) => { @@ -20,7 +22,7 @@ const errorHandler = (): middy.MiddlewareObj Date: Mon, 7 Apr 2025 13:43:42 -0300 Subject: [PATCH 11/12] chore: do not use relative import --- packages/wallet-service/src/api/middlewares/errorHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index 1625b552..82364999 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -8,8 +8,8 @@ import middy from '@middy/core' import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda' import createDefaultLogger from "@src/logger" -import { STATUS_CODE_TABLE } from '../utils'; -import { ApiError } from '../errors'; +import { STATUS_CODE_TABLE } from '@src/api/utils'; +import { ApiError } from '@src/api/errors'; const logger = createDefaultLogger(); From 0d77441474e45bc6d4253ffbf47b00a8e70608ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Mon, 7 Apr 2025 13:55:15 -0300 Subject: [PATCH 12/12] feat: internal server error code --- packages/wallet-service/src/api/errors.ts | 1 + packages/wallet-service/src/api/middlewares/errorHandler.ts | 6 +++--- packages/wallet-service/src/api/utils.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/wallet-service/src/api/errors.ts b/packages/wallet-service/src/api/errors.ts index d1d3dc72..3d8779a6 100644 --- a/packages/wallet-service/src/api/errors.ts +++ b/packages/wallet-service/src/api/errors.ts @@ -11,6 +11,7 @@ export enum ApiError { INVALID_TX_WEIGHT = 'invalid-tx-weight', INVALID_SELECTION_ALGORITHM = 'invalid-selection-algorithm', UNKNOWN_ERROR = 'unknown-error', + INTERNAL_SERVER_ERROR = 'internal-server-error', INPUTS_NOT_FOUND = 'inputs-not-found', INPUTS_ALREADY_USED = 'inputs-already-used', INPUTS_NOT_IN_WALLET = 'inputs-not-in-wallet', diff --git a/packages/wallet-service/src/api/middlewares/errorHandler.ts b/packages/wallet-service/src/api/middlewares/errorHandler.ts index 82364999..68c14341 100644 --- a/packages/wallet-service/src/api/middlewares/errorHandler.ts +++ b/packages/wallet-service/src/api/middlewares/errorHandler.ts @@ -13,7 +13,7 @@ import { ApiError } from '@src/api/errors'; const logger = createDefaultLogger(); -const defaultResponse: APIGatewayProxyResult = { statusCode: STATUS_CODE_TABLE[ApiError.UNKNOWN_ERROR], body: "Internal Server Error" }; +const defaultResponse: APIGatewayProxyResult = { statusCode: STATUS_CODE_TABLE[ApiError.INTERNAL_SERVER_ERROR], body: ApiError.INTERNAL_SERVER_ERROR }; const errorHandler = (): middy.MiddlewareObj => { const onError: middy.MiddlewareFn = async (request) => { @@ -22,11 +22,11 @@ const errorHandler = (): middy.MiddlewareObj