← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 10863: new ValidationService (currenly only used in EventService), will properly validate DataElement an...

 

------------------------------------------------------------
revno: 10863
committer: Morten Olav Hansen <mortenoh@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2013-05-17 13:44:41 +0700
message:
  new ValidationService (currenly only used in EventService), will properly validate DataElement and their values (including optionsets, usernames, etc)
added:
  dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/DefaultValidationService.java
  dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/ValidationService.java
modified:
  dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/event/BaseEventService.java
  dhis-2/dhis-dxf2/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ValidationUtils.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/DefaultValidationService.java'
--- dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/DefaultValidationService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/DefaultValidationService.java	2013-05-17 06:44:41 +0000
@@ -0,0 +1,164 @@
+package org.hisp.dhis.dxf2;
+
+/*
+ * Copyright (c) 2004-2013, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hisp.dhis.dataelement.DataElement;
+import org.hisp.dhis.i18n.I18nFormat;
+import org.hisp.dhis.i18n.I18nManager;
+import org.hisp.dhis.i18n.I18nManagerException;
+import org.hisp.dhis.i18n.locale.LocaleManager;
+import org.hisp.dhis.system.util.MathUtils;
+import org.hisp.dhis.user.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class DefaultValidationService implements ValidationService
+{
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    @Autowired
+    private I18nManager i18nManager;
+
+    @Autowired
+    private UserService userService;
+
+    // -------------------------------------------------------------------------
+    // ValidationService Implementation
+    // -------------------------------------------------------------------------
+
+    @Override
+    public ValidationStatus validateDataElement( DataElement dataElement, String value )
+    {
+        I18nFormat format;
+
+        try
+        {
+            format = i18nManager.getI18nFormat();
+        }
+        catch ( I18nManagerException ex )
+        {
+            return new ValidationStatus( false, ex.getMessage() );
+        }
+
+        value = value.trim();
+
+        if ( value.length() >= 255 )
+        {
+            return new ValidationStatus( false, value + " is more than 255 characters." );
+        }
+
+        if ( dataElement.getType().equals( DataElement.VALUE_TYPE_BOOL ) )
+        {
+            if ( !(value.equalsIgnoreCase( "true" ) || value.equalsIgnoreCase( "false" )) )
+            {
+                return new ValidationStatus( false, value + " is not a valid boolean expression." );
+            }
+        }
+        else if ( dataElement.getType().equals( DataElement.VALUE_TYPE_TRUE_ONLY ) )
+        {
+            if ( !value.equalsIgnoreCase( "true" ) )
+            {
+                return new ValidationStatus( false, value + " can only be true." );
+            }
+        }
+        else if ( dataElement.getType().equals( DataElement.VALUE_TYPE_DATE ) )
+        {
+            boolean dateIsValidated = format.parseDate( value ) != null;
+
+            if ( !dateIsValidated )
+            {
+                return new ValidationStatus( false, value + " is not a valid date expression." );
+            }
+        }
+        else if ( dataElement.getType().equals( DataElement.VALUE_TYPE_USER_NAME ) )
+        {
+            if ( userService.getUserCredentialsByUsername( value ) == null )
+            {
+                return new ValidationStatus( false, value + " is not a valid username." );
+            }
+        }
+        else if ( dataElement.getType().equals( DataElement.VALUE_TYPE_STRING ) )
+        {
+            if ( dataElement.getOptionSet() != null )
+            {
+                if ( !dataElement.getOptionSet().getOptions().contains( value ) )
+                {
+                    return new ValidationStatus( false, value + " is not a valid option for this optionSet." );
+                }
+            }
+            else if ( dataElement.getTextType().equals( DataElement.VALUE_TYPE_TEXT ) ||
+                dataElement.getTextType().equals( DataElement.VALUE_TYPE_LONG_TEXT ) )
+            {
+                // no validation for this right now, we already to length validation
+            }
+        }
+        else if ( dataElement.getType().equals( DataElement.VALUE_TYPE_NUMBER ) )
+        {
+            if ( !MathUtils.isNumeric( value ) )
+            {
+                return new ValidationStatus( false, value + " is not a valid number." );
+            }
+
+            if ( dataElement.getOptionSet() != null )
+            {
+                if ( !dataElement.getOptionSet().getOptions().contains( value ) )
+                {
+                    return new ValidationStatus( false, value + " is not a valid option for this optionSet." );
+                }
+            }
+
+            if ( dataElement.getNumberType().equals( DataElement.VALUE_TYPE_INT ) )
+            {
+                if ( !MathUtils.isInteger( value ) )
+                {
+                    return new ValidationStatus( false, value + " is not a valid integer." );
+                }
+            }
+            else if ( dataElement.getNumberType().equals( DataElement.VALUE_TYPE_POSITIVE_INT ) )
+            {
+                if ( !MathUtils.isPositiveInteger( value ) )
+                {
+                    return new ValidationStatus( false, value + " is not a valid positive integer." );
+                }
+            }
+            else if ( dataElement.getNumberType().equals( DataElement.VALUE_TYPE_NEGATIVE_INT ) )
+            {
+                if ( !MathUtils.isNegativeInteger( value ) )
+                {
+                    return new ValidationStatus( false, value + " is not a valid negative integer." );
+                }
+            }
+        }
+
+        return new ValidationStatus( true );
+    }
+}

=== added file 'dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/ValidationService.java'
--- dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/ValidationService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/ValidationService.java	2013-05-17 06:44:41 +0000
@@ -0,0 +1,66 @@
+package org.hisp.dhis.dxf2;
+
+/*
+ * Copyright (c) 2004-2013, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hisp.dhis.dataelement.DataElement;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface ValidationService
+{
+    class ValidationStatus
+    {
+        private boolean success;
+
+        private String message;
+
+        public ValidationStatus( boolean success )
+        {
+            this.success = success;
+        }
+
+        public ValidationStatus( boolean success, String message )
+        {
+            this.success = success;
+            this.message = message;
+        }
+
+        public boolean isSuccess()
+        {
+            return success;
+        }
+
+        public String getMessage()
+        {
+            return message;
+        }
+    }
+
+    ValidationStatus validateDataElement( DataElement dataElement, String value );
+}

=== modified file 'dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/event/BaseEventService.java'
--- dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/event/BaseEventService.java	2013-05-17 03:29:07 +0000
+++ dhis-2/dhis-dxf2/src/main/java/org/hisp/dhis/dxf2/event/BaseEventService.java	2013-05-17 06:44:41 +0000
@@ -29,6 +29,7 @@
 
 import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.dataelement.DataElementService;
+import org.hisp.dhis.dxf2.ValidationService;
 import org.hisp.dhis.dxf2.importsummary.ImportConflict;
 import org.hisp.dhis.dxf2.importsummary.ImportStatus;
 import org.hisp.dhis.dxf2.importsummary.ImportSummary;
@@ -82,8 +83,13 @@
     private PatientDataValueService patientDataValueService;
 
     @Autowired
+    private ValidationService validationService;
+
+    @Autowired
     private I18nManager i18nManager;
 
+    private I18nFormat format;
+
     // -------------------------------------------------------------------------
     // Implementation
     // -------------------------------------------------------------------------
@@ -122,8 +128,6 @@
 
     private ImportSummary saveSingleEventWithoutRegistration( Program program, OrganisationUnit organisationUnit, Event event )
     {
-        I18nFormat format = null;
-
         try
         {
             format = i18nManager.getI18nFormat();
@@ -141,6 +145,7 @@
         }
 
         ImportSummary importSummary = new ImportSummary();
+        importSummary.setStatus( ImportStatus.SUCCESS );
 
         ProgramStageInstance programStageInstance = saveExecutionDate( program, organisationUnit, executionDate, event.getCompleted() );
 
@@ -155,16 +160,31 @@
             }
             else
             {
-                saveDataValue( programStageInstance, dataElement, dataValue.getValue(), dataValue.getProvidedElsewhere() );
-                importSummary.getDataValueCount().incrementImported();
+                if ( validateDataElement( dataElement, dataValue.getValue(), importSummary ) )
+                {
+                    saveDataValue( programStageInstance, dataElement, dataValue.getValue(), dataValue.getProvidedElsewhere() );
+                    importSummary.getDataValueCount().incrementImported();
+                }
             }
         }
 
-        importSummary.setStatus( ImportStatus.SUCCESS );
-
         return importSummary;
     }
 
+    private boolean validateDataElement( DataElement dataElement, String value, ImportSummary importSummary )
+    {
+        ValidationService.ValidationStatus status = validationService.validateDataElement( dataElement, value );
+
+        if ( !status.isSuccess() )
+        {
+            importSummary.getConflicts().add( new ImportConflict( dataElement.getUid(), status.getMessage() ) );
+            importSummary.getDataValueCount().incrementIgnored();
+            return false;
+        }
+
+        return true;
+    }
+
     private ProgramStageInstance saveExecutionDate( Program program, OrganisationUnit organisationUnit, Date date, Boolean completed )
     {
         ProgramStage programStage = program.getProgramStages().iterator().next();

=== modified file 'dhis-2/dhis-dxf2/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-dxf2/src/main/resources/META-INF/dhis/beans.xml	2013-05-17 03:29:07 +0000
+++ dhis-2/dhis-dxf2/src/main/resources/META-INF/dhis/beans.xml	2013-05-17 06:44:41 +0000
@@ -16,6 +16,8 @@
 
   <bean id="org.hisp.dhis.dxf2.event.EventService" class="org.hisp.dhis.dxf2.event.JacksonEventService" />
 
+  <bean id="org.hisp.dhis.dxf2.ValidationService" class="org.hisp.dhis.dxf2.DefaultValidationService" />
+
   <!-- register idObject handlers -->
 
   <bean id="organisationUnitObjectHandler"

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ValidationUtils.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ValidationUtils.java	2013-04-19 15:58:28 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ValidationUtils.java	2013-05-17 06:44:41 +0000
@@ -1,7 +1,7 @@
 package org.hisp.dhis.system.util;
 
 /*
- * Copyright (c) 2004-2012, University of Oslo
+ * Copyright (c) 2004-2013, University of Oslo
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,17 +27,17 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import org.apache.commons.validator.routines.DateValidator;
+import org.apache.commons.validator.routines.EmailValidator;
+import org.apache.commons.validator.routines.UrlValidator;
+import org.hisp.dhis.dataelement.DataElement;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.validator.routines.DateValidator;
-import org.apache.commons.validator.routines.EmailValidator;
-import org.apache.commons.validator.routines.UrlValidator;
-import org.hisp.dhis.dataelement.DataElement;
-
 import static org.hisp.dhis.dataelement.DataElement.*;
 
 /**
@@ -49,15 +49,15 @@
     private static Pattern POINT_PATTERN = Pattern.compile( "\\[(.+),\\s?(.+)\\]" );
     private static Pattern DIGIT_PATTERN = Pattern.compile( ".*\\d.*" );
     private static Pattern UPPERCASE_PATTERN = Pattern.compile( ".*[A-Z].*" );
-    
+
     private static int LONG_MAX = 180;
     private static int LONG_MIN = -180;
     private static int LAT_MAX = 90;
     private static int LAT_MIN = -90;
-    
+
     /**
      * Validates whether an email string is valid.
-     * 
+     *
      * @param email the email string.
      * @return true if the email string is valid, false otherwise.
      */
