Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
lmajano committed Feb 4, 2020
2 parents e3481c6 + 00b9ce9 commit 0a16993
Show file tree
Hide file tree
Showing 22 changed files with 569 additions and 223 deletions.
10 changes: 5 additions & 5 deletions box.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name":"ColdBox Validation",
"author":"Ortus Solutions <[email protected]>",
"version":"2.0.0",
"version":"2.1.0",
"location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/cbvalidation/@build.version@/[email protected]@.zip",
"slug":"cbvalidation",
"type":"modules",
Expand Down Expand Up @@ -31,9 +31,9 @@
".git*"
],
"scripts":{
"toMaster":"recipe build/toMaster.boxr",
"format":"cfformat run models/**/*.cfc,ModuleConfig.cfc,tests/specs/**/*.cfc,*.cfc --overwrite",
"format:check":"cfformat run models/**/*.cfc,ModuleConfig.cfc,tests/specs/**/*.cfc,*.cfc --check",
"lint":"cflint models/**.cfc --text --html --json --!exitOnError --suppress"
"toMaster":"recipe build/toMaster.boxr",
"format":"cfformat run models/**/*.cfc,ModuleConfig.cfc,tests/specs/**/*.cfc,*.cfc --overwrite",
"format:check":"cfformat run models/**/*.cfc,ModuleConfig.cfc,tests/specs/**/*.cfc,*.cfc --check",
"lint":"cflint models/**.cfc --text --html --json --!exitOnError --suppress"
}
}
11 changes: 9 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# CHANGELOG

## 2.1.0

* `feature` : Added `constraintProfiles` to allow you to define which fields to validate according to defined profiles: https://github.com/coldbox-modules/cbvalidation/issues/37
* `feature` : Updated `RequiredUnless` and `RequiredIf` to use struct literal notation instead of the weird parsing we did.
* `feature` : Added the `Unique` validator thanks to @elpete!
* `improvement` : Added `null` support for the `RequiredIf,RequiredUnless` validator values

## 2.0.0

### Features

* No more manual discovery of validators, automated registration and lookup process, cleaned lots of code on this one!
* New Validator: `Accepted` - The field under validation must be yes, on, 1, or true. This is useful for validating "Terms of Service" acceptance.
* New Validator: `Alpha` - Only allows alphabetic characters
* New Validator: `RequiredUnless` with validation data: `anotherField:value,...` - The field under validation must be present and not empty unless the `anotherfield` field is equal to the passed `value`.
* New Validator: `RequiredIf` with validation data: `anotherField:value,...` - The field under validation must be present and not empty if the `anotherfield` field is equal to the passed `value`.
* New Validator: `RequiredUnless` with validation data as a struct literal `{ anotherField:value, ... }` - The field under validation must be present and not empty unless the `anotherfield` field is equal to the passed `value`.
* New Validator: `RequiredIf` with validation data as a struct literal `{ anotherField:value, ... }` - The field under validation must be present and not empty if the `anotherfield` field is equal to the passed `value`.
* Accelerated validation by removing type checks. ACF chokes on interface checks

