Skip to content

[3.5 Feb 2014] New Validations Module

Mariano Gonzalez edited this page Dec 13, 2013 · 5 revisions

Jiras:

Forum discussion: http://forum.mulesoft.org/mulesoft/topics/_3_5_feb_2014_new_validations_module?rfm=1

Motivation / Context

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

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

Choice + scripting component

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

Uses cases

  • 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

Behaviour

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)

Syntax

Validate Email address

<validate:email email="[email protected]" />

Validate using a regular expression

<!-- 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 a string is a valid time according to a pattern

<validate:time time="12:08 PM" pattern="h:mm a" locale="US" />

Validate a string is a valid date according to a pattern

<validate:date date="Wed, Jul 4, '01" pattern="EEE, MMM d, ''yy" locale="US" />

Validate String, Collection or Map is not empty

<validate:not-empty value="#[value]" />

Validate String length

<validate:length value="john" min-length="1" max-length="4" />

Validate not null (includes NullPayload)

<validate:not-null expression="#[value]" />

Validate null (includes NullPayload)

<validate:null expression="#[nullValue]" />

Validate a String can be transformed to a number

<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]" />

Valid Credit Card number

<validate:credit-card-number credit-card-number="5121749719020831" credit-card-type="MASTERCARD" />

Validate top level domain country code

<validate:top-level-domain-country country-code="ar" />

Validate a String is a valid percentage

<validate:percentage percentage="#[percentage]" />

Validate IP address

<validate:ip-address ip="127.0.0.0" />

Validate ISBN10 or ISBN13 code

<validate:isbn10 isbn="0201530821"/>
<validate:isbn13 isbn="9781413304541"/>

Validate top level domain

<validate:top-level-domain top-level-domain="com" />

Validate domain

<validate:url url="http://www.mulesoft.com" />

Fallback validations

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 &gt; 21]" />
<validate:false expression="#[customer.hasDebt()]" />

Customizing exception class and message

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 &gt; 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.

Validating many conditions at once

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 &gt; 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 &gt; 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 &gt; 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>

Risks

N/A

DevKit Impact

No impact

MMC Impact

No impact

CH Impact

No impact

Service Registry Impact

No impact

Migration Impact

No impact since this is a new feature

Documentation Impact

  • Add new doc page describing the feature
  • Update training documents
Clone this wiki locally