Make duplicate admin repair tool testable, add dry-run feature and RxJava. Also, make DupOrgRepair case-insensitive
Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/7324b5a8 Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/7324b5a8 Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/7324b5a8 Branch: refs/heads/1.x Commit: 7324b5a8312ce4e78abc8f4c01bcd1d46786e532 Parents: ebfa31a Author: Dave Johnson <[email protected]> Authored: Mon Jan 11 16:33:28 2016 -0500 Committer: Dave Johnson <[email protected]> Committed: Mon Jan 11 16:33:28 2016 -0500 ---------------------------------------------------------------------- .../usergrid/tools/DryRunUserOrgManager.java | 62 ++++ .../tools/DuplicateAdminUserRepair.java | 289 +++++++++++++++ .../usergrid/tools/DuplicateOrgInterface.java | 112 ------ .../usergrid/tools/DuplicateOrgRepair.java | 319 ++-------------- .../apache/usergrid/tools/UserOrgInterface.java | 154 ++++++++ .../apache/usergrid/tools/UserOrgManager.java | 364 +++++++++++++++++++ .../tools/DuplicateAdminUserRepairTest.java | 236 ++++++++++++ .../usergrid/tools/DuplicateOrgRepairTest.java | 320 ++++------------ .../usergrid/tools/ExportImportAdminsTest.java | 1 + .../usergrid/tools/MockUserOrgManager.java | 215 +++++++++++ stack/tools/src/test/resources/log4j.properties | 47 +++ 11 files changed, 1476 insertions(+), 643 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java new file mode 100644 index 0000000..966b18c --- /dev/null +++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.usergrid.tools; + +import org.apache.usergrid.management.ManagementService; +import org.apache.usergrid.persistence.EntityManagerFactory; + +import java.util.UUID; + +class DryRunUserOrgManager extends UserOrgManager { + + public DryRunUserOrgManager(EntityManagerFactory emf, ManagementService managementService) { + super( emf, managementService ); + } + + @Override + public void removeUserFromOrg(OrgUser user, Org org) throws Exception { + } + + @Override + public void addUserToOrg(OrgUser user, Org org) throws Exception { + } + + @Override + public void addAppToOrg(UUID appId, Org org) throws Exception { + } + + @Override + public void removeOrgUser(OrgUser orgUser) { + } + + @Override + public void updateOrgUser(OrgUser targetUserEntity) { + } + + @Override + public void setOrgUserName(OrgUser other, String newUserName) { + + } + + @Override + public void removeAppFromOrg(UUID appId, Org org) throws Exception { + } + + @Override + public void removeOrg(Org keeper, Org duplicate) throws Exception { + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java new file mode 100644 index 0000000..902eefa --- /dev/null +++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.usergrid.tools; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.lang3.StringUtils; +import org.apache.usergrid.management.ManagementService; +import org.apache.usergrid.persistence.EntityManagerFactory; +import rx.functions.Action1; + +import java.util.*; + +import static org.apache.usergrid.tools.UserOrgInterface.Org; +import static org.apache.usergrid.tools.UserOrgInterface.OrgUser; + + +/** + * Find duplicate admin users, delete the one that is not indexed. + */ +public class DuplicateAdminUserRepair extends ToolBase { + + UserOrgInterface manager = null; + + static final String THREADS_ARG_NAME = "threads"; + + int threadCount = 5; + + static final String DRYRUN_ARG_NAME = "dryrun"; + + boolean dryRun = false; + + Multimap<String, OrgUser> emails = HashMultimap.create(); + + Multimap<String, OrgUser> usernames = HashMultimap.create(); + + boolean testing = false; + + + DuplicateAdminUserRepair() { + super(); + } + + DuplicateAdminUserRepair(EntityManagerFactory emf, ManagementService managementService ) { + this(); + this.emf = emf; + this.managementService = managementService; + } + + + @Override + @SuppressWarnings("static-access") + public Options createOptions() { + + Options options = super.createOptions(); + + Option dryRunOption = OptionBuilder.hasArg() + .withType(Boolean.TRUE) + .withDescription( "-" + DRYRUN_ARG_NAME + " true to print what tool would do and do not alter data.") + .create( DRYRUN_ARG_NAME ); + options.addOption( dryRunOption ); + + Option writeThreadsOption = OptionBuilder.hasArg() + .withType(0) + .withDescription( "Write Threads -" + THREADS_ARG_NAME ) + .create(THREADS_ARG_NAME); + options.addOption( writeThreadsOption ); + + return options; + } + + + public UserOrgManager createNewRepairManager() { + return new UserOrgManager( emf, managementService ); + } + + + @Override + public void runTool(CommandLine line) throws Exception { + + startSpring(); + setVerbose( line ); + + if (StringUtils.isNotEmpty( line.getOptionValue( THREADS_ARG_NAME ) )) { + try { + threadCount = Integer.parseInt( line.getOptionValue( THREADS_ARG_NAME ) ); + } catch (NumberFormatException nfe) { + logger.error( "-" + THREADS_ARG_NAME + " must be specified as an integer. Aborting..." ); + return; + } + } + + if ( StringUtils.isNotEmpty( line.getOptionValue( DRYRUN_ARG_NAME ) )) { + dryRun = Boolean.parseBoolean( line.getOptionValue( DRYRUN_ARG_NAME )); + } + + if ( manager == null ) { // we use a special manager when mockTesting + if (dryRun) { + manager = new DryRunUserOrgManager( emf, managementService ); + } else { + manager = new UserOrgManager( emf, managementService ); + } + } + + logger.info( "DuplicateAdminUserRepair tool starting up... manager: " + manager.getClass().getSimpleName() ); + + // build multi-map of users by email and users by name + + manager.getUsers().doOnNext( new Action1<OrgUser>() { + @Override + public void call( OrgUser user ) { + + if (user.getUsername() == null) { + logger.warn( "User {} has no username", user.getId() ); + return; + } + if (user.getEmail() == null) { + logger.warn( "User {} has no email", user.getId() ); + return; + } + emails.put( user.getEmail(), user ); + usernames.put( user.getEmail(), user ); + + } + } ).toBlocking().lastOrDefault( null ); + + for ( String username : usernames.keySet() ) { + Collection<OrgUser> users = usernames.get( username ); + + if ( users.size() > 1 ) { + logger.info( "Found multiple users with the username {}", username ); + + // force the username to be reset to the user's email + resolveUsernameConflicts( username, users ); + } + } + + for ( String email : emails.keySet() ) { + + Collection<OrgUser> users = emails.get( email ); + + if ( users.size() > 1 ) { + // get the admin the same way as the rest tier, this way the OTHER + // admins will be removed + OrgUser targetUser = manager.lookupOrgUserByEmail( email ); + + if ( targetUser == null ) { + + List<OrgUser> tempUsers = new ArrayList<OrgUser>( users ); + Collections.sort( tempUsers ); + + OrgUser toLoad = tempUsers.get( 0 ); + + logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad ); + targetUser = toLoad; + + users.remove( toLoad ); + } + + users.remove( targetUser ); + + logger.warn( "Found multiple admins with the email {}. Retaining uuid {}", email, targetUser.getId() ); + + for ( OrgUser orgUser : users ) { + mergeAdmins( orgUser, targetUser ); + } + + // force the index update after all other admins have been merged + if ( dryRun ) { + logger.info("Would force re-index of 'keeper' user {}:{}", + targetUser.getUsername(), targetUser.getId()); + } else { + logger.info( "Forcing re-index of admin with email {} and id {}", email, targetUser.getId()); + manager.updateOrgUser( targetUser ); + } + } + } + + logger.info( "Repair complete" ); + } + + + /** + * When our usernames are equal, we need to check if our emails are equal. If they're not, we need to change the one + * that DOES NOT get returned on a lookup by username + */ + private void resolveUsernameConflicts( String userName, Collection<OrgUser> users ) throws Exception { + + // lookup the admin id + OrgUser existing = manager.lookupOrgUserByUsername( userName ); + + if ( existing == null ) { + logger.warn( "Could not determine an admin for colliding username '{}'. Skipping", userName ); + return; + } + + users.remove( existing ); + + boolean collision = false; + + for ( OrgUser other : users ) { + + // same username and email, these will be merged later in the process, + // skip it + if ( other != null && other.getEmail() != null && other.getEmail().equals( existing.getEmail() ) ) { + logger.info( + "Users with the same username '{}' have the same email '{}'. This will be resolved later in " + + "the process, skipping", userName, existing.getEmail() ); + continue; + } + + // if we get here, the emails do not match, but the usernames do. Force + // both usernames to emails + collision = true; + + setUserName(other, other.getEmail() ); + } + + if ( collision ) { + setUserName(existing, existing.getEmail() ); + } + } + + + /** Set the username to the one provided, if we can't due to duplicate property issues, we fall back to user+uuid */ + private void setUserName( OrgUser other, String newUserName ) throws Exception { + + if ( dryRun ) { + logger.info("Would rename user {}:{} to {}", new Object[] { + other.getUsername(), other.getId(), newUserName }); + } else { + logger.info( "Setting username to {} for user with username {} and id {}", new Object[] { + newUserName, other.getUsername(), other.getId() } ); + + manager.setOrgUserName( other, newUserName ); + } + } + + + /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */ + private void mergeAdmins( OrgUser sourceUser, OrgUser targetUser ) throws Exception { + + Set<Org> sourceOrgs = manager.getUsersOrgs( sourceUser ); + + for ( Org org : sourceOrgs ) { + + if ( dryRun ) { + logger.info("Would add org {}:{} to user {}:{}", new Object[] { + org.getName(), org.getId(), targetUser.getUsername(), targetUser.getId(), }); + + } else { + logger.info( "Adding organization {}:{} to admin with email {} and id {}", + new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } ); + + // copy it over to the target admin + manager.addUserToOrg( targetUser, org ); + } + } + + logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() ); + + if ( dryRun ) { + logger.info( "Would remove user {}:{}", new Object[]{ + sourceUser.getUsername(), sourceUser.getId() } ); + + } else { + manager.removeOrgUser( sourceUser ); + } + } + +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java deleted file mode 100644 index ab708c9..0000000 --- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.usergrid.tools; - -import rx.Observable; - -import java.util.Map; -import java.util.Set; -import java.util.UUID; - - -/** - * Abstraction to make duplicate org repair testable. - */ -interface DuplicateOrgInterface { - - Observable<Org> getOrgs() throws Exception; - - Observable<OrgUser> getUsers() throws Exception; - - Set<Org> getUsersOrgs(OrgUser user) throws Exception; - - Set<OrgUser> getOrgUsers(Org org ) throws Exception; - - void removeOrg(Org keeper, Org duplicate) throws Exception; - - void removeUserFromOrg( OrgUser user, Org org ) throws Exception; - - void addUserToOrg( OrgUser user, Org org ) throws Exception; - - Set<UUID> getOrgApps(Org org) throws Exception; - - void removeAppFromOrg( UUID appId, Org org ) throws Exception; - - void addAppToOrg( UUID appId, Org org ) throws Exception; - - void logDuplicates(Map<String, Set<Org>> duplicatesByName); - - Org getOrg(UUID keeperUuid) throws Exception; - - class Org implements Comparable<Org> { - private UUID id; - private String name; - private long created; - public Object sourceValue; - - public Org( UUID id, String name, long created ) { - this.id = id; - this.name = name; - this.created = created; - } - - @Override - public boolean equals( Object o ) { - if ( o instanceof Org ) { - Org other = (Org)o; - return getId().equals( other.getId() ); - } - return false; - } - - @Override - public int compareTo(Org o) { - return Long.compare( this.created, o.created ); - } - - public UUID getId() { - return id; - } - - public String getName() { - return name; - } - - public long getCreated() { - return created; - } - } - - class OrgUser { - private UUID id; - private String name; - public Object sourceValue; - - public OrgUser( UUID id, String name ) { - this.id = id; - this.name = name; - } - - public UUID getId() { - return id; - } - - public String getName() { - return name; - } - } -} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java index a33ca0e..f4d76fd 100644 --- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java +++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java @@ -16,21 +16,14 @@ */ package org.apache.usergrid.tools; -import com.google.common.collect.BiMap; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; -import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.usergrid.management.OrganizationInfo; -import org.apache.usergrid.management.UserInfo; +import org.apache.usergrid.management.ManagementService; import org.apache.usergrid.persistence.*; -import org.apache.usergrid.persistence.cassandra.CassandraService; -import org.apache.usergrid.persistence.entities.Group; -import rx.Observable; import rx.Scheduler; -import rx.Subscriber; import rx.functions.Action1; import rx.schedulers.Schedulers; @@ -38,15 +31,15 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import static org.apache.usergrid.tools.DuplicateOrgInterface.Org; -import static org.apache.usergrid.tools.DuplicateOrgInterface.OrgUser; +import static org.apache.usergrid.tools.UserOrgInterface.Org; +import static org.apache.usergrid.tools.UserOrgInterface.OrgUser; /** * Find duplicate orgs, delete all but oldest of each and assign users to it. */ public class DuplicateOrgRepair extends ToolBase { - DuplicateOrgInterface manager = null; + UserOrgInterface manager = null; Map<String, Set<Org>> orgsByName = new HashMap<String, Set<Org>>(); @@ -74,6 +67,16 @@ public class DuplicateOrgRepair extends ToolBase { boolean testing = false; + + DuplicateOrgRepair() { + super(); + } + + DuplicateOrgRepair( EntityManagerFactory emf, ManagementService managementService ) { + this(); + this.emf = emf; + this.managementService = managementService; + } @Override @SuppressWarnings("static-access") @@ -165,9 +168,9 @@ public class DuplicateOrgRepair extends ToolBase { if ( manager == null ) { // we use a special manager when mockTesting if (dryRun) { - manager = new DryRunManager(); + manager = new DryRunManager( emf, managementService ); } else { - manager = new RepairManager(); + manager = new UserOrgManager( emf, managementService ); } } @@ -178,7 +181,7 @@ public class DuplicateOrgRepair extends ToolBase { Org org1 = manager.getOrg( org1uuid ); Org org2 = manager.getOrg( org2uuid ); - if ( org1.getName().equals( org2.getName() )) { + if ( org1.getName().equalsIgnoreCase( org2.getName() )) { buildOrgMaps( org1, org2 ); } else { logger.error("org1 and org2 do not have same duplicate name"); @@ -203,8 +206,8 @@ public class DuplicateOrgRepair extends ToolBase { } - public RepairManager createNewRepairManager() { - return new RepairManager(); + public UserOrgManager createNewRepairManager() { + return new UserOrgManager( emf, managementService ); } @@ -213,8 +216,8 @@ public class DuplicateOrgRepair extends ToolBase { Set<Org> orgs = new HashSet<Org>(); orgs.add( org1 ); orgs.add( org2 ); - orgsByName.put( org1.getName(), orgs ); - duplicatesByName.put( org1.getName(), orgs ); + orgsByName.put( org1.getName().toLowerCase(), orgs ); + duplicatesByName.put( org1.getName().toLowerCase(), orgs ); orgsById.put( org1.getId(), org1 ); orgsById.put( org2.getId(), org2 ); @@ -250,12 +253,12 @@ public class DuplicateOrgRepair extends ToolBase { // orgs by name and duplicate orgs by name maps - Set<Org> orgs = orgsByName.get( org.getName() ); + Set<Org> orgs = orgsByName.get( org.getName().toLowerCase() ); if (orgs == null) { orgs = new HashSet<Org>(); - orgsByName.put( org.getName(), orgs ); + orgsByName.put( org.getName().toLowerCase(), orgs ); } else { - duplicatesByName.put( org.getName(), orgs ); + duplicatesByName.put( org.getName().toLowerCase(), orgs ); } orgs.add( org ); @@ -310,7 +313,7 @@ public class DuplicateOrgRepair extends ToolBase { } } catch (Exception e) { - logger.error("Error getting orgs for user {}:{}", user.getName(), user.getId()); + logger.error("Error getting orgs for user {}:{}", user.getUsername(), user.getId()); logger.error("Stack trace is: ", e); } } @@ -338,24 +341,24 @@ public class DuplicateOrgRepair extends ToolBase { for (OrgUser user : orgUsers) { if (dryRun) { Object[] args = new Object[]{ - user.getName(), user.getId(), bestOrg.getName(), bestOrg.getId()}; + user.getUsername(), user.getId(), bestOrg.getName(), bestOrg.getId()}; logger.info( "Would add user {}:{} to org {}:{}", args ); args = new Object[]{ - user.getName(), user.getId(), org.getName(), org.getId()}; + user.getUsername(), user.getId(), org.getName(), org.getId()}; logger.info( "Would remove user {}:{} org {}:{}", args ); } else { try { manager.addUserToOrg( user, bestOrg ); } catch ( Exception e ) { Object[] args = new Object[]{ - user.getName(), user.getId(), bestOrg.getName(), bestOrg.getId()}; + user.getUsername(), user.getId(), bestOrg.getName(), bestOrg.getId()}; logger.error( "Error adding user {}:{} to org {}:{}", args ); } try { manager.removeUserFromOrg( user, org ); } catch ( Exception e ) { Object[] args = new Object[]{ - user.getName(), user.getId(), org.getName(), org.getId()}; + user.getUsername(), user.getId(), org.getName(), org.getId()}; logger.info( "Error removing user {}:{} org {}:{}", args ); } } @@ -428,270 +431,12 @@ public class DuplicateOrgRepair extends ToolBase { return oldest; } - - class RepairManager implements DuplicateOrgInterface { - - private boolean dryRun = true; - - @Override - public Observable<Org> getOrgs() throws Exception { - - return Observable.create( new Observable.OnSubscribe<Org>() { - - @Override - public void call(Subscriber<? super Org> subscriber) { - subscriber.onStart(); - try { - int count = 0; - - Query query = new Query(); - query.setLimit( MAX_ENTITY_FETCH ); - query.setResultsLevel( Results.Level.ALL_PROPERTIES ); - EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); - Results results = em.searchCollection( em.getApplicationRef(), "groups", query ); - - while (results.size() > 0) { - for (Entity orgEntity : results.getList()) { - - Org org = new Org( - orgEntity.getUuid(), - orgEntity.getProperty( "path" )+"", - orgEntity.getCreated() ); - org.sourceValue = orgEntity; - - subscriber.onNext( org ); - - if ( count++ % 1000 == 0 ) { - logger.info("Emitted {} orgs", count ); - } - - // logger.info( "org: {}, \"{}\", {}", new Object[]{ - // orgEntity.getProperty( "path" ), - // orgEntity.getUuid(), - // orgEntity.getCreated()} ); - } - if (results.getCursor() == null) { - break; - } - query.setCursor( results.getCursor() ); - results = em.searchCollection( em.getApplicationRef(), "groups", query ); - } - - } catch (Exception e) { - subscriber.onError( e ); - } - subscriber.onCompleted(); - } - } ); - } - - @Override - public Observable<OrgUser> getUsers() throws Exception { - - return Observable.create( new Observable.OnSubscribe<OrgUser>() { - - @Override - public void call(Subscriber<? super OrgUser> subscriber) { - subscriber.onStart(); - try { - int count = 0; - - Query query = new Query(); - query.setLimit( MAX_ENTITY_FETCH ); - query.setResultsLevel( Results.Level.ALL_PROPERTIES ); - EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); - Results results = em.searchCollection( em.getApplicationRef(), "users", query ); - - while (results.size() > 0) { - for (Entity entity : results.getList()) { - - OrgUser orgUser = new OrgUser( - entity.getUuid(), - entity.getProperty( "username" ) + "" ); - orgUser.sourceValue = entity; - - subscriber.onNext( orgUser ); - - if ( count++ % 1000 == 0 ) { - logger.info("Emitted {} users", count ); - } - - // logger.info( "org: {}, \"{}\", {}", new Object[]{ - // entity.getProperty( "path" ), - // entity.getUuid(), - // entity.getCreated()} ); - } - if (results.getCursor() == null) { - break; - } - query.setCursor( results.getCursor() ); - results = em.searchCollection( em.getApplicationRef(), "users", query ); - } - - } catch (Exception e) { - subscriber.onError( e ); - } - subscriber.onCompleted(); - } - } ); - } - - @Override - public Set<Org> getUsersOrgs(OrgUser user) throws Exception { - - Set<Org> ret = new HashSet<Org>(); - - Map<String, Object> orgData = managementService.getAdminUserOrganizationData( user.getId() ); - - Map<String, Object> orgs = (Map<String, Object>)orgData.get("organizations"); - for ( String orgName : orgs.keySet() ) { - - Map<String, Object> orgMap = (Map<String, Object>)orgs.get( orgName ); - Group group = managementService.getOrganizationProps( - UUID.fromString( orgMap.get( "uuid" ).toString() ) ); - - Org org = new Org( - group.getUuid(), - group.getPath(), - group.getCreated() - ); - ret.add(org); - } - - return ret; - } - - - @Override - public void removeOrg(Org keeper, Org duplicate) throws Exception { - - // rename org so that it is no longer a duplicate - EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); - em.delete( new SimpleEntityRef( "group", duplicate.getId() )); - logger.info("Deleted org {}:{}", new Object[] { duplicate.getName(), duplicate.getId() }); - - // fix the org name index - OrganizationInfo orgInfoKeeper = managementService.getOrganizationByUuid( keeper.getId() ); - try { - managementService.updateOrganizationUniqueIndex( orgInfoKeeper, duplicate.getId() ); - logger.info("Updated index for keeper {}:{} not dup {}", new Object[] { - orgInfoKeeper.getName(), orgInfoKeeper.getUuid(), duplicate.getId() }); - - } catch ( Exception e ) { - // if there are multiple duplicates this will fail for all but one of them. That's OK - logger.warn("Error repairing unique value keeper {} duplicate {}", - keeper.getId(), duplicate.getId()); - } - } - - - @Override - public Set<OrgUser> getOrgUsers(Org org) throws Exception { - - Set<OrgUser> ret = new HashSet<OrgUser>(); - - List<UserInfo> userInfos = managementService.getAdminUsersForOrganization( org.getId() ); - - for ( UserInfo userInfo : userInfos ) { - OrgUser orgUser = new OrgUser( userInfo.getUuid(), userInfo.getUsername() ); - ret.add(orgUser); - } - - return ret; - } - - - @Override - public void removeUserFromOrg(OrgUser user, Org org) throws Exception { - // forcefully remove admin user from org - managementService.removeAdminUserFromOrganization( user.getId(), org.getId(), true ); - logger.info("Removed user {}:{} from org {}:{}", new Object[] { - user.getName(), user.getId(), org.getName(), org.getId() }); - } - - - @Override - public void addUserToOrg(OrgUser user, Org org) throws Exception { - UserInfo userInfo = managementService.getAdminUserByUsername( user.getName() ); - OrganizationInfo orgInfo = managementService.getOrganizationByUuid( org.getId() ); - managementService.addAdminUserToOrganization( userInfo, orgInfo, false ); - logger.info("Added user {}:{} to org {}:{}", new Object[] { - user.getName(), user.getId(), org.getName(), org.getId() }); - } - - - @Override - public Set<UUID> getOrgApps(Org org) throws Exception { - BiMap<UUID, String> apps = managementService.getApplicationsForOrganization( org.getId() ); - return apps.keySet(); - } - - - @Override - public void removeAppFromOrg(UUID appId, Org org) throws Exception { - managementService.removeOrganizationApplication( org.getId(), appId ); - logger.info("Removed app {} from org {}:{}", new Object[] { - appId, org.getName(), org.getId() }); - } - - - @Override - public void addAppToOrg(UUID appId, Org org) throws Exception { - managementService.addApplicationToOrganization( org.getId(), appId ); - logger.info("Added app {} to org {}:{}", new Object[] { - appId, org.getName(), org.getId() }); - } - - - @Override - public void logDuplicates(Map<String, Set<Org>> duplicatesByName) { - for ( String orgName : duplicatesByName.keySet() ) { - Set<Org> orgs = duplicatesByName.get(orgName); - for ( Org org : orgs ) { - Entity orgEntity = (Entity)org.sourceValue; + static class DryRunManager extends UserOrgManager { - StringBuilder sb = new StringBuilder(); - sb.append(orgEntity.toString()).append(", "); - - try { - BiMap<UUID, String> apps = - managementService.getApplicationsForOrganization( orgEntity.getUuid() ); - String sep = ""; - for ( UUID uuid : apps.keySet() ) { - String appName = apps.get(uuid); - sb.append(appName).append(":").append(uuid).append(sep); - sep = ", "; - } - - } catch (Exception e) { - logger.error("Error getting applications for org {}:{}", org.getName(), org.getId() ); - } - - logger.info(sb.toString()); - } - } + public DryRunManager(EntityManagerFactory emf, ManagementService managementService ) { + super(emf, managementService); } - - - @Override - public Org getOrg(UUID uuid) throws Exception { - - EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); - Entity entity = em.get( uuid ); - - Org org = new Org( - entity.getUuid(), - entity.getProperty( "path" )+"", - entity.getCreated() ); - org.sourceValue = entity; - - return org; - } - } - - - class DryRunManager extends RepairManager { @Override public void removeUserFromOrg(OrgUser user, Org org) throws Exception { http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java new file mode 100644 index 0000000..b756f32 --- /dev/null +++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.usergrid.tools; + +import rx.Observable; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + + +/** + * Mockable abstraction of user-org management. + */ +interface UserOrgInterface { + + Observable<Org> getOrgs() throws Exception; + + Observable<OrgUser> getUsers() throws Exception; + + Set<Org> getUsersOrgs(OrgUser user) throws Exception; + + Set<OrgUser> getOrgUsers(Org org ) throws Exception; + + void removeOrg(Org keeper, Org duplicate) throws Exception; + + void removeUserFromOrg( OrgUser user, Org org ) throws Exception; + + void addUserToOrg( OrgUser user, Org org ) throws Exception; + + Set<UUID> getOrgApps(Org org) throws Exception; + + void removeAppFromOrg( UUID appId, Org org ) throws Exception; + + void addAppToOrg( UUID appId, Org org ) throws Exception; + + void logDuplicates(Map<String, Set<Org>> duplicatesByName); + + Org getOrg(UUID id ) throws Exception; + + OrgUser getOrgUser(UUID id ) throws Exception; + + OrgUser lookupOrgUserByUsername( String username ) throws Exception; + + OrgUser lookupOrgUserByEmail( String email ) throws Exception; + + void removeOrgUser( OrgUser orgUser ) throws Exception; + + void updateOrgUser(OrgUser targetUserEntity) throws Exception; + + void setOrgUserName(OrgUser other, String newUserName) throws Exception; + + class Org implements Comparable<Org> { + private UUID id; + private String name; + private long created; + public Object sourceValue; + + public Org( UUID id, String name, long created) { + this.id = id; + this.name = name; + this.created = created; + this.created = System.currentTimeMillis(); + } + + public Org( UUID id, String name) { + this( id, name, System.currentTimeMillis()); + } + + @Override + public boolean equals( Object o ) { + if ( o instanceof Org ) { + Org other = (Org)o; + return getId().equals( other.getId() ); + } + return false; + } + + @Override + public int compareTo(Org o) { + return Long.compare( this.created, o.created ); + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public long getCreated() { + return created; + } + } + + class OrgUser implements Comparable<OrgUser> { + private UUID id; + private String username; + private String email; + private long created; + public Object sourceValue; + + public OrgUser( UUID id, String name, String email, long created ) { + this.id = id; + this.username = name; + this.email = email; + this.created = created; + } + + public OrgUser( UUID id, String name, String email ) { + this( id, name, email, System.currentTimeMillis()); + } + + public UUID getId() { + return id; + } + + public String getEmail() { + return email; + } + + public String getUsername() { + return username; + } + + public void setUsername( String username ) { + this.username = username; + } + + public long getCreated() { + return created; + } + + @Override + public int compareTo(OrgUser o) { + return Long.compare( this.created, o.created ); + } + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java new file mode 100644 index 0000000..b18b8ca --- /dev/null +++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java @@ -0,0 +1,364 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.usergrid.tools; + +import com.google.common.collect.BiMap; +import org.apache.usergrid.management.ManagementService; +import org.apache.usergrid.management.OrganizationInfo; +import org.apache.usergrid.management.UserInfo; +import org.apache.usergrid.persistence.*; +import org.apache.usergrid.persistence.cassandra.CassandraService; +import org.apache.usergrid.persistence.entities.Group; +import org.apache.usergrid.persistence.entities.User; +import org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import rx.Observable; +import rx.Subscriber; + +import java.util.*; + + +class UserOrgManager implements UserOrgInterface { + + static final Logger logger = LoggerFactory.getLogger( UserOrgManager.class ); + + EntityManagerFactory emf; + ManagementService managementService; + + public UserOrgManager(EntityManagerFactory emf, ManagementService managementService) { + this.emf = emf; + this.managementService = managementService; + } + + @Override + public Observable<Org> getOrgs() throws Exception { + + return Observable.create( new Observable.OnSubscribe<Org>() { + + @Override + public void call(Subscriber<? super Org> subscriber) { + subscriber.onStart(); + try { + int count = 0; + + Query query = new Query(); + query.setLimit( ToolBase.MAX_ENTITY_FETCH ); + query.setResultsLevel( Results.Level.ALL_PROPERTIES ); + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + Results results = em.searchCollection( em.getApplicationRef(), "groups", query ); + + while (results.size() > 0) { + for (Entity orgEntity : results.getList()) { + + Org org = new Org( + orgEntity.getUuid(), + orgEntity.getProperty( "path" ) + "", + orgEntity.getCreated() ); + org.sourceValue = orgEntity; + + subscriber.onNext( org ); + + if (count++ % 1000 == 0) { + logger.info( "Emitted {} orgs", count ); + } + + } + if (results.getCursor() == null) { + break; + } + query.setCursor( results.getCursor() ); + results = em.searchCollection( em.getApplicationRef(), "groups", query ); + } + + } catch (Exception e) { + subscriber.onError( e ); + } + subscriber.onCompleted(); + } + } ); + } + + @Override + public Observable<OrgUser> getUsers() throws Exception { + + return Observable.create( new Observable.OnSubscribe<OrgUser>() { + + @Override + public void call(Subscriber<? super OrgUser> subscriber) { + subscriber.onStart(); + try { + int count = 0; + + Query query = new Query(); + query.setLimit( ToolBase.MAX_ENTITY_FETCH ); + query.setResultsLevel( Results.Level.ALL_PROPERTIES ); + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + Results results = em.searchCollection( em.getApplicationRef(), "users", query ); + + while (results.size() > 0) { + for (Entity entity : results.getList()) { + + OrgUser orgUser = new OrgUser( + entity.getUuid(), + entity.getProperty( "username" ) + "", + entity.getProperty( "email" ) + "", + entity.getCreated() + ); + orgUser.sourceValue = entity; + + subscriber.onNext( orgUser ); + + if (count++ % 1000 == 0) { + logger.info( "Emitted {} users", count ); + } + } + if (results.getCursor() == null) { + break; + } + query.setCursor( results.getCursor() ); + results = em.searchCollection( em.getApplicationRef(), "users", query ); + } + + } catch (Exception e) { + subscriber.onError( e ); + } + subscriber.onCompleted(); + } + } ); + } + + @Override + public Set<Org> getUsersOrgs(OrgUser user) throws Exception { + + Set<Org> ret = new HashSet<Org>(); + + Map<String, Object> orgData = managementService.getAdminUserOrganizationData( user.getId() ); + + Map<String, Object> orgs = (Map<String, Object>) orgData.get( "organizations" ); + for (String orgName : orgs.keySet()) { + + Map<String, Object> orgMap = (Map<String, Object>) orgs.get( orgName ); + Group group = managementService.getOrganizationProps( + UUID.fromString( orgMap.get( "uuid" ).toString() ) ); + + Org org = new Org( + group.getUuid(), + group.getPath(), + group.getCreated() + ); + ret.add( org ); + } + + return ret; + } + + + @Override + public void removeOrg(Org keeper, Org duplicate) throws Exception { + + // rename org so that it is no longer a duplicate + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + em.delete( new SimpleEntityRef( "group", duplicate.getId() ) ); + logger.info( "Deleted org {}:{}", new Object[]{duplicate.getName(), duplicate.getId()} ); + + // fix the org name index + OrganizationInfo orgInfoKeeper = managementService.getOrganizationByUuid( keeper.getId() ); + try { + managementService.updateOrganizationUniqueIndex( orgInfoKeeper, duplicate.getId() ); + logger.info( "Updated index for keeper {}:{} not dup {}", new Object[]{ + orgInfoKeeper.getName(), orgInfoKeeper.getUuid(), duplicate.getId()} ); + + } catch (Exception e) { + // if there are multiple duplicates this will fail for all but one of them. That's OK + logger.warn( "Error repairing unique value keeper {} duplicate {}", + keeper.getId(), duplicate.getId() ); + } + } + + + @Override + public Set<OrgUser> getOrgUsers(Org org) throws Exception { + + Set<OrgUser> ret = new HashSet<OrgUser>(); + + List<UserInfo> userInfos = managementService.getAdminUsersForOrganization( org.getId() ); + + for (UserInfo userInfo : userInfos) { + OrgUser orgUser = new OrgUser( userInfo.getUuid(), userInfo.getUsername(), userInfo.getEmail() ); + ret.add( orgUser ); + } + + return ret; + } + + + @Override + public void removeUserFromOrg(OrgUser user, Org org) throws Exception { + // forcefully remove admin user from org + managementService.removeAdminUserFromOrganization( user.getId(), org.getId(), true ); + logger.info( "Removed user {}:{} from org {}:{}", new Object[]{ + user.getUsername(), user.getId(), org.getName(), org.getId()} ); + } + + + @Override + public void addUserToOrg(OrgUser user, Org org) throws Exception { + UserInfo userInfo = managementService.getAdminUserByUsername( user.getUsername() ); + OrganizationInfo orgInfo = managementService.getOrganizationByUuid( org.getId() ); + managementService.addAdminUserToOrganization( userInfo, orgInfo, false ); + logger.info( "Added user {}:{} to org {}:{}", new Object[]{ + user.getUsername(), user.getId(), org.getName(), org.getId()} ); + } + + + @Override + public Set<UUID> getOrgApps(Org org) throws Exception { + BiMap<UUID, String> apps = managementService.getApplicationsForOrganization( org.getId() ); + return apps.keySet(); + } + + + @Override + public void removeAppFromOrg(UUID appId, Org org) throws Exception { + managementService.removeOrganizationApplication( org.getId(), appId ); + logger.info( "Removed app {} from org {}:{}", new Object[]{ + appId, org.getName(), org.getId()} ); + } + + + @Override + public void addAppToOrg(UUID appId, Org org) throws Exception { + managementService.addApplicationToOrganization( org.getId(), appId ); + logger.info( "Added app {} to org {}:{}", new Object[]{ + appId, org.getName(), org.getId()} ); + } + + + @Override + public void logDuplicates(Map<String, Set<Org>> duplicatesByName) { + + for (String orgName : duplicatesByName.keySet()) { + Set<Org> orgs = duplicatesByName.get( orgName ); + for (Org org : orgs) { + Entity orgEntity = (Entity) org.sourceValue; + + StringBuilder sb = new StringBuilder(); + sb.append( orgEntity.toString() ).append( ", " ); + + try { + BiMap<UUID, String> apps = + managementService.getApplicationsForOrganization( orgEntity.getUuid() ); + String sep = ""; + for (UUID uuid : apps.keySet()) { + String appName = apps.get( uuid ); + sb.append( appName ).append( ":" ).append( uuid ).append( sep ); + sep = ", "; + } + + } catch (Exception e) { + logger.error( "Error getting applications for org {}:{}", org.getName(), org.getId() ); + } + + logger.info( sb.toString() ); + } + } + } + + + @Override + public Org getOrg(UUID uuid) throws Exception { + + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + Entity entity = em.get( uuid ); + + Org org = new Org( + entity.getUuid(), + entity.getProperty( "path" ) + "", + entity.getCreated() ); + org.sourceValue = entity; + + return org; + } + + @Override + public OrgUser getOrgUser(UUID uuid) throws Exception { + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + Entity entity = em.get( uuid ); + + OrgUser user = new OrgUser( + entity.getUuid(), + entity.getType(), + entity.getProperty("email")+"", + entity.getCreated() + ); + + return user; + } + + + @Override + public OrgUser lookupOrgUserByUsername(String username) throws Exception { + UserInfo info = managementService.getAdminUserByUsername( username ); + return info == null ? null : getOrgUser( info.getUuid() ); + } + + + @Override + public OrgUser lookupOrgUserByEmail(String email) throws Exception { + UserInfo info = managementService.getAdminUserByEmail( email ); + return info == null ? null : getOrgUser( info.getUuid() ); + } + + + @Override + public void removeOrgUser(OrgUser orgUser) throws Exception { + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + em.delete( new SimpleEntityRef( "user", orgUser.getId() )); + } + + + @Override + public void updateOrgUser(OrgUser targetUserEntity ) throws Exception { + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + User user = em.get(targetUserEntity.getId(), User.class); + user.setUsername( targetUserEntity.getUsername() ); + user.setEmail( targetUserEntity.getEmail() ); + em.update( user ); + } + + + @Override + public void setOrgUserName(OrgUser other, String newUserName ) throws Exception { + + EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID ); + + logger.info( "Setting username to {} for user with username {} and id {}", new Object[] { + newUserName, other.getUsername(), other.getId() + } ); + + try { + em.setProperty( new SimpleEntityRef( "user", other.getId() ), "username", newUserName, true ); + } + catch ( DuplicateUniquePropertyExistsException e ) { + logger.warn( "More than 1 user has the username of {}. Setting the username to their username+UUID as a " + + "fallback", newUserName ); + + setOrgUserName( other, String.format( "%s-%s", other.getUsername(), other.getId() ) ); + } + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java new file mode 100644 index 0000000..1ba3766 --- /dev/null +++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.usergrid.tools; + + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.usergrid.ServiceITSetup; +import org.apache.usergrid.ServiceITSetupImpl; +import org.apache.usergrid.ServiceITSuite; +import org.apache.usergrid.management.OrganizationOwnerInfo; +import org.junit.ClassRule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; + +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertEquals; + + +/** + * Test duplicate org repair. + */ +public class DuplicateAdminUserRepairTest { + + static final Logger logger = LoggerFactory.getLogger( DuplicateAdminUserRepairTest.class ); + + @ClassRule + public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource ); + + + /** + * Test tool logic with mock manager that returns duplicates + */ + @org.junit.Test + public void testMockWithDups() throws Exception { + + DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(); + MockManager mockManager = new MockManager(); + dor.manager = mockManager; + dor.testing = true; + + // the MockManager creates 2 pairs of duplicate orgs + assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() )); + assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() )); + assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() )); + assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() )); + + // verify that correct users indexed + usersIndexed( mockManager ); + + dor.startTool( new String[] {}, false ); // false means do not call System.exit() + + // verify that correct users indexed + usersIndexed( mockManager ); + + // verify that duplicate users are gone (the "a" users were the first ones created) + assertNull( "must remove user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() )); + assertNull( "must remove user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() )); + + // and keepers survived + assertNotNull( "user1_b survived", mockManager.getOrgUser( mockManager.user1_b.getId() )); + assertNotNull( "user2_b survived", mockManager.getOrgUser( mockManager.user2_b.getId() )); + } + + + private void usersIndexed( MockManager mockManager ) { + assertEquals("user1_b is in the index", + mockManager.lookupOrgUserByUsername(mockManager.user1_b.getUsername()).getId(), + mockManager.user1_b.getId() ); + + assertEquals("user1_b is in the index", + mockManager.lookupOrgUserByEmail(mockManager.user1_b.getEmail()).getId(), + mockManager.user1_b.getId() ); + + assertEquals("user2_b is in the index", + mockManager.lookupOrgUserByUsername(mockManager.user2_b.getUsername()).getId(), + mockManager.user2_b.getId() ); + + assertEquals("user2_b is in the index", + mockManager.lookupOrgUserByEmail(mockManager.user2_b.getEmail()).getId(), + mockManager.user2_b.getId() ); + } + + + @org.junit.Test + public void testDryRun() throws Exception { + + DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(); + MockManager mockManager = new MockManager(); + dor.manager = mockManager; + dor.testing = true; + + // the MockManager creates 2 pairs of duplicate orgs + assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() )); + assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() )); + assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() )); + assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() )); + + // verify that correct users indexed + usersIndexed( mockManager ); + + dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit() + + // verify that correct users indexed + usersIndexed( mockManager ); + + // insure nothng was deleted by dry-run + assertNotNull( "dryrun should not delete user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() )); + assertNotNull( "dryrun should not delete user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() )); + assertNotNull( "dryrun should not delete user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() )); + assertNotNull( "dryrun should not delete user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() )); + } + + + /** + * Smoke test: does "real" manager run without throwing exceptions? + */ + @org.junit.Test + public void testManagerNoDups() throws Exception { + + // create two orgs each with owning user + + final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 ); + final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization( + "org_" + random1, "user_" + random1, "user_" + random1, + "user_" + random1 + "@example.com", "password" ); + + final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 ); + final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization( + "org_" + random2, "user_" + random2, "user_" + random2, + "user_" + random2 + "@example.com", "password" ); + + // Add user1 to org2 + + setup.getMgmtSvc().addAdminUserToOrganization( + orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false ); + + DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(); + + dor.startTool( new String[]{}, false ); // false means do not call System.exit() + + dor.startTool( new String[]{"dryrun", "true"}, false ); // false means do not call System.exit() + + assertTrue( true ); // we're happy if we get to this point + } + + + @org.junit.Test + public void testManagerMethods() throws Exception { + + // create two orgs each with owning user + + final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 ); + final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization( + "org_" + random1, "user_" + random1, "user_" + random1, + "user_" + random1 + "@example.com", "password" ); + + final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 ); + final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization( + "org_" + random2, "user_" + random2, "user_" + random2, + "user_" + random2 + "@example.com", "password" ); + + DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(setup.getEmf(), setup.getMgmtSvc()); + dor.manager = dor.createNewRepairManager(); // test the real manager + + // start the tool so that Spring, Cassandra, etc/ gets initialized + dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit() + + assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); + assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() )); + + dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); + dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() )); + + assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); + assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() )); + } + + + static class MockManager extends MockUserOrgManager { + + OrgUser user1_a; + OrgUser user1_b; + OrgUser user2_a; + OrgUser user2_b; + + public MockManager() throws Exception { + + super(1); // ask parent to create one pair of duplicate orgs + + Org org = orgsById.values().iterator().next(); + + String sfx = RandomStringUtils.randomAlphanumeric(10); + + // user1 a and b have duplicate usernames AND DUPLICATE EMAIL ADDRESSES + + user1_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" ); + addUserToOrg( user1_a, org ); + pause(100); + + user1_b = createOrgUser( UUID.randomUUID(), "Username_"+sfx, "Username_"+sfx+"@example.com" ); + addUserToOrg( user1_b, org ); + pause(100); + + // user2 a and b have duplicate usernames AND DIFFERENT EMAIL ADDRESSES + + user2_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" ); + addUserToOrg( user2_a, org ); + pause(100); + + user2_b = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" ); + addUserToOrg( user2_b, org ); + pause(100); + + } + + } + +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java ---------------------------------------------------------------------- diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java index 8726a33..1cb8a9e 100644 --- a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java +++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java @@ -32,8 +32,8 @@ import rx.functions.Action1; import java.util.*; import static junit.framework.Assert.assertTrue; -import static org.apache.usergrid.tools.DuplicateOrgInterface.Org; -import static org.apache.usergrid.tools.DuplicateOrgInterface.OrgUser; +import static org.apache.usergrid.tools.UserOrgInterface.Org; +import static org.apache.usergrid.tools.UserOrgInterface.OrgUser; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -43,11 +43,11 @@ import static org.junit.Assert.fail; */ public class DuplicateOrgRepairTest { static final Logger logger = LoggerFactory.getLogger( DuplicateOrgRepairTest.class ); - + @ClassRule public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource ); - + /** * Test tool logic with mock manager that returns duplicates */ @@ -57,34 +57,34 @@ public class DuplicateOrgRepairTest { int numOrgs = 10; // create 10 orgs and a dup for each final DuplicateOrgRepair dor = new DuplicateOrgRepair(); - - dor.manager = new Manager( numOrgs ); - assertEquals( "must start with dups", - 2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single()); + dor.manager = new MockUserOrgManager( numOrgs ); - dor.startTool( new String[] {}, false ); // false means do not call System.exit() + assertEquals( "must start with dups", + 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); - assertEquals( "must remove dups", - numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single()); + dor.startTool( new String[]{}, false ); // false means do not call System.exit() + + assertEquals( "must remove dups", + numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); checkOrgsDeduped( dor ); } @org.junit.Test - public void testMockWithOneDupoDryRun() throws Exception { + public void testMockWithOneDupsDryRun() throws Exception { int numOrgs = 1; // create 1 org and a dup DuplicateOrgRepair dor = new DuplicateOrgRepair(); dor.testing = true; - dor.manager = new Manager( numOrgs ); + dor.manager = new MockUserOrgManager( numOrgs ); assertEquals( "must start with dups", 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); - Iterator<Org> orgIter = ((Manager) dor.manager).usersByOrg.keySet().iterator(); + Iterator<Org> orgIter = ((MockUserOrgManager) dor.manager).usersByOrg.keySet().iterator(); Org org1 = orgIter.next(); Org org2 = orgIter.next(); dor.startTool( new String[]{ @@ -96,8 +96,8 @@ public class DuplicateOrgRepairTest { assertEquals( "dry-run should not remove dups", 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); } - - + + @org.junit.Test public void testMockWithOneDup() throws Exception { @@ -105,40 +105,40 @@ public class DuplicateOrgRepairTest { DuplicateOrgRepair dor = new DuplicateOrgRepair(); dor.testing = true; - dor.manager = new Manager( numOrgs ); + dor.manager = new MockUserOrgManager( numOrgs ); - Iterator<Org> orgIter = ((Manager)dor.manager).usersByOrg.keySet().iterator(); + Iterator<Org> orgIter = ((MockUserOrgManager) dor.manager).usersByOrg.keySet().iterator(); Org org1 = orgIter.next(); Org org2 = orgIter.next(); - dor.startTool( new String[] { - "-org1", org1.getId()+"", - "-org2", org2.getId()+"", + dor.startTool( new String[]{ + "-org1", org1.getId() + "", + "-org2", org2.getId() + "", }, false ); // false means do not call System.exit() assertEquals( "must remove dups", - numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single()); + numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); checkOrgsDeduped( dor ); } - + private void checkOrgsDeduped(final DuplicateOrgRepair dor) throws Exception { dor.manager.getOrgs().doOnNext( new Action1<Org>() { @Override public void call(Org org) { try { - assertEquals("remaining orgs should have right number of users", - 3, dor.manager.getOrgUsers(org).size()); + assertEquals( "remaining orgs should have right number of users", + 3, dor.manager.getOrgUsers( org ).size() ); - assertEquals("remaining orgs should have right number of apps", - 3, dor.manager.getOrgApps(org).size()); + assertEquals( "remaining orgs should have right number of apps", + 3, dor.manager.getOrgApps( org ).size() ); } catch (Exception e) { - logger.error("Error counting apps or users: " + e.getMessage(), e); - fail("Error counting apps or users"); + logger.error( "Error counting apps or users: " + e.getMessage(), e ); + fail( "Error counting apps or users" ); } } - }).toBlocking().lastOrDefault( null ); + } ).toBlocking().lastOrDefault( null ); } @@ -148,18 +148,18 @@ public class DuplicateOrgRepairTest { int numOrgs = 10; // create 10 orgs and a dup for each DuplicateOrgRepair dor = new DuplicateOrgRepair(); - dor.manager = new Manager( numOrgs ); + dor.manager = new MockUserOrgManager( numOrgs ); - assertEquals( "must start with dups", - 2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single()); + assertEquals( "must start with dups", + 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); - dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit() + dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit() - assertEquals( "must detect right number of dups", + assertEquals( "must detect right number of dups", numOrgs, dor.duplicatesByName.keySet().size() ); - - assertEquals( "dryrun must not remove dups", - 2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single()); + + assertEquals( "dryrun must not remove dups", + 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() ); } @@ -185,17 +185,17 @@ public class DuplicateOrgRepairTest { setup.getMgmtSvc().addAdminUserToOrganization( orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false ); - + DuplicateOrgRepair dor = new DuplicateOrgRepair(); - - dor.startTool( new String[] {}, false ); // false means do not call System.exit() - - dor.startTool( new String[] { "dryrun", "true" }, false ); // false means do not call System.exit() - assertTrue(true); // we're happy if we get to this point + dor.startTool( new String[]{}, false ); // false means do not call System.exit() + + dor.startTool( new String[]{"dryrun", "true"}, false ); // false means do not call System.exit() + + assertTrue( true ); // we're happy if we get to this point } - + @org.junit.Test public void testManagerAddUserToOrg() throws Exception { @@ -211,43 +211,41 @@ public class DuplicateOrgRepairTest { "org_" + random2, "user_" + random2, "user_" + random2, "user_" + random2 + "@example.com", "password" ); - DuplicateOrgRepair dor = new DuplicateOrgRepair(); + DuplicateOrgRepair dor = new DuplicateOrgRepair(setup.getEmf(), setup.getMgmtSvc()); dor.manager = dor.createNewRepairManager(); // test the real manager - // start the tool so thaht Spring, Cassandra, etc/ gets initialized - dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit() - + // start the tool so that Spring, Cassandra, etc/ gets initialized + dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit() + Org org1 = new Org( - orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L); + orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L ); Org org2 = new Org( - orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L); + orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L ); OrgUser user1 = new OrgUser( - orgOwnerInfo1.getOwner().getUuid(), orgOwnerInfo1.getOwner().getUsername()); - - OrgUser user2 = new OrgUser( - orgOwnerInfo2.getOwner().getUuid(), orgOwnerInfo2.getOwner().getUsername()); + orgOwnerInfo1.getOwner().getUuid(), + orgOwnerInfo1.getOwner().getUsername(), + orgOwnerInfo1.getOwner().getEmail() ); - assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size()); - assertEquals( 1, dor.manager.getOrgUsers( org1 ).size()); - assertEquals( 1, dor.manager.getOrgUsers( org2 ).size()); + assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size() ); + assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() ); + assertEquals( 1, dor.manager.getOrgUsers( org2 ).size() ); dor.manager.addUserToOrg( user1, org2 ); - assertEquals( 2, dor.manager.getUsersOrgs( user1 ).size()); + assertEquals( 2, dor.manager.getUsersOrgs( user1 ).size() ); assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() ); assertEquals( 2, dor.manager.getOrgUsers( org2 ).size() ); dor.manager.removeUserFromOrg( user1, org2 ); - - assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size()); - assertEquals( 1, dor.manager.getOrgUsers( org1 ).size()); + + assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size() ); + assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() ); assertEquals( 1, dor.manager.getOrgUsers( org2 ).size() ); } - @org.junit.Test public void testManagerAddAppToOrg() throws Exception { @@ -255,38 +253,38 @@ public class DuplicateOrgRepairTest { final String random1 = RandomStringUtils.randomAlphanumeric( 10 ); final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization( - "org_" + random1, "user_" + random1, "user_" + random1, - "user_" + random1 + "@example.com", "password" ); + "org_" + random1, "user_" + random1, "user_" + random1, + "user_" + random1 + "@example.com", "password" ); final String random2 = RandomStringUtils.randomAlphanumeric( 10 ); final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization( - "org_" + random2, "user_" + random2, "user_" + random2, - "user_" + random2 + "@example.com", "password" ); + "org_" + random2, "user_" + random2, "user_" + random2, + "user_" + random2 + "@example.com", "password" ); // give org1 two apps - + ApplicationInfo app11 = setup.getMgmtSvc().createApplication( orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) ); - ApplicationInfo app12= setup.getMgmtSvc().createApplication( - orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 )); + ApplicationInfo app12 = setup.getMgmtSvc().createApplication( + orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) ); // give org2 one app - + ApplicationInfo app21 = setup.getMgmtSvc().createApplication( - orgOwnerInfo2.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 )); + orgOwnerInfo2.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) ); - DuplicateOrgRepair dor = new DuplicateOrgRepair(); + DuplicateOrgRepair dor = new DuplicateOrgRepair(setup.getEmf(), setup.getMgmtSvc()); dor.manager = dor.createNewRepairManager(); // test the real manager // start the tool so that Spring, Cassandra, etc/ gets initialized - dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit() + dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit() Org org1 = new Org( - orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L); + orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L ); Org org2 = new Org( - orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L); + orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L ); assertEquals( 2, dor.manager.getOrgApps( org1 ).size() ); assertEquals( 1, dor.manager.getOrgApps( org2 ).size() ); @@ -297,176 +295,10 @@ public class DuplicateOrgRepairTest { assertEquals( 1, dor.manager.getOrgApps( org2 ).size() ); dor.manager.addAppToOrg( app12.getId(), org2 ); - + assertEquals( 1, dor.manager.getOrgApps( org1 ).size() ); assertEquals( 2, dor.manager.getOrgApps( org2 ).size() ); } - - - /** - * Mock manager implementation for mockTesting. - */ - class Manager implements DuplicateOrgInterface { - - Set<Org> orgs; - - Set<OrgUser> orgUsers; - - Map<Org, Set<OrgUser>> usersByOrg = new HashMap<Org, Set<OrgUser>>(); - - Map<OrgUser, Set<Org>> orgsByUser = new HashMap<OrgUser, Set<Org>>(); - - Map<Org, Set<UUID>> appsByOrg = new HashMap<Org, Set<UUID>>(); - - Map<UUID, Org> orgsById = new HashMap<UUID, Org>(); - - - /** - * Populate manager with orgs and users. - * Will create a number of orgs and a duplicate for each. - * - * @param numOrgs One half of the number of orgs to create. - */ - public Manager(int numOrgs) { - - for (int i = 0; i < numOrgs; i++) { - - // each org name is duplicated once another org created 20 ms apart - - Org org1 = new Org( UUID.randomUUID(), "org_" + i, System.currentTimeMillis() ); - try { - Thread.sleep( 100 ); - } catch (InterruptedException intentionallyIgnored) { - } - Org org2 = new Org( UUID.randomUUID(), "org_" + i, System.currentTimeMillis() ); - - orgsById.put( org1.getId(), org1 ); - orgsById.put( org2.getId(), org2 ); - - OrgUser usera = new OrgUser( UUID.randomUUID(), "user_" + i + "_a" ); - OrgUser userb = new OrgUser( UUID.randomUUID(), "user_" + i + "_b" ); - OrgUser userc = new OrgUser( UUID.randomUUID(), "user_" + i + "_c" ); - - // add users to orgs - - Set<OrgUser> org1Users = new HashSet<OrgUser>(); - org1Users.add( usera ); - org1Users.add( userb ); - usersByOrg.put( org1, org1Users ); - - Set<OrgUser> org2Users = new HashSet<OrgUser>(); - org2Users.add( userc ); - usersByOrg.put( org2, org2Users ); - - // add orgs to users - - Set<Org> useraOrgs = new HashSet<Org>(); - useraOrgs.add( org1 ); - orgsByUser.put( usera, useraOrgs ); - - Set<Org> userbOrgs = new HashSet<Org>(); - userbOrgs.add( org1 ); - orgsByUser.put( userb, userbOrgs ); - Set<Org> usercOrgs = new HashSet<Org>(); - usercOrgs.add( org2 ); - orgsByUser.put( userc, usercOrgs ); +} - // add some apps to the orgs - - Set<UUID> org1apps = new HashSet<UUID>(); - org1apps.add( UUID.randomUUID() ); - org1apps.add( UUID.randomUUID() ); - appsByOrg.put( org1, org1apps ); - - Set<UUID> org2apps = new HashSet<UUID>(); - org2apps.add( UUID.randomUUID() ); - appsByOrg.put( org2, org2apps ); - } - } - - - @Override - public Observable<Org> getOrgs() throws Exception { - return Observable.from( usersByOrg.keySet() ); - } - - @Override - public Observable<OrgUser> getUsers() throws Exception { - return Observable.from( orgsByUser.keySet() ); - } - - @Override - public Set<Org> getUsersOrgs(OrgUser user) { - return orgsByUser.get( user ); - } - - @Override - public void removeOrg(Org keeper, Org duplicate) throws Exception { - Set<OrgUser> users = usersByOrg.get( duplicate ); - for (OrgUser user : users) { - Set<Org> userOrgs = orgsByUser.get( user ); - userOrgs.remove( duplicate ); - } - usersByOrg.remove( duplicate ); - } - - @Override - public Set<OrgUser> getOrgUsers(Org org) throws Exception { - return usersByOrg.get( org ); - } - - @Override - public void removeUserFromOrg(OrgUser user, Org org) throws Exception { - - Set<OrgUser> orgUsers = usersByOrg.get( org ); - orgUsers.remove( user ); - - Set<Org> usersOrgs = orgsByUser.get( user ); - usersOrgs.remove( org ); - } - - @Override - public void addUserToOrg(OrgUser user, Org org) throws Exception { - - Set<Org> usersOrgs = orgsByUser.get( user ); - usersOrgs.add( org ); - - Set<OrgUser> orgsUsers = usersByOrg.get( org ); - orgsUsers.add( user ); - } - - @Override - public Set<UUID> getOrgApps(Org org) { - return appsByOrg.get( org ); - } - - @Override - public void removeAppFromOrg(UUID appId, Org org) throws Exception { - Set<UUID> apps = appsByOrg.get( org ); - apps.remove( appId ); - } - - @Override - public void addAppToOrg(UUID appId, Org org) throws Exception { - Set<UUID> apps = appsByOrg.get( org ); - apps.add(appId); - } - - @Override - public void logDuplicates(Map<String, Set<Org>> duplicatesByName) { - - for (String orgName : duplicatesByName.keySet()) { - Set<Org> orgs = duplicatesByName.get( orgName ); - for (Org org : orgs) { - logger.info( "Duplicate org {}:{}", orgName, org.getId() ); - } - } - } - - @Override - public Org getOrg(UUID uuid) throws Exception { - return orgsById.get(uuid); - } - } -}