### Improvements
Expand Down
2 changes: 2 additions & 0 deletions models/Mixins.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @locale The i18n locale to use for validation messages
* @excludeFields The fields to exclude from the validation
* @includeFields The fields to include in the validation
* @profiles If passed, a list of profile names to use for validation constraints
*
* @return cbvalidation.model.result.IValidationResult
*/
Expand All @@ -26,6 +27,7 @@ function validate(){
* @locale The i18n locale to use for validation messages
* @excludeFields The fields to exclude from the validation
* @includeFields The fields to include in the validation
* @profiles If passed, a list of profile names to use for validation constraints
*
* @return The validated object or the structure fields that where validated
* @throws ValidationException
Expand Down
38 changes: 35 additions & 3 deletions models/ValidationManager.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ component accessors="true" serialize="false" singleton {
* @locale An optional locale to use for i18n messages
* @excludeFields An optional list of fields to exclude from the validation.
* @IncludeFields An optional list of fields to include in the validation.
* @profiles If passed, a list of profile names to use for validation constraints
*
* @return IValidationResult
*/
Expand All @@ -126,7 +127,8 @@ component accessors="true" serialize="false" singleton {
any constraints = "",
string locale = "",
string excludeFields = "",
string includeFields = ""
string includeFields = "",
string profiles = ""
){
var targetName = "";

Expand All @@ -152,10 +154,38 @@ component accessors="true" serialize="false" singleton {
locale : arguments.locale,
targetName : targetName,
resourceService : resourceService,
constraints : allConstraints
constraints : allConstraints,
profiles : arguments.profiles
}
);

// Discover profiles, and update the includeFields list from it
if( len( arguments.profiles ) ){
arguments.includeFields = arguments.profiles
.listToArray()
// Check if profiles defined in target and iterated one exists
.filter( function( profileKey ){
return structKeyExists( target, "constraintProfiles" ) && structKeyExists( target.constraintProfiles, profileKey );
} )
// Incorporate fields from each profile
.map( function( profileKey ){
// iterate all declared profile fields and incorporate into the includeFields
return target.constraintProfiles
.find( arguments.profileKey )
.listToArray();
} )
// Reduce all fields into a single hashset to do a distinct collection
.reduce( function( result, item ){
item
.each( function( thisField ){
result.add( thisField );
} );
return result;
}, createObject( "java", "java.util.HashSet" ) )
.toArray();
arguments.includeFields = arrayToList( arguments.includeFields );
}

// iterate over constraints defined
for ( var thisField in allConstraints ) {
var validateField = true;
Expand Down Expand Up @@ -193,6 +223,7 @@ component accessors="true" serialize="false" singleton {
* @locale An optional locale to use for i18n messages
* @excludeFields An optional list of fields to exclude from the validation.
* @IncludeFields An optional list of fields to include in the validation.
* @profiles If passed, a list of profile names to use for validation constraints
*
* @throws ValidationException
* @return any,struct: The target object that was validated, or the structure fields that where validated.
Expand All @@ -203,7 +234,8 @@ component accessors="true" serialize="false" singleton {
any constraints = "",
string locale = "",
string excludeFields = "",
string includeFields = ""
string includeFields = "",
string profiles = ""
){
var vResults = this.validate( argumentCollection = arguments );

Expand Down
37 changes: 22 additions & 15 deletions models/result/ValidationResult.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,49 @@
component accessors="true" {

/**
* A collection of error objects represented in this result object
*/
* A collection of error objects represented in this result object
*/
property name="errors" type="array";

/**
* Extra metadata you can store in the results object
*/
* Extra metadata you can store in the results object
*/
property name="resultMetadata" type="struct";

/**
* The locale this result validation is using
*/
* The locale this result validation is using
*/
property name="locale" type="string";

/**
* The name of the target object
*/
* The name of the target object
*/
property name="targetName" type="string";

/**
* The constraints evaluated in the validation process
*/
* The constraints evaluated in the validation process
*/
property name="constraints" type="struct";

/**
* The resource bundle object
*/
* The resource bundle object
*/
property name="resourceService";

/**
* Constructor
*/
* The profiles used in the validation
*/
property name="profiles" type="string";

/**
* Constructor
*/
ValidationResult function init(
string locale = "",
string targetName = "",
any resourceService = "",
struct constraints = structNew()
struct constraints = structNew(),
string profiles = ""
){
variables.errors = [];
variables.resultMetadata = {};
Expand All @@ -53,6 +59,7 @@ component accessors="true" {
variables.targetName = arguments.targetName;
variables.resourceService = arguments.resourceService;
variables.constraints = arguments.constraints;
variables.profiles = arguments.profiles;
return this;
}

Expand Down
11 changes: 6 additions & 5 deletions models/validators/AcceptedValidator.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ component accessors="true" singleton {

/**
* Will check if an incoming value validates
* @validationResultThe result object of the validation
* @targetThe target object to validate on
* @fieldThe field on the target object to validate on
* @targetValueThe target value to validate
* @validationDataThe validation data the validator was created with
*
* @validationResult The result object of the validation
* @target The target object to validate on
* @field The field on the target object to validate on
* @targetValue The target value to validate
* @validationData The validation data the validator was created with
*/
boolean function validate(
required any validationResult,
Expand Down
41 changes: 24 additions & 17 deletions models/validators/RequiredIfValidator.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Copyright since 2020 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* This validator checks if a field has value and not null
* This validator checks a struct of key-value pairs passed in the validation data.
* If those key-value pairs are equal then the target field will be required
*/
component accessors="true" extends="RequiredValidator" singleton {

Expand All @@ -18,11 +19,12 @@ component accessors="true" extends="RequiredValidator" singleton {

/**
* Will check if an incoming value validates
* @validationResultThe result object of the validation
* @targetThe target object to validate on
* @fieldThe field on the target object to validate on
* @targetValueThe target value to validate
* @validationDataThe validation data the validator was created with
*
* @validationResult The result object of the validation
* @target The target object to validate on
* @field The field on the target object to validate on
* @targetValue The target value to validate
* @validationData The validation data the validator was created with
*/
boolean function validate(
required any validationResult,
Expand All @@ -31,24 +33,29 @@ component accessors="true" extends="RequiredValidator" singleton {
any targetValue,
any validationData
){
// Validation Data Format: property:value,...
var validationArray = arguments.validationData.listToArray();
// If you passed in simple data, conver it to a struct, simple values are not evaluated
if( isSimpleValue( arguments.validationData ) ){
arguments.validationData = {};
}

// Inflate to array to test multiple properties
var isRequired = validationArray
.map( function( item ){
var isRequired = arguments.validationData
.map( function( key, value ){
// Get comparison values
var compareProperty = getToken( arguments.item, 1, ":" );
var compareValue = getToken( arguments.item, 2, ":" );
var comparePropertyValue = invoke( target, "get#compareProperty#" );
var comparePropertyValue = invoke( target, "get#key#" );
// Null checks
if( isNull( comparePropertyValue ) ){
return isNull( arguments.value );
}
// Check if the compareValue is the same as the defined one
return ( compareValue == comparePropertyValue ? true : false );
return ( arguments.value == comparePropertyValue ? true : false );
} )
// AND them all for a single result
.reduce( function( result, item ){
return arguments.result && arguments.item;
.reduce( function( result, key, value ){
return ( arguments.value && arguments.result );
}, true );

if( !validationArray.len() || !isRequired ){
if( !arguments.validationData.count() || !isRequired ){
return true;
}

Expand Down
44 changes: 26 additions & 18 deletions models/validators/RequiredUnlessValidator.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Copyright since 2020 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* This validator checks if a field has value and not null
* This validator checks a struct of key-value pairs passed in the validation data.
* If those key-value pairs are equal then the target field will NOT be required
*/
component accessors="true" extends="RequiredValidator" singleton {

Expand All @@ -18,11 +19,12 @@ component accessors="true" extends="RequiredValidator" singleton {

/**
* Will check if an incoming value validates
* @validationResultThe result object of the validation
* @targetThe target object to validate on
* @fieldThe field on the target object to validate on
* @targetValueThe target value to validate
* @validationDataThe validation data the validator was created with
*
* @validationResult The result object of the validation
* @target The target object to validate on
* @field The field on the target object to validate on
* @targetValue The target value to validate
* @validationData The validation data the validator was created with
*/
boolean function validate(
required any validationResult,
Expand All @@ -31,24 +33,30 @@ component accessors="true" extends="RequiredValidator" singleton {
any targetValue,
any validationData
){
// Validation Data Format: property:value,...
var validationArray = arguments.validationData.listToArray();
// Inflate to array to test multiple properties
var isOptional = validationArray
.map( function( item ){
// If you passed in simple data, conver it to a struct, simple values are not evaluated
if( isSimpleValue( arguments.validationData ) ){
arguments.validationData = {};
}

// Test the data
var isOptional = arguments.validationData
.map( function( key, value ){
// Get comparison values
var compareProperty = getToken( arguments.item, 1, ":" );
var compareValue = getToken( arguments.item, 2, ":" );
var comparePropertyValue = invoke( target, "get#compareProperty#" );
var comparePropertyValue = invoke( target, "get#key#" );
// Null checks
if( isNull( comparePropertyValue ) ){
return isNull( arguments.value );
}
// Check if the compareValue is the same as the defined one
return ( compareValue == comparePropertyValue ? true : false );
return ( arguments.value == comparePropertyValue ? true : false );
} )
// AND them all for a single result
.reduce( function( result, item ){
return ( arguments.item && arguments.result );
.reduce( function( result, key, value ){
return ( arguments.value && arguments.result );
}, true );

if( validationArray.len() && isOptional ){
// If we have data, then test the optional
if( arguments.validationData.count() && isOptional ){
return true;
}

Expand Down
14 changes: 7 additions & 7 deletions models/validators/UDFValidator.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ component accessors="true" singleton {
any targetValue,
any validationData
){
// return true if no data to check, type needs a data element to be checked.
if ( isNull( arguments.targetValue ) || ( isSimpleValue( arguments.targetValue ) && !len( arguments.targetValue ) ) ) {
return true;
}
// Validate against the UDF/closure
var passed = arguments.validationData(
isNull( arguments.targetValue ) ? javacast( "null", "" ) : arguments.targetValue,
arguments.target
);

// Validate against the UDF/closure
if ( arguments.validationData( arguments.targetValue, arguments.target ) ) {
if ( passed ) {
return true;
}

var args = {
message : "The '#arguments.field#' value does not validate",
field : arguments.field,
validationType : getName(),
rejectedValue : ( isSimpleValue( arguments.targetValue ) ? arguments.targetValue : "" ),
rejectedValue : !isNull( arguments.targetValue ) && isSimpleValue( arguments.targetValue ) ? arguments.targetValue : "",
validationData : arguments.validationData
};

Expand Down
Loading

0 comments on commit 0a16993

Please sign in to comment.