Эх сурвалжийг харах

Return a formatted JSON error when validation fails

GitOrigin-RevId: 0f2d3d73a6e371aa332dee245be8802250adfb2b
Andrew Rumble 10 сар өмнө
parent
commit
b1cf8d4e88

+ 17 - 1
services/web/app/src/infrastructure/Validation.js

@@ -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 })

+ 2 - 2
services/web/test/acceptance/src/ProjectCRUDTests.mjs

@@ -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')
     })

+ 11 - 4
services/web/test/acceptance/src/ProjectInviteTests.mjs

@@ -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()
             }