@ts-fluentvalidation/core
Version:
Core library of @ts-fluentvalidation providing validations in a fluent syntax.
239 lines • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createValidator = createValidator;
const tslib_1 = require("tslib");
const errors_1 = require("./errors");
const validate_async_1 = require("./functions/validate-async");
const validate_sync_1 = require("./functions/validate-sync");
const result_1 = require("./result");
const ts_helpers_1 = require("./types/ts-helpers");
const validation_context_1 = require("./validation-context");
/**
* Creates a new validator with the given configuration.
*
* @param config - The configuration for the validator.
*/
function createValidator(config) {
const _validations = {};
const keyValidations = [];
const _defaultConfig = {
cascadeMode: 'Continue'
};
const validatorConfig = Object.assign(Object.assign({}, _defaultConfig), (config !== null && config !== void 0 ? config : {}));
let preValidation = () => true;
/**
* Updates the validators validations dictionary. Called after validations for a given key were added.
*
* @param validator - The validator to update.
*/
function updateValidations(validator) {
validator.validations = keyValidations.reduce((acc, { key, validations: keyValidations }) => {
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(...keyValidations);
return acc;
}, {});
}
return {
validations: _validations,
ruleFor(key, ...cascadeModeAndValidations) {
var _a;
const cascadeMode = typeof cascadeModeAndValidations[0] === 'string' ? cascadeModeAndValidations.shift() : undefined;
const validations = cascadeModeAndValidations;
overridePropertyNames(validations);
applyConditions(...validations);
keyValidations.push({
key,
validations: validations,
cascadeMode: (_a = cascadeMode !== null && cascadeMode !== void 0 ? cascadeMode : validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
updateValidations(this);
return this;
},
ruleForEach(key, ...cascadeModeAndValidations) {
var _a;
const cascadeMode = typeof cascadeModeAndValidations[0] === 'string' ? cascadeModeAndValidations.shift() : undefined;
const validations = cascadeModeAndValidations;
overridePropertyNames(validations);
applyConditions(...validations);
validations.forEach(v => {
v.metadata.isEach = true;
});
keyValidations.push({
key,
validations: validations,
cascadeMode: (_a = cascadeMode !== null && cascadeMode !== void 0 ? cascadeMode : validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
updateValidations(this);
return this;
},
include(validator) {
Object.entries(validator.validations).forEach(([key, validations]) => {
var _a;
keyValidations.push({
key: key,
validations: validations,
cascadeMode: (_a = validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
updateValidations(this);
});
return this;
},
preValidate(preValidationFunc) {
preValidation = preValidationFunc;
return this;
},
when(predicate, callback) {
const validator = Object.assign({}, this);
const conditionalValidator = callback(createValidator());
const conditionalValidations = conditionalValidator.validations;
Object.entries(conditionalValidations).forEach(([key, validations]) => {
var _a;
keyValidations.push({
key: key,
validations: validations.map(v => v.when(predicate)),
cascadeMode: (_a = validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
});
updateValidations(validator);
return Object.assign(Object.assign({}, validator), { otherwise(callback) {
const otherwiseValidator = callback(createValidator());
const otherwiseValidations = otherwiseValidator.validations;
Object.entries(otherwiseValidations).forEach(([key, validations]) => {
var _a;
keyValidations.push({
key: key,
validations: validations.map(v => v.when((m, c) => !predicate(m, c))),
cascadeMode: (_a = validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
});
updateValidations(validator);
return Object.assign({}, validator);
} });
},
whenAsync(predicate, callback) {
const validator = Object.assign({}, this);
const conditionalValidator = callback(createValidator());
const conditionalValidations = conditionalValidator.validations;
Object.entries(conditionalValidations).forEach(([key, validations]) => {
var _a;
keyValidations.push({
key: key,
validations: validations.map(v => v.whenAsync(predicate)),
cascadeMode: (_a = validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
});
updateValidations(validator);
return Object.assign(Object.assign({}, validator), { otherwise(callback) {
const otherwiseValidator = callback(createValidator());
const otherwiseValidations = otherwiseValidator.validations;
Object.entries(otherwiseValidations).forEach(([key, validations]) => {
var _a;
keyValidations.push({
key: key,
validations: validations.map(v => v.whenAsync((m, c) => tslib_1.__awaiter(this, void 0, void 0, function* () { return !(yield predicate(m, c)); }))),
cascadeMode: (_a = validatorConfig.propertyCascadeMode) !== null && _a !== void 0 ? _a : 'Continue'
});
});
updateValidations(validator);
return Object.assign({}, validator);
} });
},
unless(predicate, callback) {
return this.when((model, validationContext) => !predicate(model, validationContext), callback);
},
unlessAsync(predicate, callback) {
return this.whenAsync((model, validationContext) => tslib_1.__awaiter(this, void 0, void 0, function* () { return !(yield predicate(model, validationContext)); }), callback);
},
validate(modelOrContext, config) {
config === null || config === void 0 ? void 0 : config(validatorConfig);
const context = getValidationContext(modelOrContext);
const preValidationResult = (0, result_1.createValidationResult)(context.failures);
const shouldContinue = preValidation(context, preValidationResult);
if (!shouldContinue) {
if (preValidationResult.isValid === false && validatorConfig.throwOnFailures) {
throwValidationError(preValidationResult);
}
return (0, result_1.createValidationResult)(preValidationResult.failures);
}
(0, validate_sync_1.validateSync)(context, keyValidations, validatorConfig);
return (0, result_1.createValidationResult)(context.failures);
},
validateAsync(modelOrContext, config) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
config === null || config === void 0 ? void 0 : config(validatorConfig);
const context = getValidationContext(modelOrContext);
const preValidationResult = (0, result_1.createValidationResult)(context.failures);
const shouldContinue = preValidation(context, preValidationResult);
if (!shouldContinue) {
if (preValidationResult.isValid === false && validatorConfig.throwOnFailures) {
throwValidationError(preValidationResult);
}
return (0, result_1.createValidationResult)(preValidationResult.failures);
}
yield (0, validate_async_1.validateAsync)(context, keyValidations, validatorConfig);
return (0, result_1.createValidationResult)(context.failures);
});
},
validateAndThrow(model) {
return this.validate(model, c => (c.throwOnFailures = true));
},
validateAndThrowAsync(model) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield this.validateAsync(model, c => (c.throwOnFailures = true));
});
}
};
}
/**
* Applies conditions if neccesary.
*
* @template TModel - The type of the model being validated.
* @template TValue - The type of the value being validated.
* @param validations - The validations to apply the conditions to.
* @returns The updated validator.
*/
function applyConditions(...validations) {
// the latest validation to add which contains `ApplyConditionTo === 'AllValidators'` or none which will default to 'AllValidators'
// will aplly its condition to all preceding validators from the same `ruleFor` call
const lastConditionValidation = (0, ts_helpers_1.getLastElement)(validations, v => v.metadata.condition !== undefined && v.metadata.applyConditionTo !== 'CurrentValidator');
if (lastConditionValidation) {
const index = validations.indexOf(lastConditionValidation);
const condition = lastConditionValidation.metadata.condition;
if (condition) {
for (let i = 0; i < index; i++) {
validations[i].metadata.condition = condition;
}
}
}
const lastAsyncConditionValidation = (0, ts_helpers_1.getLastElement)(validations, v => v.metadata.asyncCondition !== undefined && v.metadata.applyAsyncConditionTo !== 'CurrentValidator');
if (lastAsyncConditionValidation) {
const index = validations.indexOf(lastAsyncConditionValidation);
const asyncCondition = lastAsyncConditionValidation.metadata.asyncCondition;
if (asyncCondition) {
for (let i = 0; i < index; i++) {
validations[i].metadata.asyncCondition = asyncCondition;
}
}
}
}
function overridePropertyNames(keyValidations) {
var _a;
const lastPropertyNameOverride = (0, ts_helpers_1.getLastElement)(keyValidations, v => v.metadata.propertyNameOverride !== undefined);
if ((_a = lastPropertyNameOverride === null || lastPropertyNameOverride === void 0 ? void 0 : lastPropertyNameOverride.metadata) === null || _a === void 0 ? void 0 : _a.propertyNameOverride) {
const propertyNameOverride = lastPropertyNameOverride.metadata.propertyNameOverride;
keyValidations.forEach(v => {
if (v.metadata.propertyNameOverride === undefined) {
v.metadata.propertyNameOverride = propertyNameOverride;
}
});
}
}
function getValidationContext(modelOrContext) {
return (0, validation_context_1.isValidationContext)(modelOrContext) ? modelOrContext : (0, validation_context_1.createValidationContext)(modelOrContext);
}
function throwValidationError(validationResult) {
throw new errors_1.ValidationError(validationResult.failures);
}
//# sourceMappingURL=create-validator.js.map