import { some, map, filter, keys, includes, countBy, flatten, } from 'lodash/fp'; import { isSomething, $, isNonEmptyString, isNothingOrEmpty, isNothing, } from '../common'; import { all, getDefaultOptions } from '../types'; import { applyRuleSet, makerule } from '../common/validationCommon'; import { BadRequestError } from '../common/errors'; export const fieldErrors = { AddFieldValidationFailed: 'Add field validation: ', }; export const allowedTypes = () => keys(all); export const getNewField = type => ({ name: '', // how field is referenced internally type, typeOptions: getDefaultOptions(type), label: '', // how field is displayed getInitialValue: 'default', // function that gets value when initially created getUndefinedValue: 'default', // function that gets value when field undefined on record }); const fieldRules = allFields => [ makerule('name', 'field name is not set', f => isNonEmptyString(f.name)), makerule('type', 'field type is not set', f => isNonEmptyString(f.type)), makerule('label', 'field label is not set', f => isNonEmptyString(f.label)), makerule('getInitialValue', 'getInitialValue function is not set', f => isNonEmptyString(f.getInitialValue)), makerule('getUndefinedValue', 'getUndefinedValue function is not set', f => isNonEmptyString(f.getUndefinedValue)), makerule('name', 'field name is duplicated', f => isNothingOrEmpty(f.name) || countBy('name')(allFields)[f.name] === 1), makerule('type', 'type is unknown', f => isNothingOrEmpty(f.type) || some(t => f.type === t)(allowedTypes())), ]; const typeOptionsRules = (field) => { const type = all[field.type]; if (isNothing(type)) return []; const def = optName => type.optionDefinitions[optName]; return $(field.typeOptions, [ keys, filter(o => isSomething(def(o)) && isSomething(def(o).isValid)), map(o => makerule( `typeOptions.${o}`, `${def(o).requirementDescription}`, field => def(o).isValid(field.typeOptions[o]), )), ]); }; export const validateField = allFields => (field) => { const everySingleField = includes(field)(allFields) ? allFields : [...allFields, field]; return applyRuleSet([...fieldRules(everySingleField), ...typeOptionsRules(field)])(field); }; export const validateAllFields = recordNode => $(recordNode.fields, [ map(validateField(recordNode.fields)), flatten, ]); export const addField = (recordTemplate, field) => { if (isNothingOrEmpty(field.label)) { field.label = field.name; } const validationMessages = validateField([...recordTemplate.fields, field])(field); if (validationMessages.length > 0) { const errors = map(m => m.error)(validationMessages); throw new BadRequestError(`${fieldErrors.AddFieldValidationFailed} ${errors.join(', ')}`); } recordTemplate.fields.push(field); };