MotoValidation 2.0

V2.1 is out!

It’s been a while since I’ve posted something on my blog but it’s also been a while since I was working on something that I felt worth sharing.

I’m proud to present motoValidation v2 a big improvement to my original creation.  The goal of this script is to limit the amount of code that a developer must write in order to accomplish common validation use cases in IBM BPM platform.  This script is currently targeted for Heritage coaches only… I’m not sure if I like client side coaches yet… I’m sure they’ll grow on me but they need some serious stuff before I’ll consider them usable.

With motoValidation we make some assumptions:

  • The most common type of validation is a required field
  • The target for your validation is the same as the binding
  • We should not make developers provide redundant information over and over (if I have to type tw.whatever.thing.thing.thing one more time I’m gonna snap…)
  • Validation should require as little code as possible

Get it from Git

On to the documentation:

Installation

First off you’ll need to install the file as a server file in your Process Application.

serverfile

Basic Use

Now for the fun part.

Hook up a validation script for your heritage coach the way you normally would.  Then start by identifying your base object to the engine for example:

motoValidation.setBaseObjet({name:"tw.local.reqeustObject.dataObject", vlaue: tw.local.reqeustObject.dataObject});

You’ll notice that if you hover over motoValidation BPM will complain that it’s an unknown function.  This happens becasue when server files are added BPM doesn’t update Process Designers known function list.  Don’t worry about the problems BPM mentions the JavaScript here is valid.  You also notice that it’s going to complain about the standard JavaScript object were defining in the method parameter.  Again don’t worry PD’s syntax highlighter isn’t as smart as it could be and this is in fact valid JavaScript.  This will also break BPMs ability to do auto-complete. I apologize  but I won’t take responsibility for a syntax engine that should know when someone is writing valid JavaScript.

By setting our base object we are establishing a reference point for future calls to the script.  this allows us to write less code do do our validations in the future.

Now that we’ve established the base object let’s validate some fields.  We’ll start with a simple required field:

motoValidation.addFieldValidation("customerName");

In this example we are requiring the customerName value be filled out.  This code would be the equivalent of writing:

if(typeof tw.local.reqeustObject.dataObject.customerName == "undefined" || !tw.local.reqeustObject.dataObject.customerName){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.customerName", "Field is required");
}else{
  if(tw.local.reqeustObject.dataObject.customerName.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.customerName", "Field is required");
  }
}

So two simple lines of code to do 6 lines of well too much nonsense… I’ll admit it I’m lazy especially when it comes to writing redundant code and if I can find a way to slim it down I WILL.

Add Field Validation

The “addFieldValidation” method can do much more than take a simple field name ohhh yes you can do just about ANYTHING (within reason anyway).

The method accepts 4 parameters:

  • fieldName – string name of the field you want to validate
  • subObject  – default = []; an array used to go deeper into your complex object with out changing the baseObject
    • current validationType options:
      • required-string
      • email
      • phone
      • usPhone
      • boolean-true
      • bank-routing
  • validationType  – default = “required-string”; sets the type of validation to perform on the field
  • message – defaults to the standard message for validation type but can be set to a custom message
  • custom – a custom validation function

Lets try out some of the parameters to see it in action:

Lets say we have another complex object inside our base object and you want to validate one of the email addresses in it. For example we’ll say we have an email in tw.local.reqeustObject.dataobject.customerRecord.customerEmail.

motoValidation.addFieldValidation("customerEmail",["customerRecord"], "email");

This same concept could apply to an object with in an object with in an object…. (like inception). For example tw.local.reqeustObject.dataObject.firstLevel.secondLevel.thridLevel.alternateEmail.

motoValidation.addFieldValidation("alternateEmail",["firstLevel","sendondLevel","thridLevel"],"email");

The reason this option exists is so that you don’t need to change your base object for one off validation cases.  If you plan to do a lot of validation on a different object it would be much easier for you to simple change the base object as explained above.

You’ll also notice above that we specified the type of validation we wanted to do on the field in this vase “email” validation.  This will use a standard regex to ensure that the strings format matches that of an email address.

Add Object Validation

Often times you have multiple fields you’ll want to do similar validation on, inside of one object.  Or you’ll want to validate the same field in a list multiple times.  This is easily accomplished with the “addObjectValidation” method.

This method has 5 parameters:

  • objectName – like field name but this expects a complex object or list
  • values – and array or list of strings representing the names of the fields inside of the object you wish to perform the validation on
  • type – default = “object”; this must be either ‘object’ or ‘list’
  • validationType – default = “required-string”; see list above for options
  • message – defaults to the default message for the type of validation indicated
  • custom – a custom validation function

For example let’s say we wanted to validate our cutomerFirstName, customerLastName and customerNumber fields were all filled out inside of our tw.local.requestObject.dataobject.customer complex object:

motoValidation.addObjectValidation("customer", ["customerFirstName","customerLastName","customerNumber"]);

This would perform a required string check on all three fields inside the object.

Now let’s say we have an object that is a complex objects list and we want to make sure that each contactPhone in that list is a valid us phone number.  For example tw.local.reqeustObject.dataObject.contacts[i].contactPhone:

motoValidation.addObjectValidation("contacts",["contactPhone"], "list", "usPhone", "Valid US Phone Number Required");

Custom Validation

If you’re feeling adventurous you can opt to write your own validation function.  This is done by setting the validation type to “custom” for both the addFieldValidation and addObjectValidation methods.  The required format for a custom function is as follows:

  • Must accept 1 parameter.  It will be the value of the field you have targeted.
  • Must return true if your conditions are met and false if it fails validation.

For example let just say you want some non-OTB (out of the box) functionality like comparing a date to see if it’s within 5 days of today:

