=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java 2016-03-03 05:13:32 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/feedback/ErrorCode.java 2016-03-13 10:19:38 +0000 @@ -54,7 +54,8 @@ /* Preheat Errors */ E5000( "Found matching object for given reference, but import mode is CREATE. Identifier was {0}, and object was {1}." ), E5001( "No matching object for given reference. Identifier was {0}, and object was {1}." ), - E5002( "Invalid reference {0} on object {1} for association \"{2}\"." ); + E5002( "Invalid reference {0} on object {1} for association \"{2}\"." ), + E5003( "Property \"{0}\" with value \"{1}\" on object {2} already exists on object {3}." ); private String message; === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/Preheat.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/Preheat.java 2016-03-12 09:00:34 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/Preheat.java 2016-03-13 10:19:38 +0000 @@ -55,6 +55,8 @@ private Map usernames = new HashMap<>(); + private Map, Map>> uniquenessMap = new HashMap<>(); + public Preheat() { } @@ -309,4 +311,14 @@ return klass; } + + public void setUniquenessMap( Map, Map>> uniquenessMap ) + { + this.uniquenessMap = uniquenessMap; + } + + public Map, Map>> getUniquenessMap() + { + return uniquenessMap; + } } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatIdentifier.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatIdentifier.java 2016-03-12 09:00:34 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatIdentifier.java 2016-03-13 10:19:38 +0000 @@ -97,9 +97,9 @@ if ( name == null ) { - return identifiers.toString(); + return identifiers.toString() + " (" + object.getClass().getSimpleName() + ")"; } - return name + " " + identifiers.toString(); + return name + " " + identifiers.toString() + " (" + object.getClass().getSimpleName() + ")"; } } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatParams.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatParams.java 2016-03-04 11:51:15 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatParams.java 2016-03-13 10:19:38 +0000 @@ -1,6 +1,14 @@ package org.hisp.dhis.preheat; import com.google.common.base.MoreObjects; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.user.User; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /* * Copyright (c) 2004-2016, University of Oslo @@ -30,14 +38,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import org.hisp.dhis.common.IdentifiableObject; -import org.hisp.dhis.user.User; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - /** * @author Morten Olav Hansen */ @@ -53,6 +53,8 @@ private Map, Set>> references = new HashMap<>(); + private Map, List> objects = new HashMap<>(); + public PreheatParams() { } @@ -100,6 +102,16 @@ return this; } + public Map, List> getObjects() + { + return objects; + } + + public void setObjects( Map, List> objects ) + { + this.objects = objects; + } + public Map, Set>> getReferences() { return references; @@ -111,7 +123,6 @@ return this; } - @Override public String toString() { === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatService.java 2016-03-13 08:51:26 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/preheat/PreheatService.java 2016-03-13 10:19:38 +0000 @@ -80,6 +80,14 @@ */ Map, Set>> collectReferences( Map, List> objects ); + /** + * Scan objects and collect unique values (used to verify object properties with unique=true) + * + * @param objects Objects to scan + * @return Klass -> Property.name -> Value -> UID + */ + Map, Map>> collectUniqueness( Map, List> objects ); + Map, Map>> collectObjectReferences( Map, List> objects ); /** @@ -103,10 +111,11 @@ /** * Check for properties that are unique. * - * @param objects Object to check - * @param preheat Preheat Cache to use + * @param objects Object to check + * @param preheat Preheat Cache to use + * @param identifier Use this identifier type report issues */ - List checkUniqueness( List objects, Preheat preheat ); + List checkUniqueness( List objects, Preheat preheat, PreheatIdentifier identifier ); /** * Check for properties that are unique. @@ -114,7 +123,7 @@ * @param object Object to check * @param preheat Preheat Cache to use */ - List checkUniqueness( IdentifiableObject object, Preheat preheat ); + List checkUniqueness( IdentifiableObject object, Preheat preheat, PreheatIdentifier identifier ); /** * Connects id object references on a given object using a given identifier + a preheated Preheat cache. === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java 2016-03-13 08:52:19 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/preheat/DefaultPreheatService.java 2016-03-13 10:19:38 +0000 @@ -161,6 +161,35 @@ } } + Set> klasses = new HashSet<>(); + + if ( params.getReferences().containsKey( PreheatIdentifier.UID ) ) + { + klasses.addAll( params.getReferences().get( PreheatIdentifier.UID ).keySet() ); + } + + if ( params.getReferences().containsKey( PreheatIdentifier.CODE ) ) + { + klasses.addAll( params.getReferences().get( PreheatIdentifier.CODE ).keySet() ); + } + + Map, List> uniqueCollectionMap = new HashMap<>(); + + // TODO fix this, should be part of main preheat process, this will slow things down.. but we need all objects to check for uniqueness + for ( Class klass : klasses ) + { + Query query = Query.from( schemaService.getDynamicSchema( klass ) ); + query.setUser( preheat.getUser() ); + List objects = queryService.query( query ); + + if ( !objects.isEmpty() ) + { + uniqueCollectionMap.put( klass, new ArrayList<>( objects ) ); + } + } + + preheat.setUniquenessMap( collectUniqueness( uniqueCollectionMap ) ); + return preheat; } @@ -370,11 +399,51 @@ if ( uidMap.get( UserGroup.class ).isEmpty() ) uidMap.remove( UserGroup.class ); if ( codeMap.get( UserGroup.class ).isEmpty() ) codeMap.remove( UserGroup.class ); } - + } + + return map; + } + + @Override + @SuppressWarnings( "unchecked" ) + public Map, Map>> collectUniqueness( Map, List> objects ) + { + Map, Map>> uniqueMap = new HashMap<>(); + + if ( objects.isEmpty() ) + { + return uniqueMap; + } + + Map, List> scanObjects = new HashMap<>(); + scanObjects.putAll( objects ); // clone objects list, we don't want to modify it + + if ( scanObjects.containsKey( User.class ) ) + { + List users = scanObjects.get( User.class ); + List userCredentials = new ArrayList<>(); + + for ( IdentifiableObject identifiableObject : users ) + { + User user = (User) identifiableObject; + + if ( user.getUserCredentials() != null ) + { + userCredentials.add( user.getUserCredentials() ); + } + } + + scanObjects.put( UserCredentials.class, userCredentials ); + } + + for ( Class objectClass : scanObjects.keySet() ) + { + Schema schema = schemaService.getDynamicSchema( objectClass ); + List identifiableObjects = scanObjects.get( objectClass ); uniqueMap.put( objectClass, handleUniqueProperties( schema, identifiableObjects ) ); } - return map; + return uniqueMap; } private Map> handleUniqueProperties( Schema schema, List objects ) @@ -594,7 +663,7 @@ } @Override - public List checkUniqueness( List objects, Preheat preheat ) + public List checkUniqueness( List objects, Preheat preheat, PreheatIdentifier identifier ) { List objectErrorReports = new ArrayList<>(); @@ -606,7 +675,7 @@ for ( int i = 0; i < objects.size(); i++ ) { IdentifiableObject object = objects.get( i ); - List errorReports = checkUniqueness( object, preheat ); + List errorReports = checkUniqueness( object, preheat, identifier ); if ( errorReports.isEmpty() ) continue; @@ -620,14 +689,41 @@ } @Override - public List checkUniqueness( IdentifiableObject object, Preheat preheat ) + public List checkUniqueness( IdentifiableObject object, Preheat preheat, PreheatIdentifier identifier ) { List errorReports = new ArrayList<>(); - if ( object == null ) - { - return errorReports; - } + if ( object == null || Preheat.isDefault( object ) ) return errorReports; + + Map> uniquenessMap = preheat.getUniquenessMap().get( object.getClass() ); + + if ( uniquenessMap == null ) return errorReports; + + Schema schema = schemaService.getDynamicSchema( object.getClass() ); + List uniqueProperties = schema.getProperties().stream() + .filter( p -> p.isPersisted() && p.isOwner() && p.isUnique() ) + .collect( Collectors.toList() ); + + uniqueProperties.forEach( property -> { + Object value = ReflectionUtils.invokeMethod( object, property.getGetterMethod() ); + + if ( value != null ) + { + if ( uniquenessMap.containsKey( property.getName() ) && !uniquenessMap.get( property.getName() ).isEmpty() ) + { + String persistedUid = uniquenessMap.get( property.getName() ).get( value ); + + if ( persistedUid != null ) + { + if ( !object.getUid().equals( persistedUid ) ) + { + errorReports.add( new ErrorReport( object.getClass(), ErrorCode.E5003, property.getName(), value, + identifier.getIdentifiersWithName( object ), persistedUid ) ); + } + } + } + } + } ); return errorReports; } === modified file 'dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata2/objectbundle/DefaultObjectBundleService.java' --- dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata2/objectbundle/DefaultObjectBundleService.java 2016-03-13 08:51:26 +0000 +++ dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata2/objectbundle/DefaultObjectBundleService.java 2016-03-13 10:19:38 +0000 @@ -105,6 +105,7 @@ } preheatParams.setUser( params.getUser() ); + preheatParams.setObjects( params.getObjects() ); if ( PreheatMode.REFERENCE == preheatParams.getPreheatMode() ) { @@ -164,7 +165,8 @@ objectBundleValidation.addObjectErrorReports( validateBySchemas( klass, bundle.getObjectMap().get( klass ), bundle ) ); objectBundleValidation.addObjectErrorReports( preheatService.checkReferences( bundle.getObjectMap().get( klass ), bundle.getPreheat(), bundle.getPreheatIdentifier() ) ); - objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat() ) ); + objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat(), + bundle.getPreheatIdentifier() ) ); } if ( bundle.getImportMode().isCreate() ) @@ -173,7 +175,8 @@ objectBundleValidation.addObjectErrorReports( validateBySchemas( klass, bundle.getObjects( klass, false ), bundle ) ); objectBundleValidation.addObjectErrorReports( preheatService.checkReferences( bundle.getObjectMap().get( klass ), bundle.getPreheat(), bundle.getPreheatIdentifier() ) ); - objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat() ) ); + objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat(), + bundle.getPreheatIdentifier() ) ); } if ( bundle.getImportMode().isUpdate() ) @@ -182,7 +185,8 @@ objectBundleValidation.addObjectErrorReports( validateBySchemas( klass, bundle.getObjects( klass, true ), bundle ) ); objectBundleValidation.addObjectErrorReports( preheatService.checkReferences( bundle.getObjectMap().get( klass ), bundle.getPreheat(), bundle.getPreheatIdentifier() ) ); - objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat() ) ); + objectBundleValidation.addObjectErrorReports( preheatService.checkUniqueness( bundle.getObjectMap().get( klass ), bundle.getPreheat(), + bundle.getPreheatIdentifier() ) ); } if ( bundle.getImportMode().isDelete() ) === modified file 'dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata2/objectbundle/ObjectBundleServiceTest.java' --- dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata2/objectbundle/ObjectBundleServiceTest.java 2016-03-13 07:52:55 +0000 +++ dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata2/objectbundle/ObjectBundleServiceTest.java 2016-03-13 10:19:38 +0000 @@ -64,7 +64,6 @@ import org.hisp.dhis.user.UserGroup; import org.hisp.dhis.user.UserService; import org.hisp.dhis.validation.ValidationRule; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; @@ -1371,7 +1370,6 @@ } @Test - @Ignore public void testCreateMetadataWithSuperuserRoleInjected() throws IOException { createUserAndInjectSecurityContext( true ); @@ -1385,8 +1383,10 @@ params.setObjects( metadata ); ObjectBundle bundle = objectBundleService.create( params ); - assertTrue( objectBundleService.validate( bundle ).getObjectErrorReports().isEmpty() ); - objectBundleService.commit( bundle ); + ObjectBundleValidation validate = objectBundleService.validate( bundle ); + + assertFalse( validate.getObjectErrorReports().isEmpty() ); + assertEquals( 1, validate.getErrorReportsByCode( UserAuthorityGroup.class, ErrorCode.E5003 ).size() ); } private void defaultSetup()