]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5960 remove CSV from db USERS.SCM_ACCOUNTS
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Sat, 7 Feb 2015 10:23:20 +0000 (11:23 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 10 Feb 2015 13:27:45 +0000 (14:27 +0100)
27 files changed:
server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java
server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java
server/sonar-server/src/main/java/org/sonar/server/user/db/UserDao.java
server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java
server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsersTest.java
server/sonar-server/src/test/java/org/sonar/server/user/UserServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java
server/sonar-server/src/test/java/org/sonar/server/user/db/UserDaoTest.java
server/sonar-server/src/test/resources/org/sonar/server/user/UserCreatorTest/fail_to_create_user_if_already_exists.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_reactivate_user_if_not_disabled.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml
server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml
server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_by_login.xml
server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account.xml
server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account_return_many_results_when_same_email_is_used_by_many_users.xml
server/sonar-server/src/test/resources/org/sonar/server/user/index/UserIndexerTest/index.xml
server/sonar-server/src/test/resources/org/sonar/server/user/index/UserResultSetIteratorTest/shared.xml
sonar-core/src/main/java/org/sonar/core/user/UserDto.java
sonar-core/src/main/java/org/sonar/core/user/UserMapper.java
sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml
sonar-core/src/test/java/org/sonar/core/user/UserDtoTest.java [new file with mode: 0644]

index b5ed489b3506eb802fb4c012e236e221d4893689..e6ebec997f6dfdb7127f829416c4a3fd58365426 100644 (file)
 
 package org.sonar.server.db.migrations.v51;
 
-import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
 import org.sonar.api.utils.System2;
-import org.sonar.api.utils.text.CsvWriter;
 import org.sonar.core.persistence.Database;
 import org.sonar.server.db.migrations.BaseDataChange;
 import org.sonar.server.db.migrations.Select;
@@ -32,7 +31,8 @@ import org.sonar.server.db.migrations.Upsert;
 import org.sonar.server.db.migrations.UpsertImpl;
 import org.sonar.server.util.ProgressLogger;
 
-import java.io.StringWriter;
+import javax.annotation.CheckForNull;
+
 import java.sql.SQLException;
 import java.util.Collection;
 import java.util.List;
@@ -42,6 +42,8 @@ import static com.google.common.collect.Lists.newArrayList;
 
 public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
 
+  private static final char SCM_ACCOUNTS_SEPARATOR = '\n';
+
   private final System2 system;
   private final AtomicLong counter = new AtomicLong(0L);
 
@@ -60,13 +62,13 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
       final Multimap<Long, String> authorsByPersonId = ArrayListMultimap.create();
       context.prepareSelect("SELECT a.person_id, a.login FROM authors a," +
         "  (SELECT person_id, COUNT(*) AS nb FROM authors GROUP BY person_id HAVING COUNT(*) > 1) group_by_person" +
-          "     WHERE a.person_id = group_by_person.person_id "
-      ).scroll(new Select.RowHandler() {
-        @Override
-        public void handle(Select.Row row) throws SQLException {
-          authorsByPersonId.put(row.getLong(1), row.getString(2));
-        }
-      });
+        "     WHERE a.person_id = group_by_person.person_id "
+        ).scroll(new Select.RowHandler() {
+          @Override
+          public void handle(Select.Row row) throws SQLException {
+            authorsByPersonId.put(row.getLong(1), row.getString(2));
+          }
+        });
 
       Upsert update = context.prepareUpsert("UPDATE users SET scm_accounts = ?, updated_at = ? WHERE id = ?");
       for (Long personId : authorsByPersonId.keySet()) {
@@ -82,7 +84,7 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
           }
           if (!authors.isEmpty()) {
             update
-              .setString(1, convertScmAccountsToCsv(authors))
+              .setString(1, encodeScmAccounts(authors))
               .setLong(2, now)
               .setLong(3, user.id)
               .addBatch();
@@ -130,32 +132,25 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
     return users;
   }
 
