261 lines
31 KiB
JavaScript
261 lines
31 KiB
JavaScript
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||
|
});
|
||
|
};
|
||
|
export default class Validator {
|
||
|
constructor() {
|
||
|
this.steps = [];
|
||
|
this.validationAttributes = [];
|
||
|
}
|
||
|
/**
|
||
|
* @param thingName The name of the thing to validate.
|
||
|
* @param value The value to verify.
|
||
|
* @param onlyFormat {@code true} to only validate format properties, {@code false} otherwise.
|
||
|
* @param connection A connection to use in case of wrapped transactions.
|
||
|
*/
|
||
|
execute(thingName, value, onlyFormat, connection) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
const bag = new ValidationBag();
|
||
|
for (const step of this.steps) {
|
||
|
if (onlyFormat && !step.isFormat)
|
||
|
continue;
|
||
|
const result = step.verifyStep(value, thingName, connection);
|
||
|
if ((result === false || result instanceof Promise && (yield result) === false) && step.throw) {
|
||
|
const error = step.throw();
|
||
|
error.thingName = thingName;
|
||
|
error.value = value;
|
||
|
bag.addMessage(error);
|
||
|
}
|
||
|
else if (step.interrupt !== undefined && step.interrupt(value)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (bag.hasMessages()) {
|
||
|
throw bag;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
defined() {
|
||
|
this.validationAttributes.push('required');
|
||
|
this.addStep({
|
||
|
verifyStep: val => val !== undefined,
|
||
|
throw: () => new UndefinedValueValidationError(),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
acceptUndefined() {
|
||
|
this.addStep({
|
||
|
verifyStep: () => true,
|
||
|
throw: null,
|
||
|
interrupt: val => val === undefined || val === null,
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
equals(other) {
|
||
|
this.addStep({
|
||
|
verifyStep: val => val === other,
|
||
|
throw: () => new BadValueValidationError(other),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
regexp(regexp) {
|
||
|
this.validationAttributes.push(`pattern="${regexp}"`);
|
||
|
this.addStep({
|
||
|
verifyStep: val => regexp.test(val),
|
||
|
throw: () => new InvalidFormatValidationError(),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
length(length) {
|
||
|
this.addStep({
|
||
|
verifyStep: val => val.length === length,
|
||
|
throw: () => new BadLengthValidationError(length),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* @param minLength included
|
||
|
* @param maxLength included
|
||
|
*/
|
||
|
between(minLength, maxLength) {
|
||
|
this.addStep({
|
||
|
verifyStep: val => {
|
||
|
const length = val.length;
|
||
|
return length >= minLength && length <= maxLength;
|
||
|
},
|
||
|
throw: () => new BadLengthValidationError(minLength, maxLength),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* @param min included
|
||
|
*/
|
||
|
min(min) {
|
||
|
this.validationAttributes.push(`min="${min}"`);
|
||
|
this._min = min;
|
||
|
this.addStep({
|
||
|
verifyStep: val => {
|
||
|
return val >= min;
|
||
|
},
|
||
|
throw: () => new OutOfRangeValidationError(this._min, this._max),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* @param max included
|
||
|
*/
|
||
|
max(max) {
|
||
|
this.validationAttributes.push(`max="${max}"`);
|
||
|
this._max = max;
|
||
|
this.addStep({
|
||
|
verifyStep: val => {
|
||
|
return val <= max;
|
||
|
},
|
||
|
throw: () => new OutOfRangeValidationError(this._min, this._max),
|
||
|
isFormat: true,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
unique(model, querySupplier) {
|
||
|
this.addStep({
|
||
|
verifyStep: (val, thingName, c) => __awaiter(this, void 0, void 0, function* () {
|
||
|
let query;
|
||
|
if (querySupplier) {
|
||
|
query = querySupplier().where(thingName, val);
|
||
|
}
|
||
|
else {
|
||
|
query = model.constructor.select('1').where(thingName, val);
|
||
|
}
|
||
|
if (typeof model.id === 'number')
|
||
|
query = query.whereNot('id', model.id);
|
||
|
return (yield query.execute(c)).results.length === 0;
|
||
|
}),
|
||
|
throw: () => new AlreadyExistsValidationError(model.table),
|
||
|
isFormat: false,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
exists(modelClass, foreignKey) {
|
||
|
this.addStep({
|
||
|
verifyStep: (val, thingName, c) => __awaiter(this, void 0, void 0, function* () { return (yield modelClass.select('1').where(foreignKey !== undefined ? foreignKey : thingName, val).execute(c)).results.length >= 1; }),
|
||
|
throw: () => new UnknownRelationValidationError(modelClass.table, foreignKey),
|
||
|
isFormat: false,
|
||
|
});
|
||
|
return this;
|
||
|
}
|
||
|
addStep(step) {
|
||
|
this.steps.push(step);
|
||
|
}
|
||
|
getValidationAttributes() {
|
||
|
return this.validationAttributes;
|
||
|
}
|
||
|
step(step) {
|
||
|
this.validationAttributes.push(`step="${step}"`);
|
||
|
return this;
|
||
|
}
|
||
|
}
|
||
|
export class ValidationBag extends Error {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this.messages = {};
|
||
|
}
|
||
|
addMessage(err) {
|
||
|
if (!err.thingName) {
|
||
|
throw new Error('Null thing name');
|
||
|
}
|
||
|
this.messages[err.thingName] = {
|
||
|
name: err.name,
|
||
|
message: err.message,
|
||
|
value: err.value,
|
||
|
};
|
||
|
}
|
||
|
hasMessages() {
|
||
|
return Object.keys(this.messages).length > 0;
|
||
|
}
|
||
|
getMessages() {
|
||
|
return this.messages;
|
||
|
}
|
||
|
}
|
||
|
export class ValidationError extends Error {
|
||
|
get name() {
|
||
|
return this.constructor.name;
|
||
|
}
|
||
|
}
|
||
|
export class BadLengthValidationError extends ValidationError {
|
||
|
constructor(expectedLength, maxLength) {
|
||
|
super();
|
||
|
this.expectedLength = expectedLength;
|
||
|
this.maxLength = maxLength;
|
||
|
}
|
||
|
get message() {
|
||
|
return `${this.thingName} expected length: ${this.expectedLength}${this.maxLength !== undefined ? ` to ${this.maxLength}` : ''}; ` +
|
||
|
`actual length: ${this.value.length}.`;
|
||
|
}
|
||
|
}
|
||
|
export class BadValueValidationError extends ValidationError {
|
||
|
constructor(expectedValue) {
|
||
|
super();
|
||
|
this.expectedValue = expectedValue;
|
||
|
}
|
||
|
get message() {
|
||
|
return `Expected: ${this.expectedValue}; got: ${this.value}.`;
|
||
|
}
|
||
|
}
|
||
|
export class OutOfRangeValidationError extends ValidationError {
|
||
|
constructor(min, max) {
|
||
|
super();
|
||
|
this.min = min;
|
||
|
this.max = max;
|
||
|
}
|
||
|
get message() {
|
||
|
if (this.min === undefined) {
|
||
|
return `${this.thingName} must be at most ${this.max}`;
|
||
|
}
|
||
|
else if (this.max === undefined) {
|
||
|
return `${this.thingName} must be at least ${this.min}`;
|
||
|
}
|
||
|
return `${this.thingName} must be between ${this.min} and ${this.max}.`;
|
||
|
}
|
||
|
}
|
||
|
export class InvalidFormatValidationError extends ValidationError {
|
||
|
get message() {
|
||
|
return `"${this.value}" is not a valid ${this.thingName}.`;
|
||
|
}
|
||
|
}
|
||
|
export class UndefinedValueValidationError extends ValidationError {
|
||
|
get message() {
|
||
|
return `${this.thingName} is required.`;
|
||
|
}
|
||
|
}
|
||
|
export class AlreadyExistsValidationError extends ValidationError {
|
||
|
constructor(table) {
|
||
|
super();
|
||
|
this.table = table;
|
||
|
}
|
||
|
get message() {
|
||
|
return `${this.value} already exists in ${this.table}.${this.thingName}.`;
|
||
|
}
|
||
|
}
|
||
|
export class UnknownRelationValidationError extends ValidationError {
|
||
|
constructor(table, foreignKey) {
|
||
|
super();
|
||
|
this.table = table;
|
||
|
this.foreignKey = foreignKey;
|
||
|
}
|
||
|
get message() {
|
||
|
return `${this.thingName}=${this.value} relation was not found in ${this.table}${this.foreignKey !== undefined ? `.${this.foreignKey}` : ''}.`;
|
||
|
}
|
||
|
}
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6Ii4vIiwic291cmNlcyI6WyJkYi9WYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBSUEsTUFBTSxDQUFDLE9BQU8sT0FBTyxTQUFTO0lBQTlCO1FBQ3FCLFVBQUssR0FBd0IsRUFBRSxDQUFDO1FBQ2hDLHlCQUFvQixHQUFhLEVBQUUsQ0FBQztJQXlLekQsQ0FBQztJQXBLRzs7Ozs7T0FLRztJQUNHLE9BQU8sQ0FBQyxTQUFpQixFQUFFLEtBQW9CLEVBQUUsVUFBbUIsRUFBRSxVQUF1Qjs7WUFDL0YsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUVoQyxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7Z0JBQzNCLElBQUksVUFBVSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVE7b0JBQUUsU0FBUztnQkFFM0MsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsU0FBUyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUM3RCxJQUFJLENBQUMsTUFBTSxLQUFLLEtBQUssSUFBSSxNQUFNLFlBQVksT0FBTyxJQUFJLENBQUMsTUFBTSxNQUFNLENBQUMsS0FBSyxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFO29CQUMzRixNQUFNLEtBQUssR0FBb0IsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO29CQUM1QyxLQUFLLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztvQkFDNUIsS0FBSyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7b0JBQ3BCLEdBQUcsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7aUJBQ3pCO3FCQUFNLElBQUksSUFBSSxDQUFDLFNBQVMsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRTtvQkFDOUQsTUFBTTtpQkFDVDthQUNKO1lBRUQsSUFB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
|