mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	refactor(utils): add safeExtractMessageAndStackFromError util to remove code duplication
This commit is contained in:
		| @@ -9,6 +9,7 @@ import log from "../../services/log.js"; | |||||||
| import NotFoundError from "../../errors/not_found_error.js"; | import NotFoundError from "../../errors/not_found_error.js"; | ||||||
| import type { Request, Response } from "express"; | import type { Request, Response } from "express"; | ||||||
| import ValidationError from "../../errors/validation_error.js"; | import ValidationError from "../../errors/validation_error.js"; | ||||||
|  | import { safeExtractMessageAndStackFromError } from "../../services/utils.js"; | ||||||
|  |  | ||||||
| function exportBranch(req: Request, res: Response) { | function exportBranch(req: Request, res: Response) { | ||||||
|     const { branchId, type, format, version, taskId } = req.params; |     const { branchId, type, format, version, taskId } = req.params; | ||||||
| @@ -38,10 +39,11 @@ function exportBranch(req: Request, res: Response) { | |||||||
|             throw new NotFoundError(`Unrecognized export format '${format}'`); |             throw new NotFoundError(`Unrecognized export format '${format}'`); | ||||||
|         } |         } | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|         const message = `Export failed with following error: '${(e instanceof Error) ? e.message : e}'. More details might be in the logs.`; |         const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|  |         const message = `Export failed with following error: '${errMessage}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
|  |  | ||||||
|         log.error((e instanceof Error) ? message + e.stack : message); |         log.error(errMessage + errStack); | ||||||
|  |  | ||||||
|         res.setHeader("Content-Type", "text/plain").status(500).send(message); |         res.setHeader("Content-Type", "text/plain").status(500).send(message); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import TaskContext from "../../services/task_context.js"; | |||||||
| import ValidationError from "../../errors/validation_error.js"; | import ValidationError from "../../errors/validation_error.js"; | ||||||
| import type { Request } from "express"; | import type { Request } from "express"; | ||||||
| import type BNote from "../../becca/entities/bnote.js"; | import type BNote from "../../becca/entities/bnote.js"; | ||||||
|  | import { safeExtractMessageAndStackFromError } from "../../services/utils.js"; | ||||||
|  |  | ||||||
| async function importNotesToBranch(req: Request) { | async function importNotesToBranch(req: Request) { | ||||||
|     const { parentNoteId } = req.params; |     const { parentNoteId } = req.params; | ||||||
| @@ -69,7 +70,7 @@ async function importNotesToBranch(req: Request) { | |||||||
|             note = await singleImportService.importSingleFile(taskContext, file, parentNote); |             note = await singleImportService.importSingleFile(taskContext, file, parentNote); | ||||||
|         } |         } | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|         const [errMessage, errStack] = e instanceof Error ? [e.message, e.stack] : ["Unknown Error", undefined]; |         const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|         const message = `Import failed with following error: '${errMessage}'. More details might be in the logs.`; |         const message = `Import failed with following error: '${errMessage}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
|  |  | ||||||
| @@ -122,7 +123,7 @@ async function importAttachmentsToNote(req: Request) { | |||||||
|     try { |     try { | ||||||
|         await singleImportService.importAttachment(taskContext, file, parentNote); |         await singleImportService.importAttachment(taskContext, file, parentNote); | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|         const [errMessage, errStack] = e instanceof Error ? [e.message, e.stack] : ["Unknown Error", undefined]; |         const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|  |  | ||||||
|         const message = `Import failed with following error: '${errMessage}'. More details might be in the logs.`; |         const message = `Import failed with following error: '${errMessage}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import becca from "../../becca/becca.js"; | |||||||
| import syncService from "../../services/sync.js"; | import syncService from "../../services/sync.js"; | ||||||
| import sql from "../../services/sql.js"; | import sql from "../../services/sql.js"; | ||||||
| import type { Request } from "express"; | import type { Request } from "express"; | ||||||
|  | import { safeExtractMessageAndStackFromError } from "../../services/utils.js"; | ||||||
|  |  | ||||||
| interface ScriptBody { | interface ScriptBody { | ||||||
|     script: string; |     script: string; | ||||||
| @@ -34,9 +35,10 @@ async function exec(req: Request) { | |||||||
|             maxEntityChangeId: syncService.getMaxEntityChangeId() |             maxEntityChangeId: syncService.getMaxEntityChangeId() | ||||||
|         }; |         }; | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|  |         const [errMessage] = safeExtractMessageAndStackFromError(e); | ||||||
|         return { |         return { | ||||||
|             success: false, |             success: false, | ||||||
|             error: (e instanceof Error) ? e.message : "Unknown Error" |             error: errMessage | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import sql from "../../services/sql.js"; | |||||||
| import becca from "../../becca/becca.js"; | import becca from "../../becca/becca.js"; | ||||||
| import type { Request } from "express"; | import type { Request } from "express"; | ||||||
| import ValidationError from "../../errors/validation_error.js"; | import ValidationError from "../../errors/validation_error.js"; | ||||||
|  | import { safeExtractMessageAndStackFromError } from "../../services/utils.js"; | ||||||
|  |  | ||||||
| function getSchema() { | function getSchema() { | ||||||
|     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); |     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); | ||||||
| @@ -57,9 +58,10 @@ function execute(req: Request) { | |||||||
|             results |             results | ||||||
|         }; |         }; | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|  |         const [errMessage] = safeExtractMessageAndStackFromError(e); | ||||||
|         return { |         return { | ||||||
|             success: false, |             success: false, | ||||||
|             error: (e instanceof Error) ? e.message : "Unknown Error" |             error: errMessage | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import optionService from "../../services/options.js"; | |||||||
| import contentHashService from "../../services/content_hash.js"; | import contentHashService from "../../services/content_hash.js"; | ||||||
| import log from "../../services/log.js"; | import log from "../../services/log.js"; | ||||||
| import syncOptions from "../../services/sync_options.js"; | import syncOptions from "../../services/sync_options.js"; | ||||||
| import utils from "../../services/utils.js"; | import utils, { safeExtractMessageAndStackFromError } from "../../services/utils.js"; | ||||||
| import ws from "../../services/ws.js"; | import ws from "../../services/ws.js"; | ||||||
| import type { Request } from "express"; | import type { Request } from "express"; | ||||||
| import type { EntityChange } from "../../services/entity_changes_interface.js"; | import type { EntityChange } from "../../services/entity_changes_interface.js"; | ||||||
| @@ -31,9 +31,10 @@ async function testSync() { | |||||||
|  |  | ||||||
|         return { success: true, message: t("test_sync.successful") }; |         return { success: true, message: t("test_sync.successful") }; | ||||||
|     } catch (e: unknown) { |     } catch (e: unknown) { | ||||||
|  |         const [errMessage] = safeExtractMessageAndStackFromError(e); | ||||||
|         return { |         return { | ||||||
|             success: false, |             success: false, | ||||||
|             error: (e instanceof Error) ? e.message : "Unknown Error" |             error: errMessage | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import cls from "../services/cls.js"; | |||||||
| import sql from "../services/sql.js"; | import sql from "../services/sql.js"; | ||||||
| import becca from "../becca/becca.js"; | import becca from "../becca/becca.js"; | ||||||
| import type { Request, Response, Router } from "express"; | import type { Request, Response, Router } from "express"; | ||||||
|  | import { safeExtractMessageAndStackFromError } from "../services/utils.js"; | ||||||
|  |  | ||||||
| function handleRequest(req: Request, res: Response) { | function handleRequest(req: Request, res: Response) { | ||||||
|     // express puts content after first slash into 0 index element |     // express puts content after first slash into 0 index element | ||||||
| @@ -26,7 +27,7 @@ function handleRequest(req: Request, res: Response) { | |||||||
|         try { |         try { | ||||||
|             match = path.match(regex); |             match = path.match(regex); | ||||||
|         } catch (e: unknown) { |         } catch (e: unknown) { | ||||||
|             const [errMessage, errStack] = e instanceof Error ? [e.message, e.stack] : ["Unknown Error", undefined]; |             const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|             log.error(`Testing path for label '${attr.attributeId}', regex '${attr.value}' failed with error: ${errMessage}, stack: ${errStack}`); |             log.error(`Testing path for label '${attr.attributeId}', regex '${attr.value}' failed with error: ${errMessage}, stack: ${errStack}`); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| @@ -47,10 +48,8 @@ function handleRequest(req: Request, res: Response) { | |||||||
|                     res |                     res | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: unknown) { |             } catch (e: unknown) { | ||||||
|                 const [errMessage, errStack] = e instanceof Error ? [e.message, e.stack] : ["Unknown Error", undefined]; |                 const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|  |  | ||||||
|                 log.error(`Custom handler '${note.noteId}' failed with: ${errMessage}, ${errStack}`); |                 log.error(`Custom handler '${note.noteId}' failed with: ${errMessage}, ${errStack}`); | ||||||
|  |  | ||||||
|                 res.setHeader("Content-Type", "text/plain").status(500).send(errMessage); |                 res.setHeader("Content-Type", "text/plain").status(500).send(errMessage); | ||||||
|             } |             } | ||||||
|         } else if (attr.name === "customResourceProvider") { |         } else if (attr.name === "customResourceProvider") { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| import { isElectron } from "../services/utils.js"; | import { isElectron, safeExtractMessageAndStackFromError } from "../services/utils.js"; | ||||||
| import multer from "multer"; | import multer from "multer"; | ||||||
| import log from "../services/log.js"; | import log from "../services/log.js"; | ||||||
| import express from "express"; | import express from "express"; | ||||||
| @@ -494,10 +494,7 @@ function handleResponse(resultHandler: ApiResultHandler, req: express.Request, r | |||||||
| } | } | ||||||
|  |  | ||||||
| function handleException(e: unknown | Error, method: HttpMethod, path: string, res: express.Response) { | function handleException(e: unknown | Error, method: HttpMethod, path: string, res: express.Response) { | ||||||
|  |     const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); | ||||||
|     const [errMessage, errStack]: [message: string, stack: string] = (e instanceof Error) |  | ||||||
|         ? [e.message, e.stack || "No Stack Trace"] |  | ||||||
|         : ["Unknown Error", "No Stack Trace"]; |  | ||||||
|  |  | ||||||
|     log.error(`${method} ${path} threw exception: '${errMessage}', stack: ${errStack}`); |     log.error(`${method} ${path} threw exception: '${errMessage}', stack: ${errStack}`); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -500,6 +500,23 @@ describe("#isDev", () => { | |||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | describe("#safeExtractMessageAndStackFromError", () => { | ||||||
|  |     it("should correctly extract the message and stack property if it gets passed an instance of an Error", () => { | ||||||
|  |         const testMessage = "Test Message"; | ||||||
|  |         const testError = new Error(testMessage); | ||||||
|  |         const actual = utils.safeExtractMessageAndStackFromError(testError); | ||||||
|  |         expect(actual[0]).toBe(testMessage); | ||||||
|  |         expect(actual[1]).not.toBeUndefined(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it("should use the fallback 'Unknown Error' message, if it gets passed anything else than an instance of an Error", () => { | ||||||
|  |         const testNonError = "this is not an instance of an Error, but JS technically allows us to throw this anyways"; | ||||||
|  |         const actual = utils.safeExtractMessageAndStackFromError(testNonError); | ||||||
|  |         expect(actual[0]).toBe("Unknown Error"); | ||||||
|  |         expect(actual[1]).toBeUndefined(); | ||||||
|  |     }); | ||||||
|  | }) | ||||||
|  |  | ||||||
| describe("#formatDownloadTitle", () => { | describe("#formatDownloadTitle", () => { | ||||||
|     //prettier-ignore |     //prettier-ignore | ||||||
|     const testCases: [fnValue: Parameters<typeof utils.formatDownloadTitle>, expectedValue: ReturnType<typeof utils.formatDownloadTitle>][] = [ |     const testCases: [fnValue: Parameters<typeof utils.formatDownloadTitle>, expectedValue: ReturnType<typeof utils.formatDownloadTitle>][] = [ | ||||||
|   | |||||||
| @@ -363,6 +363,11 @@ export function processStringOrBuffer(data: string | Buffer | null) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function safeExtractMessageAndStackFromError(err: unknown) { | ||||||
|  |     return (err instanceof Error) ? [err.message, err.stack] as const : ["Unknown Error", undefined] as const; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     compareVersions, |     compareVersions, | ||||||
|     crash, |     crash, | ||||||
| @@ -393,6 +398,7 @@ export default { | |||||||
|     removeDiacritic, |     removeDiacritic, | ||||||
|     removeTextFileExtension, |     removeTextFileExtension, | ||||||
|     replaceAll, |     replaceAll, | ||||||
|  |     safeExtractMessageAndStackFromError, | ||||||
|     sanitizeSqlIdentifier, |     sanitizeSqlIdentifier, | ||||||
|     stripTags, |     stripTags, | ||||||
|     timeLimit, |     timeLimit, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user