import type { FieldFilter, Filter } from '@directus/types'; import { flatten } from 'lodash-es'; import { generateJoi, JoiOptions } from './generate-joi.js'; import { injectFunctionResults } from './inject-function-results.js'; import type Joi from 'joi'; /** * Validate the payload against the given filter rules * * @param {Filter} filter - The filter rules to check against * @param {Record} payload - The payload to validate * @param {JoiOptions} [options] - Optional options to pass to Joi * @returns Array of errors */ export function validatePayload( filter: Filter, payload: Record, options?: JoiOptions ): Joi.ValidationError[] { const errors: Joi.ValidationError[] = []; /** * Note there can only be a single _and / _or per level */ if (Object.keys(filter)[0] === '_and') { const subValidation = Object.values(filter)[0] as FieldFilter[]; const nestedErrors = flatten( subValidation.map((subObj: Record) => { return validatePayload(subObj, payload, options); }) ).filter((err?: Joi.ValidationError) => err); errors.push(...nestedErrors); } else if (Object.keys(filter)[0] === '_or') { const subValidation = Object.values(filter)[0] as FieldFilter[]; const swallowErrors: Joi.ValidationError[] = []; const pass = subValidation.some((subObj: Record) => { const nestedErrors = validatePayload(subObj, payload, options); if (nestedErrors.length > 0) { swallowErrors.push(...nestedErrors); return false; } return true; }); if (!pass) { errors.push(...swallowErrors); } } else { const payloadWithFunctions = injectFunctionResults(payload, filter); const schema = generateJoi(filter as FieldFilter, options); const { error } = schema.validate(payloadWithFunctions, { abortEarly: false }); if (error) { errors.push(error); } } return errors; }