From 3f85c28122604ee4c9863bd7f228c7ed0ccadb75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Mon, 18 Sep 2023 11:05:19 -0300 Subject: [PATCH] Release version v0.22.0 (#334) * chore: bump wallet-lib v1.0.1 (#335) * fix: patch the express router to handle async errors (#339) * chore: bump version v0.22.0 (#341) --- package-lock.json | 8 +-- package.json | 4 +- src/api-docs.js | 2 +- src/patch.js | 63 +++++++++++++++++++ src/routes/index.routes.js | 3 +- .../wallet/atomic-swap/atomic-swap.routes.js | 3 +- .../wallet/atomic-swap/tx-proposal.routes.js | 3 +- src/routes/wallet/p2sh/p2sh.routes.js | 3 +- src/routes/wallet/p2sh/tx-proposal.routes.js | 3 +- .../wallet/tx-proposal/tx-proposal.routes.js | 3 +- src/routes/wallet/wallet.routes.js | 3 +- 11 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 src/patch.js diff --git a/package-lock.json b/package-lock.json index aefbba4d..9407aba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hathor-wallet-headless", - "version": "0.22.0-rc5", + "version": "0.22.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1197,9 +1197,9 @@ } }, "@hathor/wallet-lib": { - "version": "1.0.0-rc8", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.0.0-rc8.tgz", - "integrity": "sha512-Ti65+yYkSf5TMNHjS+iX4UQUfq2wPCGBufR5BRfq+PU7wAcxftEgSlZat/Qdcent9/fEE59V0bLg7uH1JeYvEQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.0.1.tgz", + "integrity": "sha512-Pfjq0oks3qxD2BJXNYv7X6jv0oIW0vdSBvpkLMVsKQ/VikXV2RHP+av2vQdnkC2lMdkvBeG/gtoREF21tQzing==", "requires": { "axios": "^0.21.4", "bitcore-lib": "^8.25.10", diff --git a/package.json b/package.json index 2245fb54..72875633 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hathor-wallet-headless", - "version": "0.22.0-rc5", + "version": "0.22.0", "description": "Hathor Wallet Headless, i.e., without graphical user interface", "main": "index.js", "engines": { @@ -12,7 +12,7 @@ "@babel/node": "^7.13.0", "@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/preset-env": "^7.13.5", - "@hathor/wallet-lib": "^1.0.0-rc8", + "@hathor/wallet-lib": "^1.0.1", "express": "^4.17.1", "express-validator": "^6.10.0", "lodash": "^4.17.11", diff --git a/src/api-docs.js b/src/api-docs.js index a182e89a..2618d9da 100644 --- a/src/api-docs.js +++ b/src/api-docs.js @@ -6,7 +6,7 @@ const apiDoc = { info: { title: 'Headless Hathor Wallet API', description: 'This wallet is fully controlled through an HTTP API.', - version: '0.22.0-rc5', + version: '0.22.0', }, produces: ['application/json'], components: { diff --git a/src/patch.js b/src/patch.js new file mode 100644 index 00000000..5325fa8f --- /dev/null +++ b/src/patch.js @@ -0,0 +1,63 @@ +/** + * Patch an express router to catch any errors and invoke the error handlers. + * + * The patch will wrap some router methods (e.g. get, post) and the wrapped method + * will just call the original method but wrapping only the handlers. + * + * The wrapped handlers will not interfere with the handler funcionality, it will + * just check that the return is a promise and catch any errors. + */ +export function patchExpressRouter(router) { + /** + * This method is a wrapper for handlers that uses the logic: + * - Check that the original handler is a function, if not, just return it without wrapping it + * - Return a handler that calls the original handler and handles async errors + */ + function handlerWrapper(originalHandler) { + // Here we check that the original handler is a function + // We don't need to be strict (i.e. check that the type is Function or AsyncFunction) + // just check that the call method is available to be used is enough. + if (!originalHandler.call) { + return originalHandler; + } + + /** + * This is the returned handler, it is a sync function that calls the original handler + * and if it returns a promise, catch the error and send it to `next` so the error + * handlers and middlewares can properly handle the errors + */ + function wrappedHandler(req, res, next) { + const ret = originalHandler.call(this, req, res, next); + // Here we check that the handler returned a promise, since we use babel + // it can be challenging since it transpile promises to thenable objects + // but checking for the existence of `catch` is equivalent to checking for a thenable + if (ret && ret.catch) { + ret.catch(err => next(err)); + } + return ret; + } + return wrappedHandler; + } + + // These are the methods being patched on the given router + const methods = ['get', 'post', 'put', 'delete']; + for (const method of methods) { + const original = router[method]; + + // We create a wrapper object so the method being called can have a context (i.e. `this`) + // and the wrapped method will not be an anonymous function + // (which can cause some issues with express core methods) + const wrapper = { + [method](path, ...callbacks) { + // Call the original handler but passing the current context, path and wrap all + // handlers after that. + return original.call(this, path, ...callbacks.map(handlerWrapper)); + }, + }; + // Here we router method to our wrapped method, but eslint does not like this type of assignment + // The error given is `no-param-reassign` + // eslint-disable-next-line + router[method] = wrapper[method]; + } + return router; +} diff --git a/src/routes/index.routes.js b/src/routes/index.routes.js index 070dd0e9..af578021 100644 --- a/src/routes/index.routes.js +++ b/src/routes/index.routes.js @@ -10,8 +10,9 @@ const { query, checkSchema } = require('express-validator'); const rootControllers = require('../controllers/index.controller'); const { ReadonlyErrorHandler } = require('../middlewares/xpub-error-handler.middleware'); const { txHexSchema } = require('../schemas'); +const { patchExpressRouter } = require('../patch'); -const mainRouter = Router({ mergeParams: true }); +const mainRouter = patchExpressRouter(Router({ mergeParams: true })); const walletRouter = require('./wallet/wallet.routes'); mainRouter.get('/', rootControllers.welcome); diff --git a/src/routes/wallet/atomic-swap/atomic-swap.routes.js b/src/routes/wallet/atomic-swap/atomic-swap.routes.js index 9c738b90..5b328192 100644 --- a/src/routes/wallet/atomic-swap/atomic-swap.routes.js +++ b/src/routes/wallet/atomic-swap/atomic-swap.routes.js @@ -7,8 +7,9 @@ const { Router } = require('express'); const txProposalRouter = require('./tx-proposal.routes'); +const { patchExpressRouter } = require('../../../patch'); -const atomicSwapRouter = Router({ mergeParams: true }); +const atomicSwapRouter = patchExpressRouter(Router({ mergeParams: true })); atomicSwapRouter.use('/tx-proposal', txProposalRouter); module.exports = atomicSwapRouter; diff --git a/src/routes/wallet/atomic-swap/tx-proposal.routes.js b/src/routes/wallet/atomic-swap/tx-proposal.routes.js index 414af31a..2dbad1b5 100644 --- a/src/routes/wallet/atomic-swap/tx-proposal.routes.js +++ b/src/routes/wallet/atomic-swap/tx-proposal.routes.js @@ -28,8 +28,9 @@ const { fetchFromService, registerProposal, } = require('../../../controllers/wallet/atomic-swap/tx-proposal.controller'); +const { patchExpressRouter } = require('../../../patch'); -const txProposalRouter = Router({ mergeParams: true }); +const txProposalRouter = patchExpressRouter(Router({ mergeParams: true })); txProposalRouter.post( '/', diff --git a/src/routes/wallet/p2sh/p2sh.routes.js b/src/routes/wallet/p2sh/p2sh.routes.js index dc55dda1..f2492a84 100644 --- a/src/routes/wallet/p2sh/p2sh.routes.js +++ b/src/routes/wallet/p2sh/p2sh.routes.js @@ -7,8 +7,9 @@ const { Router } = require('express'); const txProposalRouter = require('./tx-proposal.routes'); +const { patchExpressRouter } = require('../../../patch'); -const p2shRouter = Router({ mergeParams: true }); +const p2shRouter = patchExpressRouter(Router({ mergeParams: true })); p2shRouter.use('/tx-proposal', txProposalRouter); module.exports = p2shRouter; diff --git a/src/routes/wallet/p2sh/tx-proposal.routes.js b/src/routes/wallet/p2sh/tx-proposal.routes.js index cff2b845..025015dd 100644 --- a/src/routes/wallet/p2sh/tx-proposal.routes.js +++ b/src/routes/wallet/p2sh/tx-proposal.routes.js @@ -16,8 +16,9 @@ const { signTx, signAndPush, } = require('../../../controllers/wallet/p2sh/tx-proposal.controller'); +const { patchExpressRouter } = require('../../../patch'); -const txProposalRouter = Router({ mergeParams: true }); +const txProposalRouter = patchExpressRouter(Router({ mergeParams: true })); txProposalRouter.post( '/', diff --git a/src/routes/wallet/tx-proposal/tx-proposal.routes.js b/src/routes/wallet/tx-proposal/tx-proposal.routes.js index 0417417e..ac2a04af 100644 --- a/src/routes/wallet/tx-proposal/tx-proposal.routes.js +++ b/src/routes/wallet/tx-proposal/tx-proposal.routes.js @@ -14,8 +14,9 @@ const { getInputData, } = require('../../../controllers/wallet/tx-proposal/tx-proposal.controller'); const { txBuildSchema, queryInputSchema, txInputSchema, txHexInputDataSchema, p2pkhSignature, p2shSignature } = require('../../../schemas'); +const { patchExpressRouter } = require('../../../patch'); -const txProposalRouter = Router({ mergeParams: true }); +const txProposalRouter = patchExpressRouter(Router({ mergeParams: true })); txProposalRouter.post( '/', diff --git a/src/routes/wallet/wallet.routes.js b/src/routes/wallet/wallet.routes.js index 84ff0d17..ee14a3bf 100644 --- a/src/routes/wallet/wallet.routes.js +++ b/src/routes/wallet/wallet.routes.js @@ -19,8 +19,9 @@ const p2shRouter = require('./p2sh/p2sh.routes'); const atomicSwapRouter = require('./atomic-swap/atomic-swap.routes'); const txProposalRouter = require('./tx-proposal/tx-proposal.routes'); const { MAX_DATA_SCRIPT_LENGTH } = require('../../constants'); +const { patchExpressRouter } = require('../../patch'); -const walletRouter = Router({ mergeParams: true }); +const walletRouter = patchExpressRouter(Router({ mergeParams: true })); walletRouter.use(walletMiddleware); walletRouter.use('/atomic-swap', atomicSwapRouter); walletRouter.use('/p2sh', p2shRouter);