@@ -65,11 +65,11 @@
     {
         return EmailValidator.getInstance().isValid( email );
     }
-    
+
     /**
      * Validates whether a date string is valid for the given Locale.
-     * 
-     * @param date the date string.
+     *
+     * @param date   the date string.
      * @param locale the Locale
      * @return true if the date string is valid, false otherwise.
      */
@@ -80,7 +80,7 @@
 
     /**
      * Validates whether a date string is valid for the default Locale.
-     * 
+     *
      * @param date the date string.
      * @return true if the date string is valid, false otherwise.
      */
@@ -88,10 +88,10 @@
     {
         return dateIsValid( date, null );
     }
-    
+
     /**
      * Validates whether an URL string is valid.
-     * 
+     *
      * @param url the URL string.
      * @return true if the URL string is valid, false otherwise.
      */
@@ -99,16 +99,16 @@
     {
         return new UrlValidator().isValid( url );
     }
-    
+
     /**
      * Validates whether a password is valid. A password must:
-     * 
+     * <p/>
      * <ul>
      * <li>Be between 8 and 80 characters long</li>
      * <li>Include at least one digit</li>
      * <li>Include at least one uppercase letter</li>
      * </ul>
-     * 
+     *
      * @param password the password.
      * @return true if the password is valid, false otherwise.
      */
