You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.4 KiB
JavaScript

"use strict";
const defaultValue = require("default-value");
const asExpression = require("as-expression");
const splitFilterN = require("split-filter-n");
const combinator = require("@validatem/combinator");
const ValidationError = require("@validatem/error");
const matchValidationError = require("@validatem/match-validation-error");
const validationResult = require("@validatem/validation-result");
// TODO: Document that this passes on context
function concat(arrays) {
return arrays.slice(1).reduce((combined, array) => {
return combined.concat(array);
}, arrays[0]);
}
module.exports = function wrapError(message, code, rules, options = {}) {
if (typeof code !== "string") {
throw new Error(`'code' argument must be a string`);
} else if (typeof message !== "string") {
throw new Error(`'message' argument must be a string`);
} else if (rules == null) {
throw new Error(`'rules' argument is required`);
} else {
let preserveOriginalErrors = defaultValue(options.preserveOriginalErrors, false);
return combinator((value, applyValidators, context) => {
let result = applyValidators(value, rules, context);
if (result.errors.length > 0) {
let { errors, newValue } = result;
let errorsByType = splitFilterN(errors, [ "validationRoot", "validationPath", "other" ], (error) => {
if (matchValidationError(error)) {
if (error.path.length === 0) {
return "validationRoot";
} else {
return "validationPath";
}
} else {
return "other";
}
});
let hasRootValidationErrors = errorsByType.validationRoot.length > 0;
let hasPathValidationErrors = errorsByType.validationPath.length > 0;
let hasValidationErrors = hasRootValidationErrors || hasPathValidationErrors;
let transformedValidationErrors = asExpression(() => {
if (hasValidationErrors) {
if (!preserveOriginalErrors) {
return [ new ValidationError(message, { code: code }) ];
} else {
// If possible, we try to move any subpath errors into the subErrors of an (optionally newly-created) root error. Otherwise, it can become difficult for the user to correlate together the root and subpath errors that are related, when we start changing the messages of the root errors.
if (errorsByType.validationRoot.length === 0) {
return [ new ValidationError(message, { code: code, subErrors: errorsByType.validationPath }) ];
} else if (errorsByType.validationRoot.length === 1) {
let error = errorsByType.validationRoot[0];
// TODO: Currently we cannot set `originalError` due to a bug in `create-error`; switch to a better error implementation
return [ new ValidationError(`${message} (${error.message})`, { code: code, subErrors: errorsByType.validationPath }) ];
} else {
// If there are multiple root errors, we cannot determine which root error the subpath errors belong to, so we'll just provide them as a flat list
return concat([
errorsByType.validationRoot.map((error) => {
return new ValidationError(`${message} (${error.message})`, { code: code })
}),
errorsByType.validationPath
]);
}
}
} else {
return [];
}
});
return validationResult({
newValue: newValue,
errors: concat([
transformedValidationErrors,
errorsByType.other
])
});
} else {
return result;
}
});
}
};