Return a formatted JSON error when validation fails

GitOrigin-RevId: 0f2d3d73a6e371aa332dee245be8802250adfb2b
This commit is contained in:
Andrew Rumble
2025-09-18 14:18:34 +01:00
committed by Copybot
parent 3cd9ded7c8
commit b1cf8d4e88
3 changed files with 30 additions and 7 deletions
@@ -9,6 +9,11 @@ const {
zz,
ParamsError,
} = require('@overleaf/validation-tools')
const { isZodErrorLike, fromError } = require('zod-validation-error')
/**
* @typedef {import('express').ErrorRequestHandler} ErrorRequestHandler
*/
const objectIdValidator = {
type: 'objectId',
@@ -31,10 +36,21 @@ const objectIdValidator = {
}
const Joi = CelebrateJoi.extend(objectIdValidator)
const errorMiddleware = errors()
const errorMiddleware = [
errors(),
/** @type {ErrorRequestHandler} */
(err, req, res, next) => {
if (!isZodErrorLike(err)) {
return next(err)
}
res.status(400).json({ ...fromError(err), statusCode: 400 })
},
]
/**
* Validation middleware
* @deprecated Please use Zod schemas and `validateReq` instead
*/
function validate(schema) {
return celebrate(schema, { allowUnknown: true })
@@ -190,7 +190,7 @@ describe('Project CRUD', function () {
const project = await Project.findById(this.projectId).exec()
expect(project.publicAccesLevel).to.equal('tokenBased')
})
it('returns a 400 when publicAccessLevel is set an unsupported access level', async function () {
it('returns a 400 when publicAccessLevel is an unsupported access level', async function () {
await this.user.makePrivate(this.projectId)
const { response, body } = await this.user.doRequest('POST', {
url: `/project/${this.projectId}/settings/admin`,
@@ -199,7 +199,7 @@ describe('Project CRUD', function () {
},
})
expect(response.statusCode).to.equal(400)
expect(body).to.include('Unexpected access level')
expect(body.details[0].message).to.equal('unexpected access level')
const project = await Project.findById(this.projectId).exec()
expect(project.publicAccesLevel).to.equal('private')
})
@@ -375,8 +375,10 @@ describe('ProjectInviteTests', function () {
return done(err)
}
expect(response.statusCode).to.equal(400)
expect(response.body.validation.body.message).to.equal(
'"email" must be a string'
expect(body.details).to.have.lengthOf(1)
expect(response.body.details[0].path).to.eql(['body', 'email'])
expect(response.body.details[0].message).to.equal(
'Invalid input: expected string, received object'
)
done()
}
@@ -402,8 +404,13 @@ describe('ProjectInviteTests', function () {
return done(err)
}
expect(response.statusCode).to.equal(400)
expect(response.body.validation.body.message).to.equal(
'"privileges" must be one of [readOnly, readAndWrite, review]'
expect(body.details).to.have.lengthOf(1)
expect(response.body.details[0].path).to.eql([
'body',
'privileges',
])
expect(response.body.details[0].message).to.equal(
'Invalid option: expected one of "readOnly"|"readAndWrite"|"review"'
)
done()
}