mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Merge pull request #29593 from overleaf/mfb-from-joi-to-zod-analytics
[Analytics service] Migrate from JOI to ZOD GitOrigin-RevId: 5f6abc23c5359ca8599ef4b7d660d5f08551d247
This commit is contained in:
committed by
Copybot
parent
51639030f0
commit
e861e28296
@@ -2,11 +2,13 @@ const { ParamsError } = require('./Errors')
|
||||
const { z } = require('zod')
|
||||
const { zz } = require('./zodHelpers')
|
||||
const { validateReq } = require('./validateReq')
|
||||
const { validateSchema } = require('./validateSchema')
|
||||
const { handleValidationError } = require('./handleValidationError')
|
||||
|
||||
module.exports = {
|
||||
z,
|
||||
zz,
|
||||
validateSchema,
|
||||
validateReq,
|
||||
handleValidationError,
|
||||
ParamsError,
|
||||
|
||||
60
libraries/validation-tools/validateSchema.js
Normal file
60
libraries/validation-tools/validateSchema.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// @ts-check
|
||||
const { isZodErrorLike } = require('zod-validation-error')
|
||||
|
||||
/**
|
||||
* @typedef {import('zod').ZodType} ZodType
|
||||
*/
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('zod').output<T>} output<T>
|
||||
*/
|
||||
|
||||
/**
|
||||
* A helper function to safely get a nested value from an object
|
||||
* using a path array (e.g., ["query", "resource_type"])
|
||||
*/
|
||||
function getPathValue(data, path) {
|
||||
let current = data
|
||||
for (const key of path) {
|
||||
if (current === null || typeof current !== 'object') {
|
||||
return undefined
|
||||
}
|
||||
current = current[key]
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
const isRequiredError = (issue, value) =>
|
||||
value === undefined &&
|
||||
(issue.code === 'invalid_type' || issue.code === 'invalid_union')
|
||||
|
||||
/**
|
||||
* Validates data against a Zod schema and throws a user-friendly error.
|
||||
*
|
||||
* @template {ZodType} T
|
||||
* @param {T} schema - The Zod schema
|
||||
* @param {unknown} data - The data to validate
|
||||
* @returns {output<T>} The validated (and transformed) data
|
||||
*/
|
||||
function validateSchema(schema, data) {
|
||||
try {
|
||||
return schema.parse(data)
|
||||
} catch (err) {
|
||||
if (isZodErrorLike(err)) {
|
||||
const errorMessages = err.issues.map(issue => {
|
||||
const value = getPathValue(data, issue.path)
|
||||
const fieldName = String(issue.path[issue.path.length - 1])
|
||||
if (isRequiredError(issue, value)) {
|
||||
return `"${fieldName}" is required`
|
||||
}
|
||||
return `"${fieldName}" - ` + issue.message
|
||||
})
|
||||
|
||||
throw new Error(errorMessages.join('; '))
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { validateSchema }
|
||||
@@ -3,6 +3,13 @@ const mongodb = require('mongodb')
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const dateWithTransform = (schema, allowNull = false) => {
|
||||
return schema.transform(dt => {
|
||||
if (allowNull && !dt) return null
|
||||
return dt instanceof Date ? dt : new Date(dt)
|
||||
})
|
||||
}
|
||||
|
||||
const zz = {
|
||||
objectId: () =>
|
||||
z.string().refine(ObjectId.isValid, { message: 'invalid Mongo ObjectId' }),
|
||||
@@ -12,6 +19,14 @@ const zz = {
|
||||
.refine(ObjectId.isValid, { message: 'invalid Mongo ObjectId' })
|
||||
.transform(val => new ObjectId(val)),
|
||||
hex: () => z.string().regex(/^[0-9a-f]*$/),
|
||||
datetime: () => dateWithTransform(z.union([z.iso.datetime(), z.date()])),
|
||||
datetimeNullable: () =>
|
||||
dateWithTransform(z.union([z.iso.datetime(), z.date(), z.null()]), true),
|
||||
datetimeNullish: () =>
|
||||
dateWithTransform(
|
||||
z.union([z.iso.datetime(), z.date(), z.null(), z.undefined()]),
|
||||
true
|
||||
),
|
||||
}
|
||||
|
||||
module.exports = { zz }
|
||||
|
||||
Reference in New Issue
Block a user