Skip to content

Commit 06cec7e

Browse files
committed
features : validateOrFail() new method to validate and if it fails it will throw a ValidationException. Also if the target is an object, the object is returned. If the target is a struct, the struct is returned ONLY with the validated fields.
1 parent 2ade059 commit 06cec7e

File tree

5 files changed

+304
-91
lines changed

5 files changed

+304
-91
lines changed

changelog.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
## 1.5.0
44

5-
* `feature` : `valiateModel()` is now deprecated in favor of `validate()`. `validateModel()` is now marked for deprecation.
5+
* `features` : `validateOrFail()` new method to validate and if it fails it will throw a `ValidationException`. Also if the target is an object, the object is returned. If the target is a struct, the struct is returned ONLY with the validated fields.
6+
* `feature` : `validateModel()` is now deprecated in favor of `validate()`. `validateModel()` is now marked for deprecation.
67
* `improvement` : Direct scoping for performance an avoidance of lookup bugs
78
* `improvement` : HTTPS protocol for everything
89
* `improvement` : Updated to testbox 3
9-
* `bug` : Fix mapping declaration for apidocs
10+
* `bug` : Fix mapping declaration for apidocs`
11+
* `bug` : Missing return on `addSharedConstraint()` function
1012

1113

1214
## 1.4.1

models/ValidationManager.cfc

Lines changed: 142 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,28 @@ import cbvalidation.models.result.*;
5656
component accessors="true" serialize="false" implements="IValidationManager" singleton{
5757

5858
/**
59-
* WireBox Object Factory
60-
*/
59+
* WireBox Object Factory
60+
*/
6161
property name="wirebox" inject="wirebox";
6262

6363
/**
64-
* A resource bundle plugin for i18n capabilities
65-
*/
64+
* A resource bundle plugin for i18n capabilities
65+
*/
6666
property name="resourceService" inject="ResourceService@cbi18n";
6767

6868
/**
69-
* Shared constraints that can be loaded into the validation manager
70-
*/
69+
* Shared constraints that can be loaded into the validation manager
70+
*/
7171
property name="sharedConstraints" type="struct";
7272

7373
this.id = createUUID();
7474

7575
/**
76-
* Constructor
77-
*
78-
* @sharedConstraints A structure of shared constraints
79-
*/
80-
ValidationManager function init( struct sharedConstraints=structNew() ){
76+
* Constructor
77+
*
78+
* @sharedConstraints A structure of shared constraints
79+
*/
80+
ValidationManager function init( struct sharedConstraints={} ){
8181
// valid validator registrations
8282
variables.validValidators = "required,type,size,range,regex,sameAs,sameAsNoCase,inList,discrete,udf,method,validator,min,max";
8383
// store shared constraints if passed
@@ -87,15 +87,17 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
8787
}
8888

8989
/**
90-
* Validate an object using constraints
91-
*
92-
* @target The target object to validate or a structure like a form or collection. If it is a collection, we will build a generic object for you so we can validate the structure of name-value pairs.
93-
* @fields One or more fields to validate on, by default it validates all fields in the constraints. This can be a simple list or an array.
94-
* @constraints An optional shared constraints name or an actual structure of constraints to validate on.
95-
* @locale An optional locale to use for i18n messages
96-
* @excludeFields An optional list of fields to exclude from the validation.
97-
* @IncludeFields An optional list of fields to include in the validation.
98-
*/
90+
* Validate an object using constraints
91+
*
92+
* @target The target object to validate or a structure like a form or collection. If it is a collection, we will build a generic object for you so we can validate the structure of name-value pairs.
93+
* @fields One or more fields to validate on, by default it validates all fields in the constraints. This can be a simple list or an array.
94+
* @constraints An optional shared constraints name or an actual structure of constraints to validate on.
95+
* @locale An optional locale to use for i18n messages
96+
* @excludeFields An optional list of fields to exclude from the validation.
97+
* @IncludeFields An optional list of fields to include in the validation.
98+
*
99+
* @return Results object
100+
*/
99101
IValidationResult function validate(
100102
required any target,
101103
string fields="*",
@@ -122,13 +124,15 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
122124
var allConstraints = determineConstraintsDefinition( arguments.target, arguments.constraints );
123125

124126
// create new result object
125-
var initArgs = {
126-
locale = arguments.locale,
127-
targetName = targetName,
128-
resourceService = resourceService,
129-
constraints = allConstraints
130-
};
131-
var results = wirebox.getInstance( name="cbvalidation.models.result.ValidationResult", initArguments=initArgs );
127+
var results = wirebox.getInstance(
128+
name = "cbvalidation.models.result.ValidationResult",
129+
initArguments = {
130+
locale : arguments.locale,
131+
targetName : targetName,
132+
resourceService : resourceService,
133+
constraints : allConstraints
134+
}
135+
);
132136

133137
// iterate over constraints defined
134138
for( var thisField in allConstraints ){
@@ -144,7 +148,13 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
144148
// verify we can validate the field described in the constraint
145149
if( arguments.fields == "*" || listFindNoCase( arguments.fields, thisField ) ) {
146150
// process the validation rules on the target field using the constraint validation data
147-
processRules( results=results, rules=allConstraints[ thisField ], target=arguments.target, field=thisField, locale=arguments.locale );
151+
processRules(
152+
results = results,
153+
rules = allConstraints[ thisField ],
154+
target = arguments.target,
155+
field = thisField,
156+
locale = arguments.locale
157+
);
148158
}
149159
}
150160

@@ -154,13 +164,56 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
154164
}
155165

156166
/**
157-
* Process validation rules on a target object and field
158-
*
159-
* @results The validation result object
160-
* @rules The structure containing validation rules
161-
* @target The target object to do validation on
162-
* @field The field to validate
163-
*/
167+
* Validate an object using constraints and throw a `ValidationException` if the validation fails
168+
169+
* @target The target object to validate or a structure like a form or collection. If it is a collection, we will build a generic object for you so we can validate the structure of name-value pairs.
170+
* @fields One or more fields to validate on, by default it validates all fields in the constraints. This can be a simple list or an array.
171+
* @constraints An optional shared constraints name or an actual structure of constraints to validate on.
172+
* @locale An optional locale to use for i18n messages
173+
* @excludeFields An optional list of fields to exclude from the validation.
174+
* @IncludeFields An optional list of fields to include in the validation.
175+
*
176+
* @throws ValidationException
177+
* @return any,struct: The target object that was validated, or the structure fields that where validated.
178+
*/
179+
function validateOrFail(
180+
required any target,
181+
string fields="*",
182+
any constraints="",
183+
string locale="",
184+
string excludeFields="",
185+
string includeFields=""
186+
){
187+
var vResults = this.validate( argumentCollection=arguments );
188+
189+
// Verify errors
190+
if( vResults.hasErrors() ){
191+
throw(
192+
type = "ValidationException",
193+
message = "The target failed to pass validation",
194+
extendedInfo = vResults.getAllErrorsAsJson()
195+
);
196+
}
197+
198+
// If object, return it
199+
if( isObject( arguments.target ) ){
200+
return arguments.target;
201+
}
202+
203+
// Return validated keys
204+
return arguments.target.filter( function( key ) {
205+
return constraints.keyExists( key );
206+
} );
207+
}
208+
209+
/**
210+
* Process validation rules on a target object and field
211+
*
212+
* @results The validation result object
213+
* @rules The structure containing validation rules
214+
* @target The target object to do validation on
215+
* @field The field to validate
216+
*/
164217
ValidationManager function processRules(
165218
required cbvalidation.models.result.IValidationResult results,
166219
required struct rules,
@@ -187,31 +240,33 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
187240
}
188241

189242
/**
190-
* Create validators according to types and validation data
191-
*
192-
* @validatorType The type of validator to retrieve, either internal or class path or wirebox ID
193-
* @validationData The validation data that is used for custom validators
194-
*
195-
* @throws ValidationManager.InvalidValidatorType
196-
*/
243+
* Create validators according to types and validation data
244+
*
245+
* @validatorType The type of validator to retrieve, either internal or class path or wirebox ID
246+
* @validationData The validation data that is used for custom validators
247+
*
248+
* @throws ValidationManager.InvalidValidatorType
249+
*/
197250
cbvalidation.models.validators.IValidator function getValidator(
198251
required string validatorType,
199252
required any validationData
200253
){
254+
var cfcPrefix = "cbvalidation.models.validators";
255+
201256
switch( arguments.validatorType ){
202-
case "required" : { return wirebox.getInstance( "cbvalidation.models.validators.RequiredValidator" ); }
203-
case "type" : { return wirebox.getInstance( "cbvalidation.models.validators.TypeValidator" ); }
204-
case "size" : { return wirebox.getInstance( "cbvalidation.models.validators.SizeValidator" ); }
205-
case "range" : { return wirebox.getInstance( "cbvalidation.models.validators.RangeValidator" ); }
206-
case "regex" : { return wirebox.getInstance( "cbvalidation.models.validators.RegexValidator" ); }
207-
case "sameAs" : { return wirebox.getInstance( "cbvalidation.models.validators.SameAsValidator" ); }
208-
case "sameAsNoCase" : { return wirebox.getInstance( "cbvalidation.models.validators.SameAsNoCaseValidator" ); }
209-
case "inList" : { return wirebox.getInstance( "cbvalidation.models.validators.InListValidator" ); }
210-
case "discrete" : { return wirebox.getInstance( "cbvalidation.models.validators.DiscreteValidator" ); }
211-
case "min" : { return wirebox.getInstance( "cbvalidation.models.validators.MinValidator" ); }
212-
case "max" : { return wirebox.getInstance( "cbvalidation.models.validators.MaxValidator" ); }
213-
case "udf" : { return wirebox.getInstance( "cbvalidation.models.validators.UDFValidator" ); }
214-
case "method" : { return wirebox.getInstance( "cbvalidation.models.validators.MethodValidator" ); }
257+
case "required" : { return wirebox.getInstance( "#cfcPrefix#.RequiredValidator" ); }
258+
case "type" : { return wirebox.getInstance( "#cfcPrefix#.TypeValidator" ); }
259+
case "size" : { return wirebox.getInstance( "#cfcPrefix#.SizeValidator" ); }
260+
case "range" : { return wirebox.getInstance( "#cfcPrefix#.RangeValidator" ); }
261+
case "regex" : { return wirebox.getInstance( "#cfcPrefix#.RegexValidator" ); }
262+
case "sameAs" : { return wirebox.getInstance( "#cfcPrefix#.SameAsValidator" ); }
263+
case "sameAsNoCase" : { return wirebox.getInstance( "#cfcPrefix#.SameAsNoCaseValidator" ); }
264+
case "inList" : { return wirebox.getInstance( "#cfcPrefix#.InListValidator" ); }
265+
case "discrete" : { return wirebox.getInstance( "#cfcPrefix#.DiscreteValidator" ); }
266+
case "min" : { return wirebox.getInstance( "#cfcPrefix#.MinValidator" ); }
267+
case "max" : { return wirebox.getInstance( "#cfcPrefix#.MaxValidator" ); }
268+
case "udf" : { return wirebox.getInstance( "#cfcPrefix#.UDFValidator" ); }
269+
case "method" : { return wirebox.getInstance( "#cfcPrefix#.MethodValidator" ); }
215270
case "validator" : {
216271
if( find( ":", arguments.validationData ) ){
217272
return wirebox.getInstance( getToken( arguments.validationData, 2, ":" ) );
@@ -231,54 +286,55 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
231286
}
232287

233288
/**
234-
* Retrieve the shared constraints, all of them or by name
235-
*
236-
* @name Filter by name or not
237-
*/
289+
* Retrieve the shared constraints, all of them or by name
290+
*
291+
* @name Filter by name or not
292+
*/
238293
struct function getSharedConstraints( string name ){
239294
return ( structKeyExists( arguments, "name" ) ? variables.sharedConstraints[ arguments.name ] : variables.sharedConstraints );
240295
}
241296

242297
/**
243-
* Check if a shared constraint exists by name
244-
*
245-
* @name The shared constraint to check
246-
*/
298+
* Check if a shared constraint exists by name
299+
*
300+
* @name The shared constraint to check
301+
*/
247302
boolean function sharedConstraintsExists( required string name ){
248303
return structKeyExists( variables.sharedConstraints, arguments.name );
249304
}
250305

251306

252307
/**
253-
* Set the entire shared constraints structure
254-
*
255-
* @constraints Filter by name or not
256-
*/
308+
* Set the entire shared constraints structure
309+
*
310+
* @constraints Filter by name or not
311+
*/
257312
IValidationManager function setSharedConstraints( struct constraints ){
258313
variables.sharedConstraints = arguments.constraints;
259314
return this;
260315
}
261316

262317
/**
263-
* Store a shared constraint
264-
*
265-
* @name Filter by name or not
266-
* @constraint The constraint to store.
267-
*/
318+
* Store a shared constraint
319+
*
320+
* @name The name to store the constraint as
321+
* @constraint The constraint structures to store.
322+
*/
268323
IValidationManager function addSharedConstraint( required string name, required struct constraint ){
269324
variables.sharedConstraints[ arguments.name ] = arguments.constraints;
325+
return this;
270326
}
271327

272-
/************************************** private *********************************************/
328+
/************************************** PRIVATE *********************************************/
273329

274330
/**
275-
* Determine from where to take the constraints from
276-
*
277-
* @target The target object
278-
* @constraints The constraints rules
279-
*
280-
* @throws ValidationManager.InvalidSharedConstraint
281-
*/
331+
* Determine from where to take the constraints from
332+
*
333+
* @target The target object
334+
* @constraints The constraints rules
335+
*
336+
* @throws ValidationManager.InvalidSharedConstraint
337+
*/
282338
private struct function determineConstraintsDefinition( required any target, any constraints="" ){
283339
var thisConstraints = {};
284340

@@ -290,7 +346,7 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
290346
if( !sharedConstraintsExists( arguments.constraints ) ){
291347
throw(
292348
message = "The shared constraint you requested (#arguments.constraints#) does not exist",
293-
detail = "Valid constraints are: #structKeyList(sharedConstraints)#",
349+
detail = "Valid constraints are: #structKeyList( sharedConstraints )#",
294350
type = "ValidationManager.InvalidSharedConstraint"
295351
);
296352
}
@@ -303,15 +359,12 @@ component accessors="true" serialize="false" implements="IValidationManager" sin
303359
}
304360

305361
/**
306-
* Get the constraints structure from target objects, if none, it returns an empty structure
307-
*
308-
* @target The target object
309-
*/
362+
* Get the constraints structure from target objects, if none, it returns an empty structure
363+
*
364+
* @target The target object
365+
*/
310366
private struct function discoverConstraints( required any target ){
311-
if( structKeyExists(arguments.target,"constraints") ){
312-
return arguments.target.constraints;
313-
}
314-
return {};
367+
return ( structKeyExists( arguments.target, "constraints" ) ? arguments.target.constraints : {} );
315368
}
316369

317370
}

test-harness/handlers/Main.cfc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,37 @@ component{
3939
}
4040
}
4141

42+
43+
/**
44+
* validateOrFailWithKeys
45+
*/
46+
function validateOrFailWithKeys( event, rc, prc ){
47+
48+
var constraints = {
49+
username = {required=true, size="2..20"},
50+
password = {required=true, size="2..20"}
51+
};
52+
53+
// validate
54+
prc.keys = validateOrFail( target=rc, constraints=constraints );
55+
56+
return prc.keys;
57+
}
58+
59+
/**
60+
* validateOrFailWithObject
61+
*/
62+
function validateOrFailWithObject( event, rc, prc ){
63+
64+
var oModel = populateModel( "User" );
65+
66+
// validate
67+
prc.object = validateOrFail( oModel );
68+
69+
return "Validated";
70+
}
71+
72+
4273
// Run on first init
4374
any function onAppInit( event, rc, prc ){
4475

0 commit comments

Comments
 (0)