123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- var __read = (this && this.__read) || function (o, n) {
- var m = typeof Symbol === "function" && o[Symbol.iterator];
- if (!m) return o;
- var i = m.call(o), r, ar = [], e;
- try {
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
- }
- catch (error) { e = { error: error }; }
- finally {
- try {
- if (r && !r.done && (m = i["return"])) m.call(i);
- }
- finally { if (e) throw e.error; }
- }
- return ar;
- };
- import { ValidationError } from './ValidationError';
- import { ValidationTypes } from './ValidationTypes';
- import { ValidationUtils } from './ValidationUtils';
- import { isPromise, convertToArray } from '../utils';
- import { getMetadataStorage } from '../metadata/MetadataStorage';
- /**
- * Executes validation over given object.
- */
- var ValidationExecutor = /** @class */ (function () {
- // -------------------------------------------------------------------------
- // Constructor
- // -------------------------------------------------------------------------
- function ValidationExecutor(validator, validatorOptions) {
- this.validator = validator;
- this.validatorOptions = validatorOptions;
- // -------------------------------------------------------------------------
- // Properties
- // -------------------------------------------------------------------------
- this.awaitingPromises = [];
- this.ignoreAsyncValidations = false;
- // -------------------------------------------------------------------------
- // Private Properties
- // -------------------------------------------------------------------------
- this.metadataStorage = getMetadataStorage();
- }
- // -------------------------------------------------------------------------
- // Public Methods
- // -------------------------------------------------------------------------
- ValidationExecutor.prototype.execute = function (object, targetSchema, validationErrors) {
- var _this = this;
- var _a, _b;
- /**
- * If there is no metadata registered it means possibly the dependencies are not flatterned and
- * more than one instance is used.
- *
- * TODO: This needs proper handling, forcing to use the same container or some other proper solution.
- */
- if (!this.metadataStorage.hasValidationMetaData && ((_a = this.validatorOptions) === null || _a === void 0 ? void 0 : _a.enableDebugMessages) === true) {
- console.warn("No validation metadata found. No validation will be performed. There are multiple possible reasons:\n" +
- " - There may be multiple class-validator versions installed. You will need to flatten your dependencies to fix the issue.\n" +
- " - This validation runs before any file with validation decorator was parsed by NodeJS.");
- }
- var groups = this.validatorOptions ? this.validatorOptions.groups : undefined;
- var strictGroups = (this.validatorOptions && this.validatorOptions.strictGroups) || false;
- var always = (this.validatorOptions && this.validatorOptions.always) || false;
- /** Forbid unknown values are turned on by default and any other value than false will enable it. */
- var forbidUnknownValues = ((_b = this.validatorOptions) === null || _b === void 0 ? void 0 : _b.forbidUnknownValues) === undefined || this.validatorOptions.forbidUnknownValues !== false;
- var targetMetadatas = this.metadataStorage.getTargetValidationMetadatas(object.constructor, targetSchema, always, strictGroups, groups);
- var groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas);
- if (this.validatorOptions && forbidUnknownValues && !targetMetadatas.length) {
- var validationError = new ValidationError();
- if (!this.validatorOptions ||
- !this.validatorOptions.validationError ||
- this.validatorOptions.validationError.target === undefined ||
- this.validatorOptions.validationError.target === true)
- validationError.target = object;
- validationError.value = undefined;
- validationError.property = undefined;
- validationError.children = [];
- validationError.constraints = { unknownValue: 'an unknown value was passed to the validate function' };
- validationErrors.push(validationError);
- return;
- }
- if (this.validatorOptions && this.validatorOptions.whitelist)
- this.whitelist(object, groupedMetadatas, validationErrors);
- // General validation
- Object.keys(groupedMetadatas).forEach(function (propertyName) {
- var value = object[propertyName];
- var definedMetadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type === ValidationTypes.IS_DEFINED; });
- var metadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type !== ValidationTypes.IS_DEFINED && metadata.type !== ValidationTypes.WHITELIST; });
- if (value instanceof Promise &&
- metadatas.find(function (metadata) { return metadata.type === ValidationTypes.PROMISE_VALIDATION; })) {
- _this.awaitingPromises.push(value.then(function (resolvedValue) {
- _this.performValidations(object, resolvedValue, propertyName, definedMetadatas, metadatas, validationErrors);
- }));
- }
- else {
- _this.performValidations(object, value, propertyName, definedMetadatas, metadatas, validationErrors);
- }
- });
- };
- ValidationExecutor.prototype.whitelist = function (object, groupedMetadatas, validationErrors) {
- var _this = this;
- var notAllowedProperties = [];
- Object.keys(object).forEach(function (propertyName) {
- // does this property have no metadata?
- if (!groupedMetadatas[propertyName] || groupedMetadatas[propertyName].length === 0)
- notAllowedProperties.push(propertyName);
- });
- if (notAllowedProperties.length > 0) {
- if (this.validatorOptions && this.validatorOptions.forbidNonWhitelisted) {
- // throw errors
- notAllowedProperties.forEach(function (property) {
- var _a;
- var validationError = _this.generateValidationError(object, object[property], property);
- validationError.constraints = (_a = {}, _a[ValidationTypes.WHITELIST] = "property ".concat(property, " should not exist"), _a);
- validationError.children = undefined;
- validationErrors.push(validationError);
- });
- }
- else {
- // strip non allowed properties
- notAllowedProperties.forEach(function (property) { return delete object[property]; });
- }
- }
- };
- ValidationExecutor.prototype.stripEmptyErrors = function (errors) {
- var _this = this;
- return errors.filter(function (error) {
- if (error.children) {
- error.children = _this.stripEmptyErrors(error.children);
- }
- if (Object.keys(error.constraints).length === 0) {
- if (error.children.length === 0) {
- return false;
- }
- else {
- delete error.constraints;
- }
- }
- return true;
- });
- };
- // -------------------------------------------------------------------------
- // Private Methods
- // -------------------------------------------------------------------------
- ValidationExecutor.prototype.performValidations = function (object, value, propertyName, definedMetadatas, metadatas, validationErrors) {
- var customValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CUSTOM_VALIDATION; });
- var nestedValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.NESTED_VALIDATION; });
- var conditionalValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CONDITIONAL_VALIDATION; });
- var validationError = this.generateValidationError(object, value, propertyName);
- validationErrors.push(validationError);
- var canValidate = this.conditionalValidations(object, value, conditionalValidationMetadatas);
- if (!canValidate) {
- return;
- }
- // handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not
- this.customValidations(object, value, definedMetadatas, validationError);
- this.mapContexts(object, value, definedMetadatas, validationError);
- if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) {
- return;
- }
- if (value === null && this.validatorOptions && this.validatorOptions.skipNullProperties === true) {
- return;
- }
- if ((value === null || value === undefined) &&
- this.validatorOptions &&
- this.validatorOptions.skipMissingProperties === true) {
- return;
- }
- this.customValidations(object, value, customValidationMetadatas, validationError);
- this.nestedValidations(value, nestedValidationMetadatas, validationError);
- this.mapContexts(object, value, metadatas, validationError);
- this.mapContexts(object, value, customValidationMetadatas, validationError);
- };
- ValidationExecutor.prototype.generateValidationError = function (object, value, propertyName) {
- var validationError = new ValidationError();
- if (!this.validatorOptions ||
- !this.validatorOptions.validationError ||
- this.validatorOptions.validationError.target === undefined ||
- this.validatorOptions.validationError.target === true)
- validationError.target = object;
- if (!this.validatorOptions ||
- !this.validatorOptions.validationError ||
- this.validatorOptions.validationError.value === undefined ||
- this.validatorOptions.validationError.value === true)
- validationError.value = value;
- validationError.property = propertyName;
- validationError.children = [];
- validationError.constraints = {};
- return validationError;
- };
- ValidationExecutor.prototype.conditionalValidations = function (object, value, metadatas) {
- return metadatas
- .map(function (metadata) { return metadata.constraints[0](object, value); })
- .reduce(function (resultA, resultB) { return resultA && resultB; }, true);
- };
- ValidationExecutor.prototype.customValidations = function (object, value, metadatas, error) {
- var _this = this;
- metadatas.forEach(function (metadata) {
- _this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls).forEach(function (customConstraintMetadata) {
- if (customConstraintMetadata.async && _this.ignoreAsyncValidations)
- return;
- if (_this.validatorOptions &&
- _this.validatorOptions.stopAtFirstError &&
- Object.keys(error.constraints || {}).length > 0)
- return;
- var validationArguments = {
- targetName: object.constructor ? object.constructor.name : undefined,
- property: metadata.propertyName,
- object: object,
- value: value,
- constraints: metadata.constraints,
- };
- if (!metadata.each || !(Array.isArray(value) || value instanceof Set || value instanceof Map)) {
- var validatedValue = customConstraintMetadata.instance.validate(value, validationArguments);
- if (isPromise(validatedValue)) {
- var promise = validatedValue.then(function (isValid) {
- if (!isValid) {
- var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
- error.constraints[type] = message;
- if (metadata.context) {
- if (!error.contexts) {
- error.contexts = {};
- }
- error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
- }
- }
- });
- _this.awaitingPromises.push(promise);
- }
- else {
- if (!validatedValue) {
- var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
- error.constraints[type] = message;
- }
- }
- return;
- }
- // convert set and map into array
- var arrayValue = convertToArray(value);
- // Validation needs to be applied to each array item
- var validatedSubValues = arrayValue.map(function (subValue) {
- return customConstraintMetadata.instance.validate(subValue, validationArguments);
- });
- var validationIsAsync = validatedSubValues.some(function (validatedSubValue) {
- return isPromise(validatedSubValue);
- });
- if (validationIsAsync) {
- // Wrap plain values (if any) in promises, so that all are async
- var asyncValidatedSubValues = validatedSubValues.map(function (validatedSubValue) {
- return isPromise(validatedSubValue) ? validatedSubValue : Promise.resolve(validatedSubValue);
- });
- var asyncValidationIsFinishedPromise = Promise.all(asyncValidatedSubValues).then(function (flatValidatedValues) {
- var validationResult = flatValidatedValues.every(function (isValid) { return isValid; });
- if (!validationResult) {
- var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
- error.constraints[type] = message;
- if (metadata.context) {
- if (!error.contexts) {
- error.contexts = {};
- }
- error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
- }
- }
- });
- _this.awaitingPromises.push(asyncValidationIsFinishedPromise);
- return;
- }
- var validationResult = validatedSubValues.every(function (isValid) { return isValid; });
- if (!validationResult) {
- var _b = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _b[0], message = _b[1];
- error.constraints[type] = message;
- }
- });
- });
- };
- ValidationExecutor.prototype.nestedValidations = function (value, metadatas, error) {
- var _this = this;
- if (value === void 0) {
- return;
- }
- metadatas.forEach(function (metadata) {
- if (metadata.type !== ValidationTypes.NESTED_VALIDATION && metadata.type !== ValidationTypes.PROMISE_VALIDATION) {
- return;
- }
- else if (_this.validatorOptions &&
- _this.validatorOptions.stopAtFirstError &&
- Object.keys(error.constraints || {}).length > 0) {
- return;
- }
- if (Array.isArray(value) || value instanceof Set || value instanceof Map) {
- // Treats Set as an array - as index of Set value is value itself and it is common case to have Object as value
- var arrayLikeValue = value instanceof Set ? Array.from(value) : value;
- arrayLikeValue.forEach(function (subValue, index) {
- _this.performValidations(value, subValue, index.toString(), [], metadatas, error.children);
- });
- }
- else if (value instanceof Object) {
- var targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name;
- _this.execute(value, targetSchema, error.children);
- }
- else {
- var _a = __read(_this.createValidationError(metadata.target, value, metadata), 2), type = _a[0], message = _a[1];
- error.constraints[type] = message;
- }
- });
- };
- ValidationExecutor.prototype.mapContexts = function (object, value, metadatas, error) {
- var _this = this;
- return metadatas.forEach(function (metadata) {
- if (metadata.context) {
- var customConstraint = void 0;
- if (metadata.type === ValidationTypes.CUSTOM_VALIDATION) {
- var customConstraints = _this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls);
- customConstraint = customConstraints[0];
- }
- var type = _this.getConstraintType(metadata, customConstraint);
- if (error.constraints[type]) {
- if (!error.contexts) {
- error.contexts = {};
- }
- error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
- }
- }
- });
- };
- ValidationExecutor.prototype.createValidationError = function (object, value, metadata, customValidatorMetadata) {
- var targetName = object.constructor ? object.constructor.name : undefined;
- var type = this.getConstraintType(metadata, customValidatorMetadata);
- var validationArguments = {
- targetName: targetName,
- property: metadata.propertyName,
- object: object,
- value: value,
- constraints: metadata.constraints,
- };
- var message = metadata.message || '';
- if (!metadata.message &&
- (!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages))) {
- if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) {
- message = customValidatorMetadata.instance.defaultMessage(validationArguments);
- }
- }
- var messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments);
- return [type, messageString];
- };
- ValidationExecutor.prototype.getConstraintType = function (metadata, customValidatorMetadata) {
- var type = customValidatorMetadata && customValidatorMetadata.name ? customValidatorMetadata.name : metadata.type;
- return type;
- };
- return ValidationExecutor;
- }());
- export { ValidationExecutor };
- //# sourceMappingURL=ValidationExecutor.js.map
|