-  private static String convertScmAccountsToCsv(List<String> scmAccounts) {
-    List<String> list = newArrayList(scmAccounts);
-    // Add one empty character at the begin and at the end of the list to be able to generate a coma at the begin and at the end of the
-    // text
-    list.add(0, "");
-    list.add("");
-    int size = list.size();
-    StringWriter writer = new StringWriter(size);
-    CsvWriter csv = CsvWriter.of(writer);
-    csv.values(list.toArray(new String[size]));
-    csv.close();
-    // Remove useless line break added by CsvWriter at this end of the text
-    return CharMatcher.JAVA_ISO_CONTROL.removeFrom(writer.toString());
+  @CheckForNull
+  private static String encodeScmAccounts(List<String> scmAccounts) {
+    if (scmAccounts.isEmpty()) {
+      return null;
+    }
+    return SCM_ACCOUNTS_SEPARATOR + Joiner.on(SCM_ACCOUNTS_SEPARATOR).join(scmAccounts) + SCM_ACCOUNTS_SEPARATOR;
   }
 
   private static class User {
     Long id;
     String login;
     String email;
-    String scmAcounts;
+    String scmAccounts;
 
-    User(Long id, String login, String email, String scmAcounts) {
+    User(Long id, String login, String email, String scmAccounts) {
       this.id = id;
       this.login = login;
       this.email = email;
-      this.scmAcounts = scmAcounts;
+      this.scmAccounts = scmAccounts;
     }
   }
 }
index d5ab0f666a0f40897521ce587bfed483b326cbcd..e90ec514fa3e33eaacc39c1b07107ff81040f3e8 100644 (file)
@@ -20,7 +20,6 @@
 
 package org.sonar.server.user;
 
-import com.google.common.base.CharMatcher;
 import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
@@ -32,7 +31,6 @@ import org.sonar.api.ServerComponent;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.NewUserHandler;
 import org.sonar.api.utils.System2;
-import org.sonar.api.utils.text.CsvWriter;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.user.GroupDto;
 import org.sonar.core.user.UserDto;
@@ -46,14 +44,12 @@ import org.sonar.server.util.Validation;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.io.StringWriter;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newLinkedHashSet;
 
 public class UserUpdater implements ServerComponent {
 
@@ -151,7 +147,7 @@ public class UserUpdater implements ServerComponent {
     List<String> scmAccounts = sanitizeScmAccounts(newUser.scmAccounts());
     if (scmAccounts != null && !scmAccounts.isEmpty()) {
       validateScmAccounts(dbSession, scmAccounts, login, email, null, messages);
-      userDto.setScmAccounts(convertScmAccountsToCsv(scmAccounts));
+      userDto.setScmAccounts(scmAccounts);
     }
 
     if (!messages.isEmpty()) {
@@ -186,9 +182,9 @@ public class UserUpdater implements ServerComponent {
       List<String> scmAccounts = sanitizeScmAccounts(updateUser.scmAccounts());
       if (scmAccounts != null && !scmAccounts.isEmpty()) {
         validateScmAccounts(dbSession, scmAccounts, userDto.getLogin(), email != null ? email : userDto.getEmail(), userDto, messages);
-        userDto.setScmAccounts(convertScmAccountsToCsv(scmAccounts));
+        userDto.setScmAccounts(scmAccounts);
       } else {
-        userDto.setScmAccounts(null);
+        userDto.setScmAccounts((String) null);
       }
     }
 
@@ -288,22 +284,6 @@ public class UserUpdater implements ServerComponent {
     userDto.setCryptedPassword(DigestUtils.sha1Hex("--" + saltHex + "--" + password + "--"));
   }
 
-  private static String convertScmAccountsToCsv(List<String> scmAccounts) {
-    // Use a set to remove duplication, then use a list to add empty characters
-    List<String> uniqueScmAccounts = newArrayList(newLinkedHashSet(scmAccounts));
-    // Add one empty character at the begin and at the end of the list to be able to generate a coma at the begin and at the end of the
-    // text
-    uniqueScmAccounts.add(0, "");
-    uniqueScmAccounts.add("");
-    int size = uniqueScmAccounts.size();
-    StringWriter writer = new StringWriter(size);
-    CsvWriter csv = CsvWriter.of(writer);
-    csv.values(uniqueScmAccounts.toArray(new String[size]));
-    csv.close();
-    // Remove useless line break added by CsvWriter at this end of the text
-    return CharMatcher.JAVA_ISO_CONTROL.removeFrom(writer.toString());
-  }
-
   private void notifyNewUser(String login, String name, String email) {
     newUserNotifier.onNewUser(NewUserHandler.Context.builder()
       .setLogin(login)
index ba3db9a9c7cb2e1cb49c63e42ccb7fde1c5903ef..3b813e0ddd09b906c0c6d381024321b45f924020 100644 (file)
@@ -52,7 +52,8 @@ public class UserDao extends org.sonar.core.user.UserDao implements DaoComponent
   }
 
   public List<UserDto> selectNullableByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) {
-    return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail);
+    String like = new StringBuilder().append("%").append(UserDto.SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail).append(UserDto.SCM_ACCOUNTS_SEPARATOR).append("%").toString();
+    return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like);
   }
 
   protected UserMapper mapper(DbSession session) {
index 6934ae53ba535cc3dd93f34f7a42702882bc710c..15e038159a9ac0a32f1accea306aca868cd1c24e 100644 (file)
  */
 package org.sonar.server.user.index;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.Maps;
-import org.apache.commons.csv.CSVFormat;
-import org.apache.commons.csv.CSVParser;
-import org.apache.commons.csv.CSVRecord;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
+import org.sonar.core.user.UserDto;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.db.ResultSetIterator;
 
-import javax.annotation.Nullable;
-
-import java.io.IOException;
-import java.io.StringReader;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
 
 /**
  * Scrolls over table USERS and reads documents to populate the user index
@@ -89,35 +78,10 @@ class UserResultSetIterator extends ResultSetIterator<UserDoc> {
     doc.setName(rs.getString(2));
     doc.setEmail(rs.getString(3));
     doc.setActive(rs.getBoolean(4));
-    doc.setScmAccounts(getScmAccounts(rs.getString(5), login));
+    doc.setScmAccounts(UserDto.decodeScmAccounts(rs.getString(5)));
     doc.setCreatedAt(rs.getLong(6));
     doc.setUpdatedAt(rs.getLong(7));
     return doc;
   }
 
-  private List<String> getScmAccounts(@Nullable String csv, String login) {
-    List<String> result = newArrayList();
-    if (csv == null) {
-      return result;
-    }
-    CSVParser csvParser = null;
-    StringReader reader = null;
-    try {
-      reader = new StringReader(csv);
-      csvParser = new CSVParser(reader, CSVFormat.DEFAULT);
-      for (CSVRecord csvRecord : csvParser) {
-        for (String aCsvRecord : csvRecord) {
-          if (!Strings.isNullOrEmpty(aCsvRecord)) {
-            result.add(aCsvRecord);
-          }
-        }
-      }
-      return result;
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Fail to read scm accounts for user '%s'", login), e);
-    } finally {
-      IOUtils.closeQuietly(reader);
-      IOUtils.closeQuietly(csvParser);
-    }
-  }
 }
index 251a261795be1568e699f3012bd83a20cae57089..603f1cad8efbf06a4f2098be1c219eb6a34c2e3b 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.ClassRule;
 import org.junit.Test;
 import org.sonar.api.utils.System2;
 import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.UserDto;
 import org.sonar.server.db.migrations.DatabaseMigration;
 
 import java.util.Map;
@@ -58,11 +59,11 @@ public class CopyScmAccountsFromAuthorsToUsersTest {
     migration.execute();
 
     User simon = getUserByLogin("simon");
-    assertThat(simon.scmAccounts).isEqualTo(",Simon B,simon@codehaus.org,");
+    assertThat(simon.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "Simon B" + UserDto.SCM_ACCOUNTS_SEPARATOR + "simon@codehaus.org" + UserDto.SCM_ACCOUNTS_SEPARATOR);
     assertThat(simon.updatedAt).isEqualTo(updatedDate);
 
     User fabrice = getUserByLogin("fabrice");
-    assertThat(fabrice.scmAccounts).isEqualTo(",fab,");
+    assertThat(fabrice.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "fab" + UserDto.SCM_ACCOUNTS_SEPARATOR);
     assertThat(fabrice.updatedAt).isEqualTo(updatedDate);
 
     assertThat(getUserByLogin("julien").updatedAt).isEqualTo(oldDate);
index f33f226b4548075f0cba7abb3d49afa4b2138eee..22f9e98fb2f3a310035d2d00bfaf582ca1d1e0d3 100644 (file)
@@ -89,7 +89,7 @@ public class UserServiceMediumTest {
     assertThat(userDto.getEmail()).isEqualTo("user@mail.com");
     assertThat(userDto.getCryptedPassword()).isNotNull();
     assertThat(userDto.getSalt()).isNotNull();
-    assertThat(userDto.getScmAccounts()).contains(",u1,u_1,");
+    assertThat(userDto.getScmAccountsAsList()).containsOnly("u1", "u_1");
     assertThat(userDto.getCreatedAt()).isNotNull();
     assertThat(userDto.getUpdatedAt()).isNotNull();
 
index 3121e0133c657e154bebf7b1c98e019daec54ac0..15590048f24660b14d38328111cc1dd117fb977a 100644 (file)
@@ -116,7 +116,7 @@ public class UserUpdaterTest {
     assertThat(dto.getLogin()).isEqualTo("user");
     assertThat(dto.getName()).isEqualTo("User");
     assertThat(dto.getEmail()).isEqualTo("user@mail.com");
-    assertThat(dto.getScmAccounts()).isEqualTo(",u1,u_1,User 1,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("u1", "u_1", "User 1");
     assertThat(dto.isActive()).isTrue();
 
     assertThat(dto.getSalt()).isNotNull();
@@ -158,7 +158,7 @@ public class UserUpdaterTest {
       .setPasswordConfirmation("password")
       .setScmAccounts(newArrayList("u1", "", null)));
 
-    assertThat(userDao.selectNullableByLogin(session, "user").getScmAccounts()).isEqualTo(",u1,");
+    assertThat(userDao.selectNullableByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1");
   }
 
   @Test
@@ -188,7 +188,7 @@ public class UserUpdaterTest {
       .setPasswordConfirmation("password")
       .setScmAccounts(newArrayList("u1", "u1")));
 
-    assertThat(userDao.selectNullableByLogin(session, "user").getScmAccounts()).isEqualTo(",u1,");
+    assertThat(userDao.selectNullableByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1");
   }
 
   @Test
@@ -561,7 +561,7 @@ public class UserUpdaterTest {
     assertThat(dto.isActive()).isTrue();
     assertThat(dto.getName()).isEqualTo("Marius2");
     assertThat(dto.getEmail()).isEqualTo("marius2@mail.com");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma2,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma2");
 
     assertThat(dto.getSalt()).isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
     assertThat(dto.getCryptedPassword()).isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
@@ -584,7 +584,7 @@ public class UserUpdaterTest {
     session.clearCache();
 
     UserDto dto = userDao.selectNullableByLogin(session, "marius");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma2,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma2");
   }
 
   @Test
@@ -602,7 +602,7 @@ public class UserUpdaterTest {
 
     // Following fields has not changed
     assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33");
     assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
     assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
   }
@@ -622,7 +622,7 @@ public class UserUpdaterTest {
 
     // Following fields has not changed
     assertThat(dto.getName()).isEqualTo("Marius");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33");
     assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
     assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
   }
@@ -638,7 +638,7 @@ public class UserUpdaterTest {
     session.clearCache();
 
     UserDto dto = userDao.selectNullableByLogin(session, "marius");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma2,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma2");
 
     // Following fields has not changed
     assertThat(dto.getName()).isEqualTo("Marius");
@@ -658,7 +658,7 @@ public class UserUpdaterTest {
     session.clearCache();
 
     UserDto dto = userDao.selectNullableByLogin(session, "marius");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33");
   }
 
   @Test
@@ -692,7 +692,7 @@ public class UserUpdaterTest {
 
     // Following fields has not changed
     assertThat(dto.getName()).isEqualTo("Marius");
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33");
     assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
   }
 
index 683847263667f98d53c948245cf768040eeaa286..8773aceaa0112b1db7bb55fe061d8674d2bceb7e 100644 (file)
@@ -66,7 +66,7 @@ public class UserDaoTest {
     assertThat(dto.getName()).isEqualTo("Marius");
     assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
     assertThat(dto.isActive()).isTrue();
-    assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,");
+    assertThat(dto.getScmAccountsAsList()).containsOnly("ma","marius33");
     assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
     assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
     assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L);
index 3c22b1ebf619b18870b6b429b00e26f64a2bd440..926b75324ad7b9f3000a788f868844ebd6a88787 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index 15ee29c168a1ae62cddb3ddf644b9076741378b9..d0acdb2a121e2183770cadabf47585740f56ffe2 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
   <groups id="1" name="sonar-devs" description="Sonar Devs" created_at="2014-09-08" updated_at="2014-09-08"/>
index fee1ef143c53e0469aae30e33ae084ea3b34c1be..617fe0f8389a6726b900e568f51e0c795b326b41 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
   <groups id="1" name="sonar-devs" description="Sonar Devs" created_at="2014-09-08" updated_at="2014-09-08"/>
index 5a257afb338968be42dc7ead03002edf42499a39..33dac1a0693643776c18ab9fb7cb33d774baa093 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts=",jo," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts="&#10;jo&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index 4bcf30b7e613c14d3686a4a2f1cafbd8afd09463..1f32fad9c4d75028989ed30fd6c83f1469335d5c 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts=",jo," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts="&#10;jo&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
   <users id="102" login="technical-account" name="Technical account" email="john@email.com" active="[true]" scm_accounts="[null]" created_at="1418215735482"
index 3c22b1ebf619b18870b6b429b00e26f64a2bd440..926b75324ad7b9f3000a788f868844ebd6a88787 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index d535e5bf01d7d45c82b7d4ee7bf8b5cd34591c18..6c8d4d3a854f40b93dd3c43ca4ad95e8d571c374 100644 (file)
@@ -1,8 +1,8 @@
 <dataset>
 
-  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts=",jo," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts="&#10;jo&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
-  <users id="102" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="102" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index 8bbd3daa996c46f3182ea65e2dbba90a7397d106..8daf3302314131a6d879609a104a38fff84368ed 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
   <groups id="1" name="sonar-users" description="Sonar Users" created_at="2014-09-08" updated_at="2014-09-08"/>
index 24fd96bdfc7e02cc00ecc122213e53c09cef1b56..eef35eda8791ee73cc2c848631f18db9c7bbf27b 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index 87839ca2fe53beb49d3b31142ea58cc0c820fbdd..926b75324ad7b9f3000a788f868844ebd6a88787 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts=",ma,marius33," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
 
 </dataset>
index a1879e0b4702c73c967d082fbe425ef90173aae9..5a54ec22958464439a3d68b5a8a0ffecada707f6 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts=",ma,marius33," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
   <users id="102" login="sbrandhof" name="Simon Brandhof" email="marius@lesbronzes.fr" active="[true]" scm_accounts="[null]" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8366" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fh"/>
index 854325a4e7dcab7f4c7dab9f5b86c4823fbda1a8..1a5f244b365955565576b7836df2e269b14d41b8 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts=",ma,marius33," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
   <users id="102" login="sbrandhof" name="Simon Brandhof" email="sbrandhof@lesbronzes.fr" active="[true]" scm_accounts="[null]" created_at="1418215735482"
          updated_at="1418215735485"
index 8ea78cfa0d19372ba55cba37e2c123b69d3b4942..41fe9478740f3a53eb0938074d5a313224460933 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
 
-  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts=",ma,marius33," created_at="1418215735482" updated_at="1418215735485"
+  <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="&#10;ma&#10;marius33&#10;" created_at="1418215735482" updated_at="1418215735485"
          salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
   <users id="102" login="sbrandhof" name="Simon Brandhof" email="marius@lesbronzes.fr" active="[true]" scm_accounts="[null]" created_at="1418215735482"
          updated_at="1418215735485"
index a9698f50f35e7e7bb1e55e6ab204e8a0dadc4150..013dd721b608fed9250a5322b53b7cec67b86b8f 100644 (file)
@@ -1,7 +1,7 @@
 <dataset>
 
   <users id="1" login="user1" name="User1" email="user1@mail.com" active="[true]"
-         scm_accounts="user_1,u1"
+         scm_accounts="&#10;user_1&#10;u1&#10;"
          created_at="1500000000000"
          updated_at="1500000000000"
       />
index 0163794b9dcace55cd003e93464cdffb6e91fe0d..077fc76c2ee9c1d3ed6d2351945fc5b5ed8a207a 100644 (file)
@@ -1,14 +1,14 @@
 <dataset>
 
   <users id="1" login="user1" name="User1" email="user1@mail.com" active="[true]"
-         scm_accounts=",user_1,u1,"
+         scm_accounts="&#10;user_1&#10;u1&#10;"
          created_at="1500000000000"
          updated_at="1500000000000"
       />
 
   <!-- scm accounts with comma -->
   <users id="2" login="user2" name="User2" email="user2@mail.com" active="[true]"
-         scm_accounts=",&quot;user,2&quot;,user_2,"
+         scm_accounts="&#10;user,2&#10;user_2&#10;"
          created_at="1500000000000"
          updated_at="1500000000000"
       />
index 3f62cf2fdbe3069ae9d2493c9324766f82ff1381..11cfc59cb082871acb58e0b32456d6e3a7a85a57 100644 (file)
  */
 package org.sonar.core.user;
 
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @since 3.2
  */
 public class UserDto {
+  public static final char SCM_ACCOUNTS_SEPARATOR = '\n';
+
   private Long id;
   private String login;
   private String name;
@@ -88,11 +97,39 @@ public class UserDto {
     return scmAccounts;
   }
 
-  public UserDto setScmAccounts(@Nullable String scmAccounts) {
-    this.scmAccounts = scmAccounts;
+  public List<String> getScmAccountsAsList() {
+    return decodeScmAccounts(scmAccounts);
+  }
+
+  /**
+   * List of SCM accounts separated by '|'
+   */
+  public UserDto setScmAccounts(@Nullable String s) {
+    this.scmAccounts = s;
     return this;
   }
 
+  public UserDto setScmAccounts(@Nullable List list) {
+    this.scmAccounts = encodeScmAccounts(list);
+    return this;
+  }
+
+  @CheckForNull
+  public static String encodeScmAccounts(@Nullable List<String> scmAccounts) {
+    if (scmAccounts != null && !scmAccounts.isEmpty()) {
+      return String.format("%s%s%s", SCM_ACCOUNTS_SEPARATOR, StringUtils.join(scmAccounts, SCM_ACCOUNTS_SEPARATOR), SCM_ACCOUNTS_SEPARATOR);
+    }
+    return null;
+  }
+
+  public static List<String> decodeScmAccounts(@Nullable String dbValue) {
+    if (dbValue == null) {
+      return new ArrayList<>();
+    } else {
+      return Lists.newArrayList(Splitter.on(SCM_ACCOUNTS_SEPARATOR).omitEmptyStrings().split(dbValue));
+    }
+  }
+
   public String getCryptedPassword() {
     return cryptedPassword;
   }
index 80c44052ea938fdad8f9c6923591ac97a3a2739f..6658f10dc6a3f301c2f694cd706eab5dc3196593 100644 (file)
@@ -36,7 +36,7 @@ public interface UserMapper {
    * Can return multiple results if an email is used by many users (For instance, technical account can use the same email as a none technical account)
    */
   @CheckForNull
-  List<UserDto> selectNullableByScmAccountOrLoginOrEmail(String scmAccountOrLoginOrEmail);
+  List<UserDto> selectNullableByScmAccountOrLoginOrEmail(@Param("scmAccount") String scmAccountOrLoginOrEmail, @Param("likeScmAccount") String likeScmAccount);
 
   @CheckForNull
   UserDto selectUser(long userId);
index 42a16e13139c2975a86627d206fc21c64934dbaf..aab09322e5d5a5c7a04d193c078ecf6fd5779c0f 100644 (file)
     WHERE u.login=#{login}
   </select>
 
-  <select id="selectNullableByScmAccountOrLoginOrEmail" parameterType="String" resultType="User">
+  <select id="selectNullableByScmAccountOrLoginOrEmail" parameterType="map" resultType="User">
     SELECT
     <include refid="userColumns"/>
     FROM users u
     WHERE
     u.login=#{scmAccount}
     OR u.email=#{scmAccount}
-    OR
-    <choose>
-      <when test="_databaseId == 'mssql'">
-        u.scm_accounts LIKE '%,' + #{scmAccount} + ',%'
-      </when>
-      <when test="_databaseId == 'mysql'">
-        u.scm_accounts LIKE concat('%,', #{scmAccount}, ',%')
-      </when>
-      <otherwise>
-        u.scm_accounts LIKE '%,' || #{scmAccount} || ',%'
-      </otherwise>
-    </choose>
+    OR u.scm_accounts like #{likeScmAccount}
   </select>
 
   <select id="selectUser" parameterType="long" resultType="User">
   </delete>
 
   <update id="deactivateUser" parameterType="long">
-    UPDATE users SET active=${_false}, updated_at=#{now} WHERE id=#{id}
+    UPDATE users SET active=${_false}, updated_at=#{now,jdbcType=BIGINT} WHERE id=#{id}
   </update>
 
   <insert id="insert" parameterType="User" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
diff --git a/sonar-core/src/test/java/org/sonar/core/user/UserDtoTest.java b/sonar-core/src/test/java/org/sonar/core/user/UserDtoTest.java
new file mode 100644 (file)
index 0000000..2315085
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.user;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UserDtoTest {
+
+  @Test
+  public void encode_scm_accounts() throws Exception {
+    assertThat(UserDto.encodeScmAccounts(null)).isNull();
+    assertThat(UserDto.encodeScmAccounts(Collections.<String>emptyList())).isNull();
+    assertThat(UserDto.encodeScmAccounts(Arrays.asList("foo"))).isEqualTo("\nfoo\n");
+    assertThat(UserDto.encodeScmAccounts(Arrays.asList("foo", "bar"))).isEqualTo("\nfoo\nbar\n");
+  }
+
+  @Test
+  public void decode_scm_accounts() throws Exception {
+    assertThat(UserDto.decodeScmAccounts(null)).isEmpty();
+    assertThat(UserDto.decodeScmAccounts("\nfoo\n")).containsOnly("foo");
+    assertThat(UserDto.decodeScmAccounts("\nfoo\nbar\n")).containsOnly("foo", "bar");
+  }
+}