diff --git a/services/web/app/src/Features/Errors/ErrorController.mjs b/services/web/app/src/Features/Errors/ErrorController.mjs index bc95b90128..b70774b2a6 100644 --- a/services/web/app/src/Features/Errors/ErrorController.mjs +++ b/services/web/app/src/Features/Errors/ErrorController.mjs @@ -1,10 +1,11 @@ import { isZodErrorLike, fromZodError } from 'zod-validation-error' -import Errors from './Errors.js' +import Errors, { NotFoundError } from './Errors.js' import SessionManager from '../Authentication/SessionManager.mjs' import SamlLogHandler from '../SamlLog/SamlLogHandler.mjs' import HttpErrorHandler from './HttpErrorHandler.mjs' import { plainTextResponse } from '../../infrastructure/Response.mjs' import { expressifyErrorHandler } from '@overleaf/promise-utils' +import { ParamsError } from '@overleaf/validation-tools' function notFound(req, res) { res.status(404) @@ -41,6 +42,14 @@ async function handleError(error, req, res, next) { if (shouldSendErrorResponse) { notFound(req, res) } + } else if (error instanceof ParamsError) { + req.logger.setLevel('warn') + if (shouldSendErrorResponse) { + notFound(req, res) + } else { + // convert into a NotFoundError that the default handler understands + return next(new NotFoundError('Not found').withCause(error)) + } } else if ( error instanceof URIError && error.message.match(/^Failed to decode param/) @@ -117,7 +126,7 @@ async function handleError(error, req, res, next) { function handleApiError(err, req, res, next) { req.logger.addFields({ err }) - if (err instanceof Errors.NotFoundError) { + if (err instanceof Errors.NotFoundError || err instanceof ParamsError) { req.logger.setLevel('warn') res.sendStatus(404) } else if ( diff --git a/services/web/app/src/infrastructure/Validation.mjs b/services/web/app/src/infrastructure/Validation.mjs index 4f545b4d7c..41349520bb 100644 --- a/services/web/app/src/infrastructure/Validation.mjs +++ b/services/web/app/src/infrastructure/Validation.mjs @@ -1,31 +1,8 @@ // @ts-check -import { NotFoundError } from '../Features/Errors/Errors.js' +import { parseReq, z, zz } from '@overleaf/validation-tools' -import { - parseReq as parseReqBase, - z, - zz, - ParamsError, -} from '@overleaf/validation-tools' - -export { z, zz } from '@overleaf/validation-tools' - -/** - * @param {any} req - * @param {any} schema - */ -export const parseReq = (req, schema) => { - try { - return parseReqBase(req, schema) - } catch (/** @type {any} */ err) { - if (err instanceof ParamsError) { - // convert into a NotFoundError that web understands - throw new NotFoundError('Not found').withCause(err) - } - throw err - } -} +export { ParamsError, parseReq, z, zz } from '@overleaf/validation-tools' export default { parseReq, diff --git a/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs b/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs index 1abd80a383..0c2c524871 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs +++ b/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs @@ -684,7 +684,7 @@ describe('SubscriptionController', function () { ctx.next = sinon.stub() await expect( ctx.SubscriptionController.pauseSubscription(ctx.req, ctx.res, ctx.next) - ).to.be.rejectedWith('Not found') + ).to.be.rejectedWith('Invalid params') }) it('should throw an error if an invalid pause length is provided', async function (ctx) { diff --git a/services/web/types/backend/express/request.d.ts b/services/web/types/backend/express/request.d.ts index 29eb4304b5..42b4524750 100644 --- a/services/web/types/backend/express/request.d.ts +++ b/services/web/types/backend/express/request.d.ts @@ -2,6 +2,14 @@ import 'express' import OAuth2Server from '@node-oauth/oauth2-server' import type SessionData from 'express-session' +// Request-scoped logger attached by @overleaf/metrics http.monitor() middleware. +// See libraries/metrics/http.js RequestLogger class. +interface RequestLogger { + addFields(fields: Record): void + setLevel(level: string): void + disable(): void +} + // Add properties to Express's Request object that are defined in JS middleware // or controllers and expected to be present in controllers. declare module 'express' { @@ -10,5 +18,6 @@ declare module 'express' { session: SessionData userRestrictions?: Set oauth_user?: OAuth2Server.User + logger: RequestLogger } }