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.

127 lines
2.8 KiB
JavaScript

"use strict";
const errorChain = require("error-chain");
let ValidationError = errorChain("ValidationError");
function isPresent(value) {
if (value == null) {
throw new ValidationError("Required value is missing", { errorType: "isPresent" });
}
}
function isString(value) {
if (value != null && typeof value !== "string") {
throw new ValidationError("Value is not a string", { errorType: "isString" });
}
}
function isBoolean(value) {
if (value != null && typeof value !== "boolean") {
throw new ValidationError("Value is not a boolean", { errorType: "isBoolean" });
}
}
function isOneOf(...acceptableValues) {
let valueSet = new Set(acceptableValues);
return function (value) {
if (value != null && !valueSet.has(value)) {
throw new ValidationError("Value is not one of the specified acceptable values", {
errorType: "isOneOf",
acceptableValues: acceptableValues
});
}
};
}
/* NOTE: Extremely sketchy way of tracking validation state via a module-global object, but we can get away with this because validation is completely synchronous */
let context;
function assertProperties(object, properties) {
for (let [property, assertions] of Object.entries(properties)) {
context.path.push(property);
let value = object[property];
try {
for (let assertion of assertions) {
assertion(value);
}
} catch (error) {
if (error.input == null) {
error.input = value;
}
throw error;
}
context.path.pop();
}
}
/* FIXME: Abstract out the path and value assignment for errors into a `withPath(...)` abstraction */
function arrayOf(assertions) {
return function (value) {
if (value != null) {
if (!Array.isArray(value)) {
throw new ValidationError("Value is not an array", { errorType: "arrayOf" });
} else {
for (let [i, item] of value.entries()) {
context.path.push(i);
try {
for (let assertion of assertions) {
assertion(item);
}
} catch (error) {
if (error.input == null) {
error.input = value;
}
throw error;
}
context.path.pop();
}
}
}
};
}
function validate(validator, value) {
context = {
path: []
};
try {
validator(value);
} catch (error) {
if (error.path == null) {
error.path = context.path;
}
throw error;
}
context = undefined;
}
module.exports = {
validate, assertProperties, ValidationError,
isPresent, isString, isOneOf, arrayOf, isBoolean
};
// try {
// validate(validateLoginPayload, {
// type: 'm.login.password',
// password: 'test',
// identifier: { type: 'm.id.user', user: 'joepie91' },
// initial_device_display_name: 'http://localhost:3001/index.html via Firefox on Linux',
// user: 'joepie91',
// arrayStuff: [ 42 ]
// });
// } catch (error) {
// console.log(require("util").inspect(error, {colors: true, depth: null}));
// }