motoValidation.addFieldValidation("dateRequested", [], "custom", "Date of request must be within 5 days.", function(value){
  var today = new Date();
  if(Date.parse(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 5) < value ){
    return true;
  }else{
    return false;
  }
});

This provides you the opportunity to supply custom validation to the script with the goal of saving you time.  If you find that your regularly using a function please do a pull request and I’ll gladly add it to the project if I can see the value.

Execution

Once you’ve identified all the fields you want to validate you simply need to call:

motoValidation.validate();

This will tell the script that you’ve got all your validations loaded and your ready to go.

Comparison

Let’s compare a fairly complex validation requirement with what an equivalent validation would look like if done without motoValidation.

With motoValidate

motoValidation.setBaseObject({name:"tw.local.requestObject", value:tw.local.requestObject});
motoValidation.addFieldValidation("termsAgreed", [], "boolean-true", "You must agree to our terms and conditions.");
motoValidation.addFieldValidation("dateofReqeust");
motoValidation.addFieldValidation("email", ["customer"], "email", "Valid Email Required");
motoValidation.addObjectValidation("customer",["name","age","height","weight"]);
motoValidation.addObjectValidation("addresses", ["address1","city","state","zip"], "list");
motoValidation.setBaseObject({name:"tw.local.reqeustObject.dataObject",value:tw.local.reqeustObject.bankInfo});
motoValidation.addFieldValidation("routingNumber", [], "bank-rounting");
motoValidation.addFieldValidation("accountNumber");
motoValidation.addFieldValidation("email", [], "email");
motoValidation.validate();

Without motoValidate

if(!tw.local.requestObject.termsAgreed){
	tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.termsAgreed", "You must agree to our terms and conditions.");
}
if(typeof tw.local.reqeustObject.dateOfReqeust == "undefined" || !tw.local.tw.local.reqeustObject.dateOfReqeust){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dateOfReqeust", "Field is required");
}else{
  if(tw.local.reqeustObject.dateOfReqeust.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dateOfReqeust", "Field is required");
  }
}
var emailRegEx = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
if(!emailRegEx.test(tw.local.reqeustObject.customer.email)){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.email", "Valid Email Required");
}
if(typeof tw.local.reqeustObject.customer.name == "undefined" || !tw.local.tw.local.reqeustObject.customer.name){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.name", "Field is required");
}else{
  if(tw.local.reqeustObject.customer.name.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.name", "Field is required");
  }
}
if(typeof tw.local.reqeustObject.customer.age == "undefined" || !tw.local.tw.local.reqeustObject.customer.age){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.age", "Field is required");
}else{
  if(tw.local.reqeustObject.customer.age.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.age", "Field is required");
  }
}
if(typeof tw.local.reqeustObject.customer.height == "undefined" || !tw.local.tw.local.reqeustObject.customer.height){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.height", "Field is required");
}else{
  if(tw.local.reqeustObject.customer.height.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.height", "Field is required");
  }
}
if(typeof tw.local.reqeustObject.customer.weight == "undefined" || !tw.local.tw.local.reqeustObject.customer.weight){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.weight", "Field is required");
}else{
  if(tw.local.reqeustObject.customer.weight.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.weight", "Field is required");
  }
}
for(var i = 0; i < tw.local.reqeustObject.addresses.listLength; i++){
	if(typeof tw.local.reqeustObject.addresses[i].address1 == "undefined" || !tw.local.tw.local.reqeustObject.addresses[i].address1){
	  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.addresses["+i+"].address1", "Field is required");
	}else{
	  if(tw.local.reqeustObject.customer.addresses[i].address1.trim() == ""){
	    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.addresses["+i+"].address1", "Field is required");
	  }
	}
	if(typeof tw.local.reqeustObject.addresses[i].city == "undefined" || !tw.local.tw.local.reqeustObject.addresses[i].city){
	  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.addresses["+i+"].city", "Field is required");
	}else{
	  if(tw.local.reqeustObject.customer.addresses[i].city.trim() == ""){
	    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.addresses["+i+"].city", "Field is required");
	  }
	}
	if(typeof tw.local.reqeustObject.addresses[i].state == "undefined" || !tw.local.tw.local.reqeustObject.addresses[i].state){
	  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.addresses["+i+"].state", "Field is required");
	}else{
	  if(tw.local.reqeustObject.customer.addresses[i].state.trim() == ""){
	    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.addresses["+i+"].state", "Field is required");
	  }
	}
	if(typeof tw.local.reqeustObject.addresses[i].zip == "undefined" || !tw.local.tw.local.reqeustObject.addresses[i].zip){
	  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.customer.addresses["+i+"].zip", "Field is required");
	}else{
	  if(tw.local.reqeustObject.customer.addresses[i].zip.trim() == ""){
	    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.addresses["+i+"].zip", "Field is required");
	  }
	}
}
var bankRoutingRegEx = /^((0[0-9])|(1[0-2])|(2[1-9])|(3[0-2])|(6[1-9])|(7[0-2])|80)([0-9]{7})$/;
if(!bankRoutingRegEx.test(tw.local.reqeustObject.dataObject.routingNumber)){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.routingNumber", "Please provide a valid routing number");
}
if(typeof tw.local.reqeustObject.dataObject.accountNumber == "undefined" || !tw.local.tw.local.dataObject.accountNumber){
  tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.accountNumber", "Field is required");
}else{
  if(tw.local.reqeustObject.dataObject.accountNumber.trim() == ""){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.accountNumber", "Field is required");
  }
}
if(!emailRegEx.test(tw.local.reqeustObject.dataObject.email)){
    tw.system.addCoachValidationError(tw.system.coachValidation, "tw.local.reqeustObject.dataObject.email", "Valid Email Required");
}

I’ll let you decide which way you want to do it 😉

Tell me what you REALLY think...

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>