]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7205 WS users/search with token count in response 721/head
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 18 Jan 2016 11:12:39 +0000 (12:12 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 18 Jan 2016 17:33:40 +0000 (18:33 +0100)
13 files changed:
server/sonar-server/src/main/java/org/sonar/server/user/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/user/ws/UserJsonWriter.java
server/sonar-server/src/main/resources/org/sonar/server/user/ws/example-search.json [deleted file]
server/sonar-server/src/main/resources/org/sonar/server/user/ws/search-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/user/ws/SearchActionTest/five_users.json
server/sonar-server/src/test/resources/org/sonar/server/user/ws/SearchActionTest/user_one.json
sonar-db/src/main/java/org/sonar/db/MyBatis.java
sonar-db/src/main/java/org/sonar/db/user/UserTokenCount.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/user/UserTokenDao.java
sonar-db/src/main/java/org/sonar/db/user/UserTokenMapper.java
sonar-db/src/main/resources/org/sonar/db/user/UserTokenMapper.xml
sonar-db/src/test/java/org/sonar/db/user/UserTokenDaoTest.java

index c7f84c9d2b8cc7a7793ac43cdb9aa0f178e65af3..270a336fe3fec6713f33eb1df4fcbeac89b57139 100644 (file)
@@ -21,10 +21,12 @@ package org.sonar.server.user.ws;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.sonar.api.server.ws.Request;
@@ -32,14 +34,14 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.db.DbSession;
-import org.sonar.db.MyBatis;
 import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.es.SearchResult;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
 
+import static com.google.common.base.Objects.firstNonNull;
 import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
 
 public class SearchAction implements UsersWsAction {
@@ -60,7 +62,7 @@ public class SearchAction implements UsersWsAction {
       .setDescription("Get a list of active users. Administer System permission is required to show the 'groups' field.")
       .setSince("3.6")
       .setHandler(this)
-      .setResponseExample(getClass().getResource("example-search.json"));
+      .setResponseExample(getClass().getResource("search-example.json"));
 
     action.addFieldsParam(UserJsonWriter.FIELDS);
     action.addPagingParams(50, MAX_LIMIT);
@@ -77,31 +79,34 @@ public class SearchAction implements UsersWsAction {
     SearchResult<UserDoc> result = userIndex.search(request.param(Param.TEXT_QUERY), options);
 
     Multimap<String, String> groupsByLogin = ArrayListMultimap.create();
+    Map<String, Integer> tokenCountsByLogin = new HashMap<>();
     DbSession dbSession = dbClient.openSession(false);
     try {
-      Collection<String> logins = Collections2.transform(result.getDocs(), new Function<UserDoc, String>() {
+      List<String> logins = Lists.transform(result.getDocs(), new Function<UserDoc, String>() {
         @Override
         public String apply(@Nonnull UserDoc input) {
           return input.login();
         }
       });
       groupsByLogin = dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, logins);
+      tokenCountsByLogin = dbClient.userTokenDao().countTokensByLogins(dbSession, logins);
     } finally {
-      MyBatis.closeQuietly(dbSession);
+      dbClient.closeSession(dbSession);
     }
 
     JsonWriter json = response.newJsonWriter().beginObject();
     options.writeJson(json, result.getTotal());
-    writeUsers(json, result, fields, groupsByLogin);
+    writeUsers(json, result, groupsByLogin, tokenCountsByLogin, fields);
     json.endObject().close();
   }
 
-  private void writeUsers(JsonWriter json, SearchResult<UserDoc> result, @Nullable List<String> fields, Multimap<String, String> groupsByLogin) {
+  private void writeUsers(JsonWriter json, SearchResult<UserDoc> result, Multimap<String, String> groupsByLogin, Map<String, Integer> tokenCountsByLogin,
+    @Nullable List<String> fields) {
 
     json.name("users").beginArray();
     for (UserDoc user : result.getDocs()) {
       Collection<String> groups = groupsByLogin.get(user.login());
-      userWriter.write(json, user, groups, fields);
+      userWriter.write(json, user, firstNonNull(tokenCountsByLogin.get(user.login()), 0), groups, fields);
     }
     json.endArray();
   }
index c9d730da357e868a6ad859f08a83e237e4f22e29..e19fbdba8aad8bcdd51724192d175cef52a2a7fe 100644 (file)
@@ -40,6 +40,7 @@ public class UserJsonWriter {
   private static final String FIELD_SCM_ACCOUNTS = "scmAccounts";
   private static final String FIELD_GROUPS = "groups";
   private static final String FIELD_ACTIVE = "active";
+  private static final String FIELD_TOKEN_COUNT = "tokenCount";
 
   public static final Set<String> FIELDS = ImmutableSet.of(FIELD_NAME, FIELD_EMAIL, FIELD_SCM_ACCOUNTS, FIELD_GROUPS, FIELD_ACTIVE);
   private static final Set<String> CONCISE_FIELDS = ImmutableSet.of(FIELD_NAME, FIELD_EMAIL, FIELD_ACTIVE);
@@ -53,7 +54,14 @@ public class UserJsonWriter {
   /**
    * Serializes a user to the passed JsonWriter.
    */
-  public void write(JsonWriter json, User user, Collection<String> groups, Collection<String> fields) {
+  public void write(JsonWriter json, User user, Collection<String> groups, @Nullable Collection<String> fields) {
+    write(json, user, null, groups, fields);
+  }
+
+  /**
+   * Serializes a user to the passed JsonWriter.
+   */
+  public void write(JsonWriter json, User user, @Nullable Integer tokenCount, Collection<String> groups, @Nullable Collection<String> fields) {
     json.beginObject();
     json.prop(FIELD_LOGIN, user.login());
     writeIfNeeded(json, user.name(), FIELD_NAME, fields);
@@ -61,6 +69,7 @@ public class UserJsonWriter {
     writeIfNeeded(json, user.active(), FIELD_ACTIVE, fields);
     writeGroupsIfNeeded(json, groups, fields);
     writeScmAccountsIfNeeded(json, fields, user);
+    writeTokenCount(json, tokenCount);
     json.endObject();
   }
 
@@ -93,4 +102,12 @@ public class UserJsonWriter {
         .endArray();
     }
   }
+
+  private static void writeTokenCount(JsonWriter json, @Nullable Integer tokenCount) {
+    if (tokenCount == null) {
+      return;
+    }
+
+    json.prop(FIELD_TOKEN_COUNT, tokenCount);
+  }
 }
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/user/ws/example-search.json b/server/sonar-server/src/main/resources/org/sonar/server/user/ws/example-search.json
deleted file mode 100644 (file)
index 39448ce..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "users": [
-    {
-      "login": "fmallet",
-      "name": "Freddy Mallet",
-      "active": true,
-      "email": "f@m.com",
-      "scmAccounts": [],
-      "groups": ["sonar-users", "sonar-administrators"]
-    },
-    {
-      "login": "sbrandhof",
-      "name": "Simon",
-      "active": true,
-      "email": "s.brandhof@company.tld",
-      "scmAccounts": ["simon.brandhof", "s.brandhof@company.tld"],
-      "groups": ["sonar-users"]
-    }
-  ]
-}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/user/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/user/ws/search-example.json
new file mode 100644 (file)
index 0000000..43f791d
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "users": [
+    {
+      "login": "fmallet",
+      "name": "Freddy Mallet",
+      "active": true,
+      "email": "f@m.com",
+      "scmAccounts": [],
+      "groups": [
+        "sonar-users",
+        "sonar-administrators"
+      ],
+      "tokenCount": 1
+    },
+    {
+      "login": "sbrandhof",
+      "name": "Simon",
+      "active": true,
+      "email": "s.brandhof@company.tld",
+      "scmAccounts": [
+        "simon.brandhof",
+        "s.brandhof@company.tld"
+      ],
+      "groups": [
+        "sonar-users"
+      ],
+      "tokenCount": 3
+    }
+  ]
+}
index 50a89ef02d0f809b3ae0e9f0b9410e4c7ada813d..24689abde3d3677b207ee070a93e9f6b4f8ab7d8 100644 (file)
 package org.sonar.server.user.ws;
 
 import com.google.common.collect.Lists;
-import java.util.Arrays;
 import java.util.List;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.sonar.api.config.Settings;
-import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.System2;
 import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.GroupMembershipDao;
 import org.sonar.db.user.UserDto;
-import org.sonar.db.user.UserGroupDao;
 import org.sonar.db.user.UserGroupDto;
-import org.sonar.server.db.DbClient;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.db.user.GroupDao;
-import org.sonar.db.user.UserDao;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
 import org.sonar.server.user.index.UserIndexDefinition;
 import org.sonar.server.ws.WsTester;
 import org.sonar.test.DbTests;
 
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.user.UserTokenTesting.newUserToken;
 
 @Category(DbTests.class)
 public class SearchActionTest {
 
-  @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
   @ClassRule
   public static final EsTester esTester = new EsTester().addDefinitions(new UserIndexDefinition(new Settings()));
-
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+  DbClient dbClient = db.getDbClient();
+  DbSession dbSession = db.getSession();
 
-  WebService.Controller controller;
-
-  WsTester tester;
-
+  WsTester ws;
   UserIndex index;
 
-  DbClient dbClient;
-
-  DbSession session;
-
   @Before
   public void setUp() {
-    dbTester.truncateTables();
     esTester.truncateIndices();
-
-    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(),
-      new GroupMembershipDao(dbTester.myBatis()),
-      new UserDao(dbTester.myBatis(), new System2()),
-      new GroupDao(new System2()),
-      new UserGroupDao());
-    session = dbClient.openSession(false);
-
     index = new UserIndex(esTester.client());
-    tester = new WsTester(new UsersWs(new SearchAction(index, dbClient, new UserJsonWriter(userSession))));
-    controller = tester.controller("api/users");
-  }
-
-  @After
-  public void tearDown() {
-    session.close();
+    ws = new WsTester(new UsersWs(new SearchAction(index, dbClient, new UserJsonWriter(userSession))));
   }
 
   @Test
   public void search_empty() throws Exception {
-    tester.newGetRequest("api/users", "search").execute().assertJson(getClass(), "empty.json");
+    ws.newGetRequest("api/users", "search").execute().assertJson(getClass(), "empty.json");
   }
 
   @Test
   public void search_without_parameters() throws Exception {
     injectUsers(5);
 
-    tester.newGetRequest("api/users", "search").execute().assertJson(getClass(), "five_users.json");
+    ws.newGetRequest("api/users", "search").execute().assertJson(getClass(), "five_users.json");
   }
 
   @Test
   public void search_with_query() throws Exception {
     injectUsers(5);
 
-    tester.newGetRequest("api/users", "search").setParam("q", "user-1").execute().assertJson(getClass(), "user_one.json");
+    ws.newGetRequest("api/users", "search").setParam("q", "user-1").execute().assertJson(getClass(), "user_one.json");
   }
 
   @Test
   public void search_with_paging() throws Exception {
     injectUsers(10);
 
-    tester.newGetRequest("api/users", "search").setParam(Param.PAGE_SIZE, "5").execute().assertJson(getClass(), "page_one.json");
-    tester.newGetRequest("api/users", "search").setParam(Param.PAGE_SIZE, "5").setParam(Param.PAGE, "2").execute().assertJson(getClass(), "page_two.json");
+    ws.newGetRequest("api/users", "search").setParam(Param.PAGE_SIZE, "5").execute().assertJson(getClass(), "page_one.json");
+    ws.newGetRequest("api/users", "search").setParam(Param.PAGE_SIZE, "5").setParam(Param.PAGE, "2").execute().assertJson(getClass(), "page_two.json");
   }
 
   @Test
   public void search_with_fields() throws Exception {
     injectUsers(1);
 
-    assertThat(tester.newGetRequest("api/users", "search").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").execute().outputAsString())
       .contains("login")
       .contains("name")
       .contains("email")
       .contains("scmAccounts")
       .doesNotContain("groups");
 
-    assertThat(tester.newGetRequest("api/users", "search").setParam(Param.FIELDS, "").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").setParam(Param.FIELDS, "").execute().outputAsString())
       .contains("login")
       .contains("name")
       .contains("email")
       .contains("scmAccounts")
       .doesNotContain("groups");
 
-    assertThat(tester.newGetRequest("api/users", "search").setParam(Param.FIELDS, "scmAccounts").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").setParam(Param.FIELDS, "scmAccounts").execute().outputAsString())
       .contains("login")
       .doesNotContain("name")
       .doesNotContain("email")
       .contains("scmAccounts")
       .doesNotContain("groups");
 
-    assertThat(tester.newGetRequest("api/users", "search").setParam(Param.FIELDS, "groups").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").setParam(Param.FIELDS, "groups").execute().outputAsString())
       .contains("login")
       .doesNotContain("name")
       .doesNotContain("email")
@@ -158,14 +131,14 @@ public class SearchActionTest {
 
     loginAsAdmin();
 
-    assertThat(tester.newGetRequest("api/users", "search").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").execute().outputAsString())
       .contains("login")
       .contains("name")
       .contains("email")
       .contains("scmAccounts")
       .contains("groups");
 
-    assertThat(tester.newGetRequest("api/users", "search").setParam(Param.FIELDS, "groups").execute().outputAsString())
+    assertThat(ws.newGetRequest("api/users", "search").setParam(Param.FIELDS, "groups").execute().outputAsString())
       .contains("login")
       .doesNotContain("name")
       .doesNotContain("email")
@@ -177,14 +150,14 @@ public class SearchActionTest {
   public void search_with_groups() throws Exception {
     List<UserDto> users = injectUsers(1);
 
-    GroupDto group1 = dbClient.groupDao().insert(session, new GroupDto().setName("sonar-users"));
-    GroupDto group2 = dbClient.groupDao().insert(session, new GroupDto().setName("sonar-admins"));
-    dbClient.userGroupDao().insert(session, new UserGroupDto().setGroupId(group1.getId()).setUserId(users.get(0).getId()));
-    dbClient.userGroupDao().insert(session, new UserGroupDto().setGroupId(group2.getId()).setUserId(users.get(0).getId()));
-    session.commit();
+    GroupDto group1 = dbClient.groupDao().insert(dbSession, new GroupDto().setName("sonar-users"));
+    GroupDto group2 = dbClient.groupDao().insert(dbSession, new GroupDto().setName("sonar-admins"));
+    dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(group1.getId()).setUserId(users.get(0).getId()));
+    dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(group2.getId()).setUserId(users.get(0).getId()));
+    dbSession.commit();
 
     loginAsAdmin();
-    tester.newGetRequest("api/users", "search").execute().assertJson(getClass(), "user_with_groups.json");
+    ws.newGetRequest("api/users", "search").execute().assertJson(getClass(), "user_with_groups.json");
   }
 
   private List<UserDto> injectUsers(int numberOfUsers) throws Exception {
@@ -195,9 +168,9 @@ public class SearchActionTest {
       String email = String.format("user-%d@mail.com", index);
       String login = String.format("user-%d", index);
       String name = String.format("User %d", index);
-      List<String> scmAccounts = Arrays.asList(String.format("user-%d", index));
+      List<String> scmAccounts = singletonList(String.format("user-%d", index));
 
-      userDtos.add(dbClient.userDao().insert(session, new UserDto()
+      userDtos.add(dbClient.userDao().insert(dbSession, new UserDto()
         .setActive(true)
         .setCreatedAt(createdAt)
         .setEmail(email)
@@ -214,8 +187,14 @@ public class SearchActionTest {
         .setName(name)
         .setScmAccounts(scmAccounts)
         .setUpdatedAt(createdAt);
+
+      for (int tokenIndex = 0; tokenIndex < index; tokenIndex++) {
+        dbClient.userTokenDao().insert(dbSession, newUserToken()
+          .setLogin(login)
+          .setName(String.format("%s-%d", login, tokenIndex)));
+      }
     }
-    session.commit();
+    dbSession.commit();
     esTester.putDocuments(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, users);
     return userDtos;
   }
index 88a6fec9ecb943b1008cbe522c7dbc802d2f7306..576b41ad6cea83abf8c24093eca7c1433fbf4d4d 100644 (file)
@@ -9,7 +9,8 @@
       "email": "user-0@mail.com",
       "scmAccounts": [
         "user-0"
-      ]
+      ],
+      "tokenCount": 0
     },
     {
       "login": "user-1",
@@ -17,7 +18,8 @@
       "email": "user-1@mail.com",
       "scmAccounts": [
         "user-1"
-      ]
+      ],
+      "tokenCount": 1
     },
     {
       "login": "user-2",
@@ -25,7 +27,8 @@
       "email": "user-2@mail.com",
       "scmAccounts": [
         "user-2"
-      ]
+      ],
+      "tokenCount": 2
     },
     {
       "login": "user-3",
@@ -33,7 +36,8 @@
       "email": "user-3@mail.com",
       "scmAccounts": [
         "user-3"
-      ]
+      ],
+      "tokenCount": 3
     },
     {
       "login": "user-4",
@@ -41,7 +45,8 @@
       "email": "user-4@mail.com",
       "scmAccounts": [
         "user-4"
-      ]
+      ],
+      "tokenCount": 4
     }
   ]
 }
index 3c5b27e955231808174b0c3116b2338a9dae5832..2f3d06e9ac6c9633fd3843d25ec91370a524efb9 100644 (file)
@@ -9,7 +9,8 @@
       "email": "user-1@mail.com",
       "scmAccounts": [
         "user-1"
-      ]
+      ],
+      "tokenCount": 1
     }
   ]
 }
index 23065bfde9d502d2d13125de22a32e53fd1e37f0..9cf940005040706f4930c2af56240b249e89429d 100644 (file)
@@ -126,6 +126,7 @@ import org.sonar.db.user.UserGroupDto;
 import org.sonar.db.user.UserGroupMapper;
 import org.sonar.db.user.UserMapper;
 import org.sonar.db.user.UserRoleDto;
+import org.sonar.db.user.UserTokenCount;
 import org.sonar.db.user.UserTokenDto;
 import org.sonar.db.user.UserTokenMapper;
 import org.sonar.db.version.SchemaMigrationDto;
@@ -216,6 +217,7 @@ public class MyBatis {
     confBuilder.loadAlias("CustomMeasure", CustomMeasureDto.class);
     confBuilder.loadAlias("ViewsSnapshot", ViewsSnapshotDto.class);
     confBuilder.loadAlias("UserToken", UserTokenDto.class);
+    confBuilder.loadAlias("UserTokenCount", UserTokenCount.class);
 
     // AuthorizationMapper has to be loaded before IssueMapper because this last one used it
     confBuilder.loadMapper("org.sonar.db.user.AuthorizationMapper");
diff --git a/sonar-db/src/main/java/org/sonar/db/user/UserTokenCount.java b/sonar-db/src/main/java/org/sonar/db/user/UserTokenCount.java
new file mode 100644 (file)
index 0000000..4489675
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.db.user;
+
+public class UserTokenCount {
+  private String login;
+  private Integer tokenCount;
+
+  public String getLogin() {
+    return login;
+  }
+
+  public UserTokenCount setLogin(String login) {
+    this.login = login;
+    return this;
+  }
+
+  public Integer tokenCount() {
+    return tokenCount;
+  }
+
+  public UserTokenCount setTokenCount(Integer tokenCount) {
+    this.tokenCount = tokenCount;
+    return this;
+  }
+}
index eb494dc983da1fd7eac83d2a3c0ffe74437f58d2..ff6a69c0bedbc7ebc095bcdd684f72d1047eea9b 100644 (file)
  */
 package org.sonar.db.user;
 
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
 import org.sonar.db.Dao;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 import org.sonar.db.RowNotFoundException;
 
@@ -53,6 +58,22 @@ public class UserTokenDao implements Dao {
     return mapper(dbSession).selectByLogin(login);
   }
 
+  public Map<String, Integer> countTokensByLogins(final DbSession dbSession, List<String> logins) {
+    final Map<String, Integer> result = new HashMap<>();
+    DatabaseUtils.executeLargeInputs(logins, new Function<List<String>, List<UserTokenCount>>() {
+      @Override
+      public List<UserTokenCount> apply(@Nonnull List<String> input) {
+        List<UserTokenCount> userTokenCounts = mapper(dbSession).countTokensByLogins(input);
+        for (UserTokenCount userTokenCount : userTokenCounts) {
+          result.put(userTokenCount.getLogin(), userTokenCount.tokenCount());
+        }
+        return userTokenCounts;
+      }
+    });
+
+    return result;
+  }
+
   public void deleteByLogin(DbSession dbSession, String login) {
     mapper(dbSession).deleteByLogin(login);
   }
index f8ace95818f66521b0c71b649b9c7920917bc872..bb814b86e4cef140eebd3e5c553742dfa1ff1649 100644 (file)
@@ -34,4 +34,6 @@ public interface UserTokenMapper {
   void deleteByLogin(String login);
 
   void deleteByLoginAndName(@Param("login") String login, @Param("name") String name);
+
+  List<UserTokenCount> countTokensByLogins(@Param("logins") List<String> logins);
 }
index 1b74df52c2a041befe3f0c20959e1d375fe47a2f..3078c44137edb6e3424d903e0016af35439b9331 100644 (file)
     FROM user_tokens t
     WHERE t.login=#{login}
   </select>
+  
+  <select id="countTokensByLogins" parameterType="map" resultType="UserTokenCount">
+    SELECT t.login as "login", count(t.name) as "tokenCount"
+    FROM user_tokens t
+    WHERE t.login in
+    <foreach collection="logins" open="(" close=")" item="login" separator=",">
+      #{login}
+    </foreach>
+    GROUP BY t.login
+  </select>
 
   <delete id="deleteByLogin">
     DELETE FROM user_tokens WHERE login=#{login}
index 9b25fe6c28f0b633a165575969f725b0aff077e8..bfe27081188718d15b47c94d48cbad5387ad60f1 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.db.user;
 
 import com.google.common.base.Optional;
+import java.util.Map;
 import org.assertj.guava.api.Assertions;
 import org.junit.Before;
 import org.junit.Rule;
@@ -32,6 +33,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.RowNotFoundException;
 import org.sonar.test.DbTests;
 
+import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.db.user.UserTokenTesting.newUserToken;
 
@@ -129,6 +131,17 @@ public class UserTokenDaoTest {
     Assertions.assertThat(underTest.selectByLoginAndName(dbSession, "another-login", "name")).isPresent();
   }
 
+  @Test
+  public void count_tokens_by_login() {
+    insertToken(newUserToken().setLogin("login").setName("name"));
+    insertToken(newUserToken().setLogin("login").setName("another-name"));
+
+    Map<String, Integer> result = underTest.countTokensByLogins(dbSession, newArrayList("login"));
+
+    assertThat(result.get("login")).isEqualTo(2);
+    assertThat(result.get("unknown-login")).isNull();
+  }
+
   private void insertToken(UserTokenDto userToken) {
     underTest.insert(dbSession, userToken);
     dbSession.commit();