-
Notifications
You must be signed in to change notification settings - Fork 684
[3.5 Feb 2014] New Validations Module
Jiras:
- https://www.mulesoft.org/jira/browse/MULE-6087
- https://www.mulesoft.org/jira/browse/MULE-6631
- https://www.mulesoft.org/jira/browse/MULE-6624
Forum discussion: http://forum.mulesoft.org/mulesoft/topics/_3_5_feb_2014_new_validations_module?rfm=1
It's a very common use case when doing SaaS integration to need a mechanism to test some conditions are met and throw an exception if validation failed. Mule currently provides some mechanisms to do this but non of them completely fullfils the use case:
Filters can stop execution of a flow if a condition is not met, however they're not a good fit for this because:
- Filters just kill event's processing. They don't throw an exception or provide any mechanism to take action on that error
- Filters force you to write a MEL expression for each validation. No out of the box solution
Another option is to use a choice element to evaluate the condition and then throw an exception using groovy or other expression evaluator. This solution is quite verbose and overly complex.
- You have to evalute the condition through a custom expression
- You need custom code to actually throw the exception
- The choice element forces you to an unnecesary clause
- As a developer, I want to be able to throw an exception is a condition is not met
- As a developer, I want to be able to optionally customize the type of the exception and the exception message
- As a developer, I want to have common validations already implemented so that I don't need a expression for all of them
- As a developer, I want to be able to execute many validations at once and get one single error report for all of them
- As a developer, I want all arguments in all validators to accept MEL expressions
This will be implemented as as a set of message processors in a new CE module called validations. If validation fails, a ValidationException will be thrown. By default, this exceptio will have a meaningful message attached. You can optionally customize that message though and you can even customize the type of exception you want to thrown, as long as that exception time has a constructor that overrides Exception(String)
<validate:email email="[email protected]" />
<!-- Matches Canadian PostalCode formats with or without spaces (e.g., "T2X 1V4" or "T2X1V4") -->
<validate:using-regex value="T2X1V4" regex="^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$" />
<validate:time time="12:08 PM" pattern="h:mm a" locale="US" />
<validate:date date="Wed, Jul 4, '01" pattern="EEE, MMM d, ''yy" locale="US" />
<validate:not-empty value="#[value]" />
<validate:length value="john" min-length="1" max-length="4" />
<validate:not-null expression="#[value]" />
<validate:null expression="#[nullValue]" />
<validate:long value="#[value]" min-value="#[min]" max-value="#[max]" />
<validate:double value="#[value]" min-value="#[min]" max-value="#[max]" />
<validate:float value="#[value]" min-value="#[min]" max-value="#[max]" />
<validate:integer value="#[value]" min-value="#[min]" max-value="#[max]" />
<validate:short value="#[value]" min-value="#[min]" max-value="#[max]" />
<validate:credit-card-number credit-card-number="5121749719020831" credit-card-type="MASTERCARD" />
<validate:top-level-domain-country country-code="ar" />
<validate:percentage percentage="#[percentage]" />
<validate:ip-address ip="127.0.0.0" />
<validate:isbn10 isbn="0201530821"/>
<validate:isbn13 isbn="9781413304541"/>
<validate:top-level-domain top-level-domain="com" />
<validate:url url="http://www.mulesoft.com" />
Although the validations above are quite general and cover many use cases, there's always the possibility that it doesn't quite matches your use case, that's why there're two fallback expressions which simply evaluate that a given expression is true or false
<validate:true expression="#[payload > 21]" />
<validate:false expression="#[customer.hasDebt()]" />
On any of these validations you can customize the type of exception thrown by providing the canonical name of an exception type. If that exception type does not override the constructor Exception(String) an IllegalArgumentException will be thrown.
<validate:true expression="#[payload > 21]" exception-class="org.myproject.NotAnAdultException" message="#[payload.name] #[payload.surname] is not an adult" />
NOTE: You don't have to customize both the exception type and the message, you also just customize one of them.
There're scenarios in which you want to evaluate many conditions with the possiblity that many of them fail. In those cases, it's useful to generate only one error with all of the descriptions. This use case is also supported
<validate:all>
<validate:true expression="#[payload > 21]" exception-class="org.myproject.NotAnAdultException" />
<validate:credit-card-number credit-card-number="#[payload.ccNumber]" credit-card-type="#[payload.ccType]" />
<validate:not-empty value=#[payload.name] message="name cannot be empty" />
</validate:all>
If any of the validations above fail, one single exception of type ValidationResultException is thrown. This exception contains an object of type ValidationResult which will give you access to all the error messages.
Unlike the other validators, the all validator does not allow customizing the exception type or the message (you can however customize the message of the inner validators though). What it does allow validating is wether it should throw an exception in case of failure or not. You can set this validator to not throw an exception but set the message payload to an instance of ValidationResult instead.
For example, the flow below implements a validation endpoint which tells you if the payload is valid or not:
<flow name="validate">
<validate:all throws-exception="false">
<validate:true expression="#[payload > 21]" exception-class="org.myproject.NotAnAdultException" />
<validate:credit-card-number credit-card-number="#[payload.ccNumber]" credit-card-type="#[payload.ccType]" />
<validate:not-empty value=#[payload.name] message="name cannot be empty" />
</validate:all>
<choice>
<when expression="#[payload.hasErrors()]">
<set-payload="Errors where found: #[payload.toString()]">
</when>
<otherwise>
<set-payload="Everything's valid!">
</otherwise>
</choice>
</flow>
You can also just return the ValidationResult object as json
<flow name="validate">
<validate:all throws-exception="false">
<validate:true expression="#[payload > 21]" exception-class="org.myproject.NotAnAdultException" />
<validate:credit-card-number credit-card-number="#[payload.ccNumber]" credit-card-type="#[payload.ccType]" />
<validate:not-empty value=#[payload.name] message="name cannot be empty" />
</validate:all>
<json:object-to-json-transformer doc:name="Object to JSON"/>
</flow>
N/A
No impact
No impact
No impact
No impact
No impact since this is a new feature
- Add new doc page describing the feature
- Update training documents