From 0ef898e413c3bec2634bcaec93fa20f84c0d4c2e Mon Sep 17 00:00:00 2001 From: Frank Chiang Date: Mon, 28 Oct 2019 04:06:37 -0400 Subject: [PATCH] fix: pass context for isDefined and custom validators (#296) Close #292 --- src/validation/ValidationExecutor.ts | 10 ++++- test/functional/validation-options.spec.ts | 51 ++++++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/validation/ValidationExecutor.ts b/src/validation/ValidationExecutor.ts index 36e63d0f2f..52bf6d6efe 100644 --- a/src/validation/ValidationExecutor.ts +++ b/src/validation/ValidationExecutor.ts @@ -168,6 +168,7 @@ export class ValidationExecutor { // handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not this.defaultValidations(object, value, definedMetadatas, validationError.constraints); + this.mapContexts(object, value, definedMetadatas, validationError); if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) { return; @@ -186,6 +187,7 @@ export class ValidationExecutor { this.nestedValidations(value, nestedValidationMetadatas, validationError.children); this.mapContexts(object, value, metadatas, validationError); + this.mapContexts(object, value, customValidationMetadatas, validationError); } private generateValidationError(object: Object, value: any, propertyName: string) { @@ -376,7 +378,13 @@ export class ValidationExecutor { return metadatas .forEach(metadata => { if (metadata.context) { - const type = this.getConstraintType(metadata); + let customConstraint; + if (metadata.type === ValidationTypes.CUSTOM_VALIDATION) { + const customConstraints = this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls); + customConstraint = customConstraints[0]; + } + + const type = this.getConstraintType(metadata, customConstraint); if (error.constraints[type]) { if (!error.contexts) { diff --git a/test/functional/validation-options.spec.ts b/test/functional/validation-options.spec.ts index 7ef61206bd..d4c66ff498 100644 --- a/test/functional/validation-options.spec.ts +++ b/test/functional/validation-options.spec.ts @@ -1,7 +1,7 @@ import "es6-shim"; -import {Contains, Matches, MinLength, ValidateNested, ValidatorConstraint, Validate } from "../../src/decorator/decorators"; +import {Contains, IsDefined, Matches, MinLength, ValidateNested, ValidatorConstraint, Validate } from "../../src/decorator/decorators"; import {Validator} from "../../src/validation/Validator"; -import {ValidationError, ValidatorConstraintInterface} from "../../src"; +import {ValidationError, ValidatorConstraintInterface, ValidationOptions, registerDecorator, ValidationArguments} from "../../src"; import {should, use} from "chai"; @@ -937,6 +937,30 @@ describe("validation options", function() { describe("context", function() { it("should map context", function() { + function IsLongerThan(property: string, validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + name: "isLongerThan", + validator: { + validate(value: any, args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + if (relatedValue === undefined || relatedValue === null) + return true; + + return typeof value === "string" && + typeof relatedValue === "string" && + value.length > relatedValue.length; + } + } + }); + }; + } + class MyClass { @Contains("hello", { message: "String is not valid. You string must contain a hello word", @@ -953,14 +977,33 @@ describe("validation options", function() { } }) someOtherProperty: string; + + @IsDefined({ + context: { + foo: "bar" + } + }) + requiredProperty: string; + + @IsLongerThan("lastName", { + context: { baz: "qux" }, + message: "$property must be longer then $constraint1. Given value: $value" + }) + firstName: string; + + lastName: string; } const model = new MyClass(); - // model.someProperty = "hell no world"; + model.firstName = "Short"; + model.lastName = "LongerThanFirstName"; + return validator.validate(model).then(errors => { - errors.length.should.be.equal(2); + errors.length.should.be.equal(4); errors[0].contexts["contains"].should.be.eql({ hi: "there" }); errors[1].contexts["contains"].should.be.eql({ bye: "now" }); + errors[2].contexts["isDefined"].should.be.eql({ foo: "bar" }); + errors[3].contexts["isLongerThan"].should.be.eql({ baz: "qux" }); }); });