revises the way that recursive backend validation works

This commit is contained in:
James
2020-06-16 15:35:28 -04:00
parent d3b36e00df
commit df31f4f144
7 changed files with 116 additions and 86 deletions

View File

@@ -1,8 +1,12 @@
const { ValidationError } = require('../errors');
const createValidationPromise = async (data, field) => {
const result = await field.validate(data, field);
return { result, field };
};
exports.iterateFields = async (data, fields, path = '') => {
const validationPromises = [];
const validatedFields = [];
fields.forEach((field) => {
const dataToValidate = data || {};
@@ -13,34 +17,47 @@ exports.iterateFields = async (data, fields, path = '') => {
// validated against directly
if (field.name === undefined && field.fields) {
field.fields.forEach((subField) => {
validationPromises.push(subField.validate(dataToValidate[subField.name], {
...subField,
path: `${path}${subField.name}`,
}));
validatedFields.push(subField);
validationPromises.push(createValidationPromise(dataToValidate[subField.name], subField));
});
} else if (field.fields) {
if (field.type === 'repeater' || field.type === 'flexible') {
const rowCount = Array.isArray(dataToValidate[field.name]) ? dataToValidate[field.name].length : 0;
validationPromises.push(createValidationPromise(rowCount, field));
if (Array.isArray(dataToValidate[field.name])) {
dataToValidate[field.name].forEach((rowData, i) => {
validationPromises.push(exports.iterateFields(rowData, field.fields, `${path}${field.name}.${i}.`));
});
}
} else {
validationPromises.push(exports.iterateFields(dataToValidate[field.name], field.fields, `${path}${field.name}`));
}
} else {
validationPromises.push(field.validate(dataToValidate[field.name], field));
validatedFields.push(field);
validationPromises.push(createValidationPromise(dataToValidate[field.name], field));
}
}
});
const validationResults = await Promise.all(validationPromises);
const errors = validationResults.reduce((results, result, i) => {
const field = validatedFields[i];
const errors = validationResults.reduce((results, result) => {
const { field, result: validationResult } = result;
if (Array.isArray(result)) {
return [
...results,
...result,
];
} if (result !== true) {
}
if (validationResult === false || typeof validationResult === 'string') {
const fieldPath = `${path}${field.name}`;
return [
...results,
{
field: `${path}${field.name}`,
message: result,
field: fieldPath,
message: validationResult,
},
];
}

View File

@@ -1,5 +1,3 @@
const { iterateFields } = require('./validateCreate');
const optionsToValidatorMap = {
number: (value, options = {}) => {
const parsedValue = parseInt(value, 10);
@@ -93,41 +91,17 @@ const optionsToValidatorMap = {
if (value) return true;
return 'This field is required.';
},
group: async (value, options) => {
return iterateFields(value, options.fields, `${options.name}.`);
},
repeater: async (value, options = {}) => {
const path = options.path || options.name;
if (!value || value.length === 0) {
return 'This field requires at least one row.';
}
if (options.minRows && value.length < options.minRows) {
repeater: (value, options = {}) => {
if (options.minRows && value < options.minRows) {
return `This field requires at least ${options.minRows} row(s).`;
}
if (options.maxRows && value.length > options.maxRows) {
if (options.maxRows && value > options.maxRows) {
return `This field requires no more than ${options.maxRows} row(s).`;
}
const errors = [];
const rowPromises = [];
value.forEach((row, i) => {
rowPromises.push(iterateFields(row, options.fields, `${path}.${i}.`));
});
const rowResults = await Promise.all(rowPromises);
rowResults.forEach((row) => {
row.forEach((fieldError) => {
errors.push(fieldError);
});
});
if (errors.length > 0) {
return errors;
if (!value) {
return 'This field requires at least one row.';
}
return true;
@@ -140,38 +114,17 @@ const optionsToValidatorMap = {
if (value) return true;
return 'This field is required.';
},
flexible: async (value, options) => {
if (value.length === 0) {
return `This field requires at least one ${options.singularLabel}.`;
flexible: (value, options) => {
if (options.minRows && value < options.minRows) {
return `This field requires at least ${options.minRows} row(s).`;
}
const errors = [];
const rowPromises = [];
if (options.maxRows && value > options.maxRows) {
return `This field requires no more than ${options.maxRows} row(s).`;
}
value.forEach((row, i) => {
const { blockType } = row;
const block = options.blocks.find(availableBlock => availableBlock.slug === blockType);
if (!block) {
return errors.push({
message: `Block ${i + 1} is missing a block type.`,
field: options.name,
});
}
return rowPromises.push(iterateFields(row, block.fields, `${options.name}.${i}.`));
});
const rowResults = await Promise.all(rowPromises);
rowResults.forEach((row) => {
row.forEach((fieldError) => {
errors.push(fieldError);
});
});
if (errors.length > 0) {
return errors;
if (!value) {
return 'This field requires at least one row.';
}
return true;