Thursday, July 21, 2016

Custom Validators with WSO2 DSS


Input validation allows data services to validate the input parameter values that are presented in a request. This allows to stops the execution of the request, if the input has not met the required criteria. WSO2 Data Services Server provides a set of built-in validators which will be sufficient for the mostly used use cases. And also it provides an extension mechanism, where custom validators can be written.
So input validators in WSO2 DSS can be categorized as follows.
  • Built-in validators
  • Long Range Validator- Validates if an integer value is in the specified range.
  • Double Range Validator- Validates if an integer value is in the specified range.
  • Length Validator- Validates the string length of a given parameter against a specified length. 
  • Pattern Validator- Validates the string value of the parameter against a given regular expression. 
  • Custom Validator- allows the user to instroduce custom validation logic by adding Custom Validators.
In this post custom validators will be discussed in detail.

For defining a custom validator, the user must implement the interface "org.wso2.carbon.dataservices.core.validation.Validator" or the "org.wso2.carbon.dataservices.core.validation.ValidatorExt". If the validation fails, the validate method in the interface by default throws an exception of type ValidationException .

The definition of the Validator interface is as follows.


public interface Validator {
    public void validate(ValidationContext context, String name, ParamValue value) throws ValidationException;
}

ValidatorExt interface is introduced from the DSS 3.5.1 release onwards. If the custom validator needs to be initialized by providing any properties, it is required to implement the interface ValidatorExt. It extends the Validator interface and the definition of the interface is as follows.

public interface ValidatorExt extends Validator {
    public void init(Map<String, String> props);
}
A sample Java code for Custom Validator class which implements the ValdiatorExt is as follows. You can add any validation logic as required.


package org.acme;

import org.wso2.carbon.dataservices.core.engine.ParamValue;
import org.wso2.carbon.dataservices.core.validation.ValidationContext;
import org.wso2.carbon.dataservices.core.validation.ValidationException;
import org.wso2.carbon.dataservices.core.validation.ValidatorExt;

import java.util.Map;

public class EmployeeEmailValidator implements ValidatorExt {
    String domain;
    int maxLength;

    @Override public void validate(ValidationContext context, String name, ParamValue value)
            throws ValidationException {

        if (!isEmpty(value)) {
            String email = value.getScalarValue();
            String[] emailParts = email.split("@");
            
            String partUser;
            String partDomain;

            if (emailParts.length == 2) {
                partUser = emailParts[0]; 
                partDomain = emailParts[1]; 
            } else {
                throw new ValidationException("Invalid Email Format", name, value);
            }

            //Validate for the property "domain"
            if (!partDomain.equals(domain)) {
                throw new ValidationException("Invalid Email Domain", name, value);
            }

            //Validate for the property "maxLength"
            if (partUser.length() > maxLength) {
                throw new ValidationException("Invalid Email Length", name, value);
            }

        } else {
            throw new ValidationException("Email should not be empty", name, value);
        }
    }

    @Override public void init(Map<String, String> map) {
        domain = map.get("domain");
        String len = map.get("maxLength");
        if (len != null) {
            maxLength = Integer.parseInt(len);
        }
    }

    private boolean isEmpty(ParamValue val) {
        return val == null || val.getScalarValue() == null || val.getScalarValue().length() == 0;
    }
}

The class structure for the above class is as follows.

You can complie and create the jar file as follows. When doing the compile, you need to point your build path to org.wso2.carbon.dataservices.core_x.x.x.jar file.


javac -d "classes" -cp /home/testuser/DSS/wso2dss-3.5.1/repository/components/plugins/org.wso2.carbon.dataservices.core_4.3.5.jar org/acme/EmployeeEmailValidator.java
jar cvf emailvalidator.jar -C classes/ .


Then copy the created jar file to the wso2dss-x.y.z/repository/components/lib folder and restart the service. Then you can create the data service which use this custom validator.

When adding the validator you can add the custom properties via the UI as follows.
The complete data services definition for the above example is as follows.


<data name="AddEmployeeDS" transports="http https local">
   <config enableOData="false" id="TestDB">
      <property name="driverClassName">org.h2.Driver</property>
      <property name="url">jdbc:h2:file:./samples/database/DATA_SERV_SAMP</property>
      <property name="username">wso2ds</property>
      <property name="password">wso2ds</property>
   </config>
   <query id="addEmployeeQuery" useConfig="TestDB">
      <sql>insert into Employees (employeeNumber,lastName,firstName,email,salary) values(:employeeNumber,:lastName,:firstName,:email,:salary)</sql>
      <param name="employeeNumber" sqlType="STRING"/>
      <param name="lastName" sqlType="STRING"/>
      <param name="firstName" sqlType="STRING"/>
      <param name="email" sqlType="STRING">
         <validateCustom class="org.acme.EmployeeEmailValidator">
            <properties>
               <property name="maxLength">5</property>
               <property name="domain">abc.com</property>
            </properties>
         </validateCustom>
      </param>
      <param name="salary" sqlType="STRING"/>
   </query>
   <operation name="AddEmployee">
      <call-query href="addEmployeeQuery">
         <with-param name="employeeNumber" query-param="employeeNumber"/>
         <with-param name="lastName" query-param="lastName"/>
         <with-param name="firstName" query-param="firstName"/>
         <with-param name="email" query-param="email"/>
         <with-param name="salary" query-param="salary"/>
      </call-query>
   </operation>
</data>
Now service creation is completed and you can test. For example if you invoke the AddEmployee operation with invalid email length you will receive a response like below.


References: https://docs.wso2.com/display/DSS350/Adding+Input+Mappings#AddingInputMappings-Customvalidators

No comments:

Post a Comment