/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: BUSL-1.1 */ /* eslint-disable no-console */ import validators from 'vault/utils/forms/validators'; import { get } from '@ember/object'; import type { Validations, ValidationMap, FormValidations } from 'vault/app-types'; export const generateErrorCountMessage = (errorCount: number) => { if (errorCount < 1) return ''; // returns count specific message: 'There is an error/are N errors with this form.' const isPlural = errorCount > 1 ? `are ${errorCount} errors` : false; return `There ${isPlural ? isPlural : 'is an error'} with this form.`; }; export const validate = ( data?: unknown, validations?: Validations, validationMapKey = '' ): FormValidations => { let isValid = true; const state: ValidationMap = {}; let errorCount = 0; // consider valid when validations are not provided if (!validations) { return { isValid: true, state, invalidFormMessage: '' }; } for (const key in validations) { const rules = validations[key]; if (!Array.isArray(rules)) { console.error(`Must provide validations as an array for property "${key}".`); continue; } // a stateKey may be passed in to map the validations to a nested object // eg. validate(this.data, validations, 'data') => { 'data.key': { errors: [], warnings: [], isValid: true } } const stateKey = validationMapKey ? `${validationMapKey}.${key}` : key; state[stateKey] = { errors: [], warnings: [], isValid: true }; for (const rule of rules) { const { type, options, level, message, validator: customValidator } = rule; // check for custom validator or lookup in validators util by type const useCustomValidator = typeof customValidator === 'function'; const validator = useCustomValidator ? customValidator : validators[type]; if (!validator) { console.error( !type ? 'Validator not found. Either define type or pass custom validator function under "validator" key in validations object' : `Validator type: "${type}" not found. Available validators: ${Object.keys(validators).join( ', ' )}` ); continue; } // dot notation may be used to define key for nested property const passedValidation = useCustomValidator ? validator(data) : validator(get(data, key), options); if (!passedValidation) { // message can also be a function const validationMessage = typeof message === 'function' ? message(data) : message; // consider setting a prop like validationErrors directly on the model // for now return an errors object if (level === 'warn') { state[stateKey].warnings.push(validationMessage); } else { state[stateKey].errors.push(validationMessage); if (isValid) { isValid = false; } } } } errorCount += state[stateKey].errors.length; state[stateKey].isValid = !state[stateKey].errors.length; } return { isValid, state, invalidFormMessage: generateErrorCountMessage(errorCount) }; };