@@ -118,13 +118,13 @@
         {
             return false;
         }
-        
+
         return DIGIT_PATTERN.matcher( password ).matches() && UPPERCASE_PATTERN.matcher( password ).matches();
     }
-    
+
     /**
      * Validates whether a coordinate is valid.
-     * 
+     *
      * @return true if the coordinate is valid, false otherwise.
      */
     public static boolean coordinateIsValid( String coordinate )
@@ -133,17 +133,17 @@
         {
             return false;
         }
-        
+
         Matcher matcher = POINT_PATTERN.matcher( coordinate );
-        
+
         if ( !matcher.find() )
         {
             return false;
         }
-        
+
         double longitude = 0.0;
         double latitude = 0.0;
-        
+
         try
         {
             longitude = Double.parseDouble( matcher.group( 1 ) );
@@ -153,15 +153,15 @@
         {
             return false;
         }
-        
+
         return longitude >= LONG_MIN && longitude <= LONG_MAX && latitude >= LAT_MIN && latitude <= LAT_MAX;
     }
-    
+
     /**
      * Returns the longitude from the given coordinate. Returns null if the
      * coordinate string is not valid. The coordinate is on the form
      * longitude / latitude.
-     * 
+     *
      * @param coordinate the coordinate string.
      * @return the longitude.
      */
