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.

84 lines
2.3 KiB
JavaScript

"use strict";
const asExpression = require("as-expression");
const defaultValue = require("default-value");
const assureArray = require("assure-array");
const flatten = require("flatten");
const ValidationError = require("@validatem/error");
const validationResult = require("@validatem/validation-result");
const annotateErrors = require("@validatem/annotate-errors");
const combinator = require("@validatem/combinator");
function containsRules(rules) {
// TODO: We cannot use normalize-rules here (for now) since that would create a cyclical dependency; figure out a way to work around this, maybe by removing the hasShape feature in `normalize-rules`, or moving the flattening itself out into a separate `flatten-rules` package?
if (rules == null) {
return false;
} else {
// TODO: Switch to `Array#flat` once Node 10.x goes EOL (April 2021)
let flattenedRules = flatten(assureArray(rules));
if (!flattenedRules.some((rule) => rule != null)) {
return false;
} else {
return true;
}
}
}
module.exports = function hasShape(rules) {
return combinator((object, applyValidators, context) => {
let allowExtraProperties = defaultValue(context.allowExtraProperties, false);
let unexpectedPropertyErrors = asExpression(() => {
if (!allowExtraProperties) {
return Object.keys(object).map((propertyName) => {
if (!containsRules(rules[propertyName])) {
return new ValidationError(`Encountered an unexpected property '${propertyName}'`);
} else {
return null;
}
}).filter((error) => {
return (error != null);
});
} else {
return [];
}
});
if (unexpectedPropertyErrors.length > 0) {
return validationResult({
errors: unexpectedPropertyErrors,
newValue: object
});
} else {
let errors = [];
let newObject = {};
for (let [ key, value ] of Object.entries(object)) {
let rule = rules[key];
if (rule != null) {
let { errors: keyErrors, newValue } = applyValidators(value, rule);
let annotatedErrors = annotateErrors({
pathSegments: [ key ],
errors: keyErrors
});
errors.push(... annotatedErrors);
newObject[key] = newValue;
} else {
// Extraneous property
newObject[key] = value;
}
}
return validationResult({
errors: errors,
newValue: newObject
});
}
});
};