diff --git a/box.json b/box.json index e80bf25..5eab7d6 100644 --- a/box.json +++ b/box.json @@ -1,7 +1,7 @@ { "name":"ColdBox Validation", "author":"Ortus Solutions ", - "version":"4.3.1", + "version":"4.4.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/cbvalidation/@build.version@/cbvalidation-@build.version@.zip", "slug":"cbvalidation", "type":"modules", diff --git a/changelog.md b/changelog.md index 827a369..bfa47b2 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- requiredIf accepts a UDF and closure now + +### Fixed + +- UDF validator now treats nulls correctly + ## [4.3.1] - 2023-06-15 ### Fixed diff --git a/models/validators/MethodValidator.cfc b/models/validators/MethodValidator.cfc index 0e6b30e..7c2d990 100644 --- a/models/validators/MethodValidator.cfc +++ b/models/validators/MethodValidator.cfc @@ -38,11 +38,6 @@ component extends="BaseValidator" accessors="true" singleton { return true; } - // return true if no data to check, type needs a data element to be checked. - if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { - return true; - } - // Validate via method if ( invoke( diff --git a/models/validators/RequiredIfValidator.cfc b/models/validators/RequiredIfValidator.cfc index 62ff365..fa77e35 100644 --- a/models/validators/RequiredIfValidator.cfc +++ b/models/validators/RequiredIfValidator.cfc @@ -37,7 +37,8 @@ component any validationData, struct rules ){ - var isRequired = true; + var isRequired = true; + var errorMetadata = {}; // If you passed in simple data, simply check that the target field has a value if ( isSimpleValue( arguments.validationData ) && len( arguments.validationData ) ) { @@ -60,13 +61,24 @@ component .reduce( function( result, key, value ){ return ( arguments.value && arguments.result ); }, true ); + // If passed a UDF/closure + } else if ( + isCustomFunction( arguments.validationData ) || + isClosure( arguments.validationData ) + ) { + // Validate against the UDF/closure + var isRequired = arguments.validationData( + isNull( arguments.targetValue ) ? javacast( "null", "" ) : arguments.targetValue, + arguments.target, + errorMetadata + ); } else { validationResult.addError( validationResult.newError( message = "The target for RequiredIf must be a simple field name or a struct of field to target value pairs.", field = arguments.field, validationType = getName(), - rejectedValue = arguments.validationData, + rejectedValue = isSimpleValue( arguments.validationData ) ? arguments.validationData : "", validationData = arguments.validationData ) ); @@ -94,7 +106,9 @@ component validationData : arguments.validationData }; - validationResult.addError( validationResult.newError( argumentCollection = args ) ); + validationResult.addError( + validationResult.newError( argumentCollection = args ).setErrorMetadata( errorMetadata ) + ); return false; } diff --git a/models/validators/UDFValidator.cfc b/models/validators/UDFValidator.cfc index 14e6833..50b0619 100644 --- a/models/validators/UDFValidator.cfc +++ b/models/validators/UDFValidator.cfc @@ -34,9 +34,14 @@ component extends="BaseValidator" accessors="true" singleton { ){ var errorMetadata = {}; + // return true if no data to check, type needs a data element to be checked. + if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { + return true; + } + // Validate against the UDF/closure var passed = arguments.validationData( - isNull( arguments.targetValue ) ? javacast( "null", "" ) : arguments.targetValue, + arguments.targetValue, arguments.target, errorMetadata ); diff --git a/test-harness/tests/specs/ValidationIntegrations.cfc b/test-harness/tests/specs/ValidationIntegrations.cfc index efefbe0..bb4274f 100644 --- a/test-harness/tests/specs/ValidationIntegrations.cfc +++ b/test-harness/tests/specs/ValidationIntegrations.cfc @@ -159,7 +159,8 @@ component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" { params = { username : "luis", email : "lmajano@ortussolutions.com", - password : "luis" + password : "luis", + status : 4 // should not validate }, method = "post" ); diff --git a/test-harness/tests/specs/validators/RequiredIfValidatorTest.cfc b/test-harness/tests/specs/validators/RequiredIfValidatorTest.cfc index 501b998..b1ca2bb 100644 --- a/test-harness/tests/specs/validators/RequiredIfValidatorTest.cfc +++ b/test-harness/tests/specs/validators/RequiredIfValidatorTest.cfc @@ -106,17 +106,47 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod expect( model.validate( result, mock, "testField", "", "name" ) ).toBeFalse(); - // expect( - // model.validate( - // result, - // mock, - // "testField", - // "", - // "missing" - // ) - // ).toBeTrue(); + expect( model.validate( result, mock, "testField", "", "missing" ) ).toBeTrue(); + } ); + it( "can accept a closure as validationData", function(){ + var mock = createStub() + .$( "getName", "luis" ) + .$( "getRole", "admin" ) + .$( "getMissing", javacast( "null", "" ) ); + var result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); + + expect( model.validate( result, mock, "testField", "", isRequired1 ) ).toBeFalse(); + + expect( model.validate( result, mock, "testField", "", isRequired2 ) ).toBeTrue(); + } ); + it( "can use custom error metadata", function(){ + var mock = createStub() + .$( "getName", "luis" ) + .$( "getRole", "admin" ) + .$( "getMissing", javacast( "null", "" ) ); + var result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); + + expect( model.validate( result, mock, "testField", "", isRequired3 ) ).toBeFalse(); + + var errorMetadata = result.getErrors()[ 1 ].getErrorMetadata(); + + expect( errorMetaData ).toHaveKey( "customMessage" ); + expect( errorMetaData.customMessage ).toBe( "This is custom data" ); } ); } ); } + private function isRequired1( value, target, errorMetadata ){ + return true; + } + + private function isRequired2( value, target, errorMetadata ){ + return false; + } + + private function isRequired3( value, target, errorMetadata ){ + arguments.errorMetadata[ "customMessage" ] = "This is custom data"; + return true; + } + } diff --git a/test-harness/tests/specs/validators/UDFValidatorTest.cfc b/test-harness/tests/specs/validators/UDFValidatorTest.cfc index c9172f1..bf7ced5 100644 --- a/test-harness/tests/specs/validators/UDFValidatorTest.cfc +++ b/test-harness/tests/specs/validators/UDFValidatorTest.cfc @@ -34,7 +34,7 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod javacast( "null", "" ), variables.validate3 ); - assertEquals( false, r ); + assertEquals( true, r ); } private function validate( value, target ){