@@ -171,9 +171,9 @@
         {
             return null;
         }
-        
+
         Matcher matcher = POINT_PATTERN.matcher( coordinate );
-        
+
         return matcher.find() ? matcher.group( 1 ) : null;
     }
 
@@ -181,7 +181,7 @@
      * Returns the latitude from the given coordinate. Returns null if the
      * coordinate string is not valid. The coordinate is on the form
      * longitude / latitude.
-     * 
+     *
      * @param coordinate the coordinate string.
      * @return the latitude.
      */
@@ -191,31 +191,31 @@
         {
             return null;
         }
-        
+
         Matcher matcher = POINT_PATTERN.matcher( coordinate );
-        
+
         return matcher.find() ? matcher.group( 2 ) : null;
     }
 
     /**
-     * Returns a coordinate string based on the given latitude and longitude. 
+     * Returns a coordinate string based on the given latitude and longitude.
      * The coordinate is on the form longitude / latitude.
-     * 
+     *
      * @param longitude the longitude string.
-     * @param latitude the latitude string.
+     * @param latitude  the latitude string.
      * @return a coordinate string.
      */
     public static String getCoordinate( String longitude, String latitude )
     {
         return "[" + longitude + "," + latitude + "]";
     }
-    
+
     /**
      * Checks if the given data value is valid according to the value type of the
      * given data element. Considers the value to be valid if null or empty.
      * Returns a string if the valid is invalid, possible
      * values are:
-     * 
+     * <p/>
      * <ul>
      * <li>value_null_or_empty</li>
      * <li>data_element_or_type_null_or_empty</li>
@@ -226,8 +226,8 @@
      * <li>value_not_negative_integer</li>
      * <li>value_is_zero_and_not_zero_significant</li>
      * </ul>
-     * 
-     * @param value the data value.
+     *
+     * @param value       the data value.
      * @param dataElement the data element.
      * @return null if the value is valid, a string if not.
      */
@@ -237,47 +237,47 @@
         {
             return null;
         }
-        
+
         if ( dataElement == null || dataElement.getType() == null || dataElement.getType().isEmpty() )
         {
             return "data_element_or_type_null_or_empty";
         }
-        
+
         List<String> types = Arrays.asList( VALUE_TYPE_STRING, VALUE_TYPE_INT, VALUE_TYPE_NUMBER, VALUE_TYPE_POSITIVE_INT, VALUE_TYPE_NEGATIVE_INT );
-        
+
         String type = dataElement.getDetailedNumberType();
-        
+
         if ( types.contains( type ) && value.length() > 255 )
         {
             return "value_length_greater_than_max_length";
         }
-        
+
         if ( VALUE_TYPE_NUMBER.equals( type ) && !MathUtils.isNumeric( value ) )
         {
             return "value_not_numeric";
         }
-        
+
         if ( VALUE_TYPE_INT.equals( type ) && !MathUtils.isInteger( value ) )
         {
             return "value_not_integer";
         }
-        
+
         if ( VALUE_TYPE_POSITIVE_INT.equals( type ) && !MathUtils.isPositiveInteger( value ) )
         {
             return "value_not_positive_integer";
         }
-        
+
         if ( VALUE_TYPE_NEGATIVE_INT.equals( type ) && !MathUtils.isNegativeInteger( value ) )
         {
             return "value_not_negative_integer";
         }
-        
-        if ( VALUE_TYPE_INT.equals( dataElement.getType() ) && MathUtils.isZero( value ) && 
+
+        if ( VALUE_TYPE_INT.equals( dataElement.getType() ) && MathUtils.isZero( value ) &&
             !dataElement.isZeroIsSignificant() && !AGGREGATION_OPERATOR_AVERAGE.equals( dataElement.getAggregationOperator() ) )
         {
             return "value_is_zero_and_not_zero_significant";
         }
-        
+
         return null;
-    }    
+    }
 }