]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4930 Add pagination
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 4 Dec 2013 11:36:25 +0000 (12:36 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 4 Dec 2013 11:36:37 +0000 (12:36 +0100)
sonar-core/src/main/java/org/sonar/core/user/GroupMembershipDao.java
sonar-core/src/main/java/org/sonar/core/user/GroupMembershipQuery.java
sonar-core/src/main/resources/org/sonar/core/user/GroupMembershipMapper.xml
sonar-core/src/test/java/org/sonar/core/user/GroupMembershipDaoTest.java
sonar-server/src/main/java/org/sonar/server/group/GroupMembershipFinder.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/group/GroupMembershipQueryResult.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/group/InternalGroupMembershipQueryService.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/user_groups_controller.rb
sonar-server/src/test/java/org/sonar/server/group/GroupMembershipFinderTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/group/InternalGroupMembershipQueryServiceTest.java

index 1c314dd0fd5357fd07ab737ca59a0d2135aacdaf..d70c5ef24ebfe8ff5dce164bfff0367272e924f9 100644 (file)
 
 package org.sonar.core.user;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import org.apache.ibatis.session.RowBounds;
 import org.apache.ibatis.session.SqlSession;
 import org.sonar.core.persistence.MyBatis;
 
 import java.util.List;
+import java.util.Map;
 
 public class GroupMembershipDao {
 
@@ -33,14 +37,19 @@ public class GroupMembershipDao {
 
   private final MyBatis mybatis;
 
-  public List<GroupMembershipDto> selectGroups(GroupMembershipQuery query) {
+  public List<GroupMembershipDto> selectGroups(GroupMembershipQuery query, Long userId, int offset, int limit) {
     SqlSession session = mybatis.openSession();
     try {
-      GroupMembershipMapper mapper = session.getMapper(GroupMembershipMapper.class);
-      return mapper.selectGroups(query);
+      Map<String, Object> params = ImmutableMap.of("query",  query, "userId", userId);
+      return session.selectList("org.sonar.core.user.GroupMembershipMapper.selectGroups", params, new RowBounds(offset, limit));
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
+  @VisibleForTesting
+  List<GroupMembershipDto> selectGroups(GroupMembershipQuery query, Long userId) {
+    return selectGroups(query, userId, 0, Integer.MAX_VALUE);
+  }
+
 }
index 9c7bb596c182e73c5b44e24b6a4f2d06fd4dcd35..0af288cdc380b918df8f5536a7bd2cbe936c7cae 100644 (file)
@@ -36,18 +36,18 @@ public class GroupMembershipQuery {
   public static final int DEFAULT_PAGE_INDEX = 1;
   public static final int DEFAULT_PAGE_SIZE = 100;
 
-  public static final String ALL = "ALL";
-  public static final String MEMBER_ONLY = "MEMBER_ONLY";
-  public static final String NOT_MEMBER = "NOT_MEMBER";
-  public static final Set<String> AVAILABLE_MEMBERSHIP = ImmutableSet.of(ALL, MEMBER_ONLY, NOT_MEMBER);
+  public static final String ANY = "ANY";
+  public static final String IN = "IN";
+  public static final String OUT = "OUT";
+  public static final Set<String> AVAILABLE_MEMBERSHIP = ImmutableSet.of(ANY, IN, OUT);
 
-  private final Long userId;
-  private final String memberShip;
+  private final String login;
+  private final String membership;
 
-  private final String searchText;
+  private final String groupSearch;
 
   // for internal use in MyBatis
-  final String searchTextSql;
+  final String groupSearchSql;
 
   // max results per page
   private final int pageSize;
@@ -57,16 +57,16 @@ public class GroupMembershipQuery {
 
 
   private GroupMembershipQuery(Builder builder) {
-    this.userId = builder.userId;
-    this.memberShip = builder.memberShip;
-    this.searchText = builder.searchText;
-    this.searchTextSql = searchTextToSql(searchText);
+    this.login = builder.login;
+    this.membership = builder.membership;
+    this.groupSearch = builder.groupSearch;
+    this.groupSearchSql = groupSearchToSql(groupSearch);
 
     this.pageSize = builder.pageSize;
     this.pageIndex = builder.pageIndex;
   }
 
-  private String searchTextToSql(@Nullable String s) {
+  private String groupSearchToSql(@Nullable String s) {
     String sql = null;
     if (s != null) {
       sql = StringUtils.replace(s, "%", "/%");
@@ -76,21 +76,21 @@ public class GroupMembershipQuery {
     return sql;
   }
 
-  public Long userId() {
-    return userId;
+  public String login() {
+    return login;
   }
 
   @CheckForNull
-  public String memberShip() {
-    return memberShip;
+  public String membership() {
+    return membership;
   }
 
   /**
    * Search for groups or names containing a given string
    */
   @CheckForNull
-  public String searchText() {
-    return searchText;
+  public String groupSearch() {
+    return groupSearch;
   }
 
   public int pageSize() {
@@ -106,9 +106,9 @@ public class GroupMembershipQuery {
   }
 
   public static class Builder {
-    private Long userId;
-    private String memberShip = GroupMembershipQuery.ALL;
-    private String searchText;
+    private String login;
+    private String membership;
+    private String groupSearch;
 
     private Integer pageIndex = DEFAULT_PAGE_INDEX;
     private Integer pageSize = DEFAULT_PAGE_SIZE;
@@ -116,18 +116,18 @@ public class GroupMembershipQuery {
     private Builder() {
     }
 
-    public Builder userId(Long userId) {
-      this.userId = userId;
+    public Builder login(String login) {
+      this.login = login;
       return this;
     }
 
-    public Builder memberShip(@Nullable String memberShip) {
-      this.memberShip = memberShip;
+    public Builder membership(@Nullable String membership) {
+      this.membership = membership;
       return this;
     }
 
-    public Builder searchText(@Nullable String s) {
-      this.searchText = StringUtils.defaultIfBlank(s, null);
+    public Builder groupSearch(@Nullable String s) {
+      this.groupSearch = StringUtils.defaultIfBlank(s, null);
       return this;
     }
 
@@ -141,12 +141,32 @@ public class GroupMembershipQuery {
       return this;
     }
 
+    private void initMembership() {
+      if (membership == null) {
+        membership = GroupMembershipQuery.ANY;
+      } else {
+        // TODO check
+      }
+    }
+
+    private void initPageSize() {
+      if (pageSize == null) {
+        pageSize = DEFAULT_PAGE_SIZE;
+      }
+    }
+
     private void initPageIndex() {
+      if (pageIndex == null) {
+        pageIndex = DEFAULT_PAGE_INDEX;
+      }
       Preconditions.checkArgument(pageIndex > 0, "Page index must be greater than 0 (got " + pageIndex + ")");
     }
 
     public GroupMembershipQuery build() {
-      Preconditions.checkNotNull(userId, "User id cant be null.");
+      Preconditions.checkNotNull(login, "User cant be null.");
+      initMembership();
+      initPageIndex();
+      initPageSize();
       return new GroupMembershipQuery(this);
     }
   }
index d94c6b043e5a83b99d964dbfbd2cc61df7121b08..4c49396ab12dc118527d0c27091f281a025e5e13 100644 (file)
@@ -5,27 +5,32 @@
 
   <sql id="groupColumns">
     g.id as id,
-    g.name as name
+    g.name as name,
+    gu.user_id as userId
   </sql>
 
   <select id="selectGroups" parameterType="map" resultType="GroupMembership">
-    SELECT  g.id as id, g.name as name, gu.user_id as userId
+    SELECT <include refid="groupColumns"/>
     FROM groups g
+    <include refid="selectQueryConditions"/>
+    ORDER BY g.name
+  </select>
+
+  <sql id="selectQueryConditions">
     LEFT JOIN groups_users gu ON gu.group_id=g.id AND gu.user_id=#{userId}
     <where>
       <choose>
-        <when test="memberShip == 'MEMBER_ONLY'">
-          AND gu.user_id is not null
+        <when test="query.membership() == 'IN'">
+          AND gu.user_id IS NOT NULL
         </when>
-        <when test="memberShip == 'NOT_MEMBER'">
-          AND gu.user_id is null
+        <when test="query.membership() == 'OUT'">
+          AND gu.user_id IS NULL
         </when>
       </choose>
-      <if test="searchText != null">
-        AND (g.name LIKE #{searchTextSql} ESCAPE '/')
+      <if test="query.groupSearch() != null">
+        AND (g.name LIKE #{query.groupSearchSql} ESCAPE '/')
       </if>
     </where>
-    ORDER BY g.name
-  </select>
+  </sql>
 
 </mapper>
index 1035c1c5b5bb018f83fe59acd89728c1eb1fbe64..68ce939cf85d5a6eda8a5472983427e7441c4655 100644 (file)
@@ -41,8 +41,8 @@ public class GroupMembershipDaoTest extends AbstractDaoTestCase {
   public void select_all_groups_by_query() throws Exception {
     setupData("shared");
 
-    GroupMembershipQuery query = GroupMembershipQuery.builder().userId(200L).build();
-    List<GroupMembershipDto> result = dao.selectGroups(query);
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").build();
+    List<GroupMembershipDto> result = dao.selectGroups(query, 200L);
     assertThat(result).hasSize(3);
   }
 
@@ -50,8 +50,8 @@ public class GroupMembershipDaoTest extends AbstractDaoTestCase {
   public void select_user_group() throws Exception {
     setupData("select_user_group");
 
-    GroupMembershipQuery query = GroupMembershipQuery.builder().userId(201L).build();
-    List<GroupMembershipDto> result = dao.selectGroups(query);
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").build();
+    List<GroupMembershipDto> result = dao.selectGroups(query, 201L);
     assertThat(result).hasSize(1);
 
     GroupMembershipDto dto = result.get(0);
@@ -65,11 +65,11 @@ public class GroupMembershipDaoTest extends AbstractDaoTestCase {
     setupData("shared");
 
     // 200 is member of 3 groups
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(200L).memberShip(GroupMembershipQuery.MEMBER_ONLY).build())).hasSize(3);
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.IN).build(), 200L)).hasSize(3);
     // 201 is member of 1 group on 3
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(201L).memberShip(GroupMembershipQuery.MEMBER_ONLY).build())).hasSize(1);
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.IN).build(), 201L)).hasSize(1);
     // 999 is member of 0 group
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(999L).memberShip(GroupMembershipQuery.MEMBER_ONLY).build())).isEmpty();
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.IN).build(), 999L)).isEmpty();
   }
 
   @Test
@@ -77,23 +77,23 @@ public class GroupMembershipDaoTest extends AbstractDaoTestCase {
     setupData("shared");
 
     // 200 is member of 3 groups
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(200L).memberShip(GroupMembershipQuery.NOT_MEMBER).build())).isEmpty();
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.OUT).build(), 200L)).isEmpty();
     // 201 is member of 1 group on 3
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(201L).memberShip(GroupMembershipQuery.NOT_MEMBER).build())).hasSize(2);
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.OUT).build(), 201L)).hasSize(2);
     // 999 is member of 0 group
-    assertThat(dao.selectGroups(GroupMembershipQuery.builder().userId(999L).memberShip(GroupMembershipQuery.NOT_MEMBER).build())).hasSize(3);
+    assertThat(dao.selectGroups(GroupMembershipQuery.builder().login("arthur").membership(GroupMembershipQuery.OUT).build(), 2999L)).hasSize(3);
   }
 
   @Test
   public void select_only_matching_group_name() throws Exception {
     setupData("shared");
 
-    List<GroupMembershipDto> result = dao.selectGroups(GroupMembershipQuery.builder().userId(200L).searchText("user").build());
+    List<GroupMembershipDto> result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").groupSearch("user").build(), 200L);
     assertThat(result).hasSize(1);
 
     assertThat(result.get(0).getName()).isEqualTo("sonar-users");
 
-    result = dao.selectGroups(GroupMembershipQuery.builder().userId(200L).searchText("sonar").build());
+    result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").groupSearch("sonar").build(), 200L);
     assertThat(result).hasSize(3);
   }
 
@@ -101,11 +101,30 @@ public class GroupMembershipDaoTest extends AbstractDaoTestCase {
   public void should_be_sorted_by_group_name() throws Exception {
     setupData("should_be_sorted_by_group_name");
 
-    List<GroupMembershipDto> result = dao.selectGroups(GroupMembershipQuery.builder().userId(200L).memberShip(GroupMembershipQuery.ALL).build());
+    List<GroupMembershipDto> result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").build(), 200L);
     assertThat(result).hasSize(3);
     assertThat(result.get(0).getName()).isEqualTo("sonar-administrators");
     assertThat(result.get(1).getName()).isEqualTo("sonar-reviewers");
     assertThat(result.get(2).getName()).isEqualTo("sonar-users");
   }
 
+  @Test
+  public void should_be_paginated() throws Exception {
+    setupData("shared");
+
+    List<GroupMembershipDto> result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").build(), 200L, 0, 2);
+    assertThat(result).hasSize(2);
+    assertThat(result.get(0).getName()).isEqualTo("sonar-administrators");
+    assertThat(result.get(1).getName()).isEqualTo("sonar-reviewers");
+
+    result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").build(), 200L, 1, 2);
+    assertThat(result).hasSize(2);
+    assertThat(result.get(0).getName()).isEqualTo("sonar-reviewers");
+    assertThat(result.get(1).getName()).isEqualTo("sonar-users");
+
+    result = dao.selectGroups(GroupMembershipQuery.builder().login("arthur").build(), 200L, 2, 1);
+    assertThat(result).hasSize(1);
+    assertThat(result.get(0).getName()).isEqualTo("sonar-users");
+  }
+
 }
diff --git a/sonar-server/src/main/java/org/sonar/server/group/GroupMembershipFinder.java b/sonar-server/src/main/java/org/sonar/server/group/GroupMembershipFinder.java
new file mode 100644 (file)
index 0000000..6a0b898
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.server.group;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.core.user.*;
+import org.sonar.server.exceptions.NotFoundException;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class GroupMembershipFinder implements ServerComponent {
+
+  private final UserDao userDao;
+  private final GroupMembershipDao groupMembershipDao;
+
+  public GroupMembershipFinder(UserDao userDao, GroupMembershipDao groupMembershipDao) {
+    this.userDao = userDao;
+    this.groupMembershipDao = groupMembershipDao;
+  }
+
+  public GroupMembershipQueryResult find(GroupMembershipQuery query) {
+    Long userId = userId(query.login());
+    int pageSize = query.pageSize();
+    int pageIndex = query.pageIndex();
+
+    int offset = (pageIndex - 1) * pageSize;
+    // Add one to page size in order to be able to know if there's more results or not
+    int limit = pageSize + 1;
+    List<GroupMembershipDto> dtos = groupMembershipDao.selectGroups(query, userId, offset, limit);
+    boolean hasMoreResults = false;
+    if (dtos.size() == limit) {
+      hasMoreResults = true;
+      // Removed last entry as it's only need to know if there more results or not
+      dtos.remove(dtos.size() - 1);
+    }
+    return new GroupMembershipQueryResult(toGroupMembership(dtos), hasMoreResults);
+  }
+
+  private Long userId(String login) {
+    UserDto userDto = userDao.selectActiveUserByLogin(login);
+    if (userDto == null) {
+      throw new NotFoundException("User '"+ login +"' does not exists.");
+    }
+    return userDto.getId();
+  }
+
+  private List<GroupMembership> toGroupMembership(List<GroupMembershipDto> dtos) {
+    List<GroupMembership> groups = newArrayList();
+    for (GroupMembershipDto groupMembershipDto : dtos) {
+      groups.add(groupMembershipDto.toDefaultGroupMembership());
+    }
+    return groups;
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/group/GroupMembershipQueryResult.java b/sonar-server/src/main/java/org/sonar/server/group/GroupMembershipQueryResult.java
new file mode 100644 (file)
index 0000000..647f0a2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.server.group;
+
+import org.sonar.core.user.GroupMembership;
+
+import java.util.List;
+
+public class GroupMembershipQueryResult {
+
+  private List<GroupMembership> groups;
+  private boolean hasMoreResults;
+
+  public GroupMembershipQueryResult(List<GroupMembership> groups, boolean hasMoreResults) {
+    this.groups = groups;
+    this.hasMoreResults = hasMoreResults;
+  }
+
+  public List<GroupMembership> groups() {
+    return groups;
+  }
+
+  public boolean hasMoreResults() {
+    return hasMoreResults;
+  }
+
+}
index 7793f8362dd30285abe95285de4388ea3549b924..d3cda7f66415158af84f8a6cef512e4b65c8aa18 100644 (file)
 package org.sonar.server.group;
 
 import org.sonar.api.ServerComponent;
-import org.sonar.core.user.*;
-import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.core.user.GroupMembershipQuery;
+import org.sonar.server.util.RubyUtils;
 
-import java.util.List;
 import java.util.Map;
 
-import static com.google.common.collect.Lists.newArrayList;
-
 /**
- * Used by ruby code <pre>Internal.groupmembership</pre>
+ * Used by ruby code <pre>Internal.group_membership</pre>
  */
 public class InternalGroupMembershipQueryService implements ServerComponent {
 
-  private final UserDao userDao;
-  private final GroupMembershipDao groupMembershipDao;
+  private final GroupMembershipFinder finder;
 
-  public InternalGroupMembershipQueryService(UserDao userDao, GroupMembershipDao groupMembershipDao) {
-    this.userDao = userDao;
-    this.groupMembershipDao = groupMembershipDao;
+  public InternalGroupMembershipQueryService(GroupMembershipFinder finder) {
+    this.finder = finder;
   }
 
-  public List<GroupMembership> find(Map<String, String> params) {
-    List<GroupMembership> groupMemberships = newArrayList();
-    String user = user(params);
-    UserDto userDto = userDao.selectActiveUserByLogin(user);
-    if (userDto == null) {
-      throw new NotFoundException("User '"+ user +"' does not exists.");
-    }
-    List<GroupMembershipDto> dtos = groupMembershipDao.selectGroups(parseQuery(params, userDto.getId()));
-    for (GroupMembershipDto dto : dtos){
-      groupMemberships.add(dto.toDefaultGroupMembership());
-    }
-    return groupMemberships;
+  public GroupMembershipQueryResult find(Map<String, Object> params) {
+    return finder.find(parseQuery(params));
   }
 
-  private GroupMembershipQuery parseQuery(Map<String, String> params, Long userId) {
+  private GroupMembershipQuery parseQuery(Map<String, Object> params) {
     GroupMembershipQuery.Builder builder = GroupMembershipQuery.builder();
-    builder.memberShip(memberShip(params));
-    builder.searchText(params.get("query"));
-    builder.userId(userId);
+    builder.membership(membership(params));
+    builder.groupSearch((String) params.get("query"));
+    builder.pageIndex(RubyUtils.toInteger(params.get("page")));
+    builder.pageSize(RubyUtils.toInteger(params.get("pageSize")));
+    builder.login((String) params.get("login"));
     return builder.build();
   }
 
-  private String user(Map<String, String> params){
-    return params.get("user");
-  }
-
-  private String memberShip(Map<String, String> params){
-    String selected = params.get("selected");
+  private String membership(Map<String, Object> params) {
+    String selected = (String) params.get("selected");
     if ("selected".equals(selected)) {
-      return GroupMembershipQuery.MEMBER_ONLY;
+      return GroupMembershipQuery.IN;
     } else if ("deselected".equals(selected)) {
-      return GroupMembershipQuery.NOT_MEMBER;
+      return GroupMembershipQuery.OUT;
     } else {
-      return GroupMembershipQuery.ALL;
+      return GroupMembershipQuery.ANY;
     }
   }
 }
index 46afaa57e76d3fa4603ba0ef4f518ff1fa973c47..df8298376297c89bde524eb0cf4345be686cdfb9 100644 (file)
@@ -81,6 +81,7 @@ import org.sonar.server.db.EmbeddedDatabaseFactory;
 import org.sonar.server.db.migrations.DatabaseMigration;
 import org.sonar.server.db.migrations.DatabaseMigrations;
 import org.sonar.server.db.migrations.DatabaseMigrator;
+import org.sonar.server.group.GroupMembershipFinder;
 import org.sonar.server.group.InternalGroupMembershipQueryService;
 import org.sonar.server.issue.*;
 import org.sonar.server.notifications.NotificationCenter;
@@ -274,6 +275,7 @@ public final class Platform {
 
     // groups
     servicesContainer.addSingleton(InternalGroupMembershipQueryService.class);
+    servicesContainer.addSingleton(GroupMembershipFinder.class);
 
     // components
     servicesContainer.addSingleton(DefaultComponentFinder.class);
index 5bd60c5014edfb5f51864965ba2cf72ba678513c..0ef6d3fe6e71286eb937722e8b7a6ba7230d237c 100644 (file)
@@ -26,12 +26,14 @@ class UserGroupsController < ApplicationController
   #
   #
   def search
-    groups = Internal.group_membership.find(params)
+    result = Internal.group_membership.find(params)
+    groups = result.groups()
+    more = result.hasMoreResults()
 
     respond_to do |format|
       format.json {
         render :json => {
-            :more => false,
+            :more => more,
             :results => groups.map { |group| {
                 :id => group.id(),
                 :name => group.name(),
diff --git a/sonar-server/src/test/java/org/sonar/server/group/GroupMembershipFinderTest.java b/sonar-server/src/test/java/org/sonar/server/group/GroupMembershipFinderTest.java
new file mode 100644 (file)
index 0000000..9d3628b
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.server.group;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.core.user.*;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GroupMembershipFinderTest {
+
+  @Mock
+  UserDao userDao;
+
+  @Mock
+  GroupMembershipDao groupMembershipDao;
+
+  GroupMembershipFinder finder;
+
+  @Before
+  public void setUp() throws Exception {
+    when(userDao.selectActiveUserByLogin("arthur")).thenReturn(new UserDto().setId(100L).setName("arthur"));
+    finder = new GroupMembershipFinder(userDao, groupMembershipDao);
+  }
+
+  @Test
+  public void find() throws Exception {
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").build();
+    when(groupMembershipDao.selectGroups(eq(query), anyLong(), anyInt(), anyInt())).thenReturn(
+      newArrayList(new GroupMembershipDto().setId(1L).setName("users"))
+    );
+
+    GroupMembershipQueryResult result = finder.find(query);
+    assertThat(result.groups()).hasSize(1);
+    assertThat(result.hasMoreResults()).isFalse();
+  }
+
+  @Test
+  public void find_with_paging() throws Exception {
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").pageIndex(3).pageSize(10).build();
+    finder.find(query);
+
+    ArgumentCaptor<Integer> argumentOffset = ArgumentCaptor.forClass(Integer.class);
+    ArgumentCaptor<Integer> argumentLimit = ArgumentCaptor.forClass(Integer.class);
+    verify(groupMembershipDao).selectGroups(eq(query), anyLong(), argumentOffset.capture(), argumentLimit.capture());
+
+    assertThat(argumentOffset.getValue()).isEqualTo(20);
+    assertThat(argumentLimit.getValue()).isEqualTo(11);
+  }
+
+  @Test
+  public void find_with_paging_having_more_results() throws Exception {
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").pageIndex(1).pageSize(2).build();
+    when(groupMembershipDao.selectGroups(eq(query), anyLong(), anyInt(), anyInt())).thenReturn(newArrayList(
+      new GroupMembershipDto().setId(1L).setName("group1"),
+      new GroupMembershipDto().setId(2L).setName("group2"),
+      new GroupMembershipDto().setId(3L).setName("group3"))
+    );
+    GroupMembershipQueryResult result = finder.find(query);
+
+    ArgumentCaptor<Integer> argumentOffset = ArgumentCaptor.forClass(Integer.class);
+    ArgumentCaptor<Integer> argumentLimit = ArgumentCaptor.forClass(Integer.class);
+    verify(groupMembershipDao).selectGroups(eq(query), anyLong(), argumentOffset.capture(), argumentLimit.capture());
+
+    assertThat(argumentOffset.getValue()).isEqualTo(0);
+    assertThat(argumentLimit.getValue()).isEqualTo(3);
+    assertThat(result.hasMoreResults()).isTrue();
+  }
+
+  @Test
+  public void find_with_paging_having_no_more_results() throws Exception {
+    GroupMembershipQuery query = GroupMembershipQuery.builder().login("arthur").pageIndex(1).pageSize(10).build();
+    when(groupMembershipDao.selectGroups(eq(query), anyLong(), anyInt(), anyInt())).thenReturn(newArrayList(
+      new GroupMembershipDto().setId(1L).setName("group1"),
+      new GroupMembershipDto().setId(2L).setName("group2"),
+      new GroupMembershipDto().setId(3L).setName("group3"),
+      new GroupMembershipDto().setId(4L).setName("group4"))
+    );
+    GroupMembershipQueryResult result = finder.find(query);
+
+    ArgumentCaptor<Integer> argumentOffset = ArgumentCaptor.forClass(Integer.class);
+    ArgumentCaptor<Integer> argumentLimit = ArgumentCaptor.forClass(Integer.class);
+    verify(groupMembershipDao).selectGroups(eq(query), anyLong(), argumentOffset.capture(), argumentLimit.capture());
+
+    assertThat(argumentOffset.getValue()).isEqualTo(0);
+    assertThat(argumentLimit.getValue()).isEqualTo(11);
+    assertThat(result.hasMoreResults()).isFalse();
+  }
+}
index 4940357182eec9dceb5558425e1ce84bac7f0bd7..bde0bb1e5ecd86dc64256132c6c7902f68d263b7 100644 (file)
@@ -45,16 +45,18 @@ public class InternalGroupMembershipQueryServiceTest extends AbstractDaoTestCase
   public void before() throws Exception {
     GroupMembershipDao groupMembershipDao = new GroupMembershipDao(getMyBatis());
     UserDao userDao = new UserDao(getMyBatis());
-    service = new InternalGroupMembershipQueryService(userDao, groupMembershipDao);
+    GroupMembershipFinder finder = new GroupMembershipFinder(userDao, groupMembershipDao);
+    service = new InternalGroupMembershipQueryService(finder);
   }
 
   @Test
   public void find_all_member_groups() {
     setupData("shared");
 
-    List<GroupMembership> result = service.find(ImmutableMap.of(
-      "user", "user1",
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
       "selected", "all"));
+    List<GroupMembership> result = queryResult.groups();
     assertThat(result).hasSize(3);
     check(result.get(0), "sonar-administrators", false);
     check(result.get(1), "sonar-reviewers", false);
@@ -65,8 +67,9 @@ public class InternalGroupMembershipQueryServiceTest extends AbstractDaoTestCase
   public void find_all_member_groups_when_no_selected_parameter() {
     setupData("shared");
 
-    List<GroupMembership> result = service.find(ImmutableMap.of(
-      "user", "user1"));
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1"));
+    List<GroupMembership> result = queryResult.groups();
     assertThat(result).hasSize(3);
     check(result.get(0), "sonar-administrators", false);
     check(result.get(1), "sonar-reviewers", false);
@@ -77,9 +80,10 @@ public class InternalGroupMembershipQueryServiceTest extends AbstractDaoTestCase
   public void find_member_groups() {
     setupData("shared");
 
-    List<GroupMembership> result = service.find(ImmutableMap.of(
-      "user", "user1",
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
       "selected", "selected"));
+    List<GroupMembership> result = queryResult.groups();
     assertThat(result).hasSize(1);
     check(result.get(0), "sonar-users", true);
   }
@@ -88,21 +92,52 @@ public class InternalGroupMembershipQueryServiceTest extends AbstractDaoTestCase
   public void find_not_member_groups() {
     setupData("shared");
 
-    List<GroupMembership> result = service.find(ImmutableMap.of(
-      "user", "user1",
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
       "selected", "deselected"));
+    List<GroupMembership> result = queryResult.groups();
     assertThat(result).hasSize(2);
     check(result.get(0), "sonar-administrators", false);
     check(result.get(1), "sonar-reviewers", false);
   }
 
+  @Test
+  public void find_with_paging_with_more_results() {
+    setupData("shared");
+
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
+      "selected", "all",
+      "page", 1,
+      "pageSize", 2
+    ));
+    List<GroupMembership> result = queryResult.groups();
+    assertThat(result).hasSize(2);
+    assertThat(queryResult.hasMoreResults()).isTrue();
+  }
+
+  @Test
+  public void find_with_paging_with_no_more_results() {
+    setupData("shared");
+
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
+      "selected", "all",
+      "page", 3,
+      "pageSize", 1
+    ));
+    List<GroupMembership> result = queryResult.groups();
+    assertThat(result).hasSize(1);
+    assertThat(queryResult.hasMoreResults()).isFalse();
+  }
+
   @Test
   public void fail_if_user_not_found() {
     setupData("shared");
 
     try {
-      service.find(ImmutableMap.of(
-        "user", "user_not_existing",
+      service.find(ImmutableMap.<String, Object>of(
+        "login", "user_not_existing",
         "selected", "all"));
       fail();
     } catch (Exception e) {
@@ -114,17 +149,19 @@ public class InternalGroupMembershipQueryServiceTest extends AbstractDaoTestCase
   public void find_matched_groups_name() {
     setupData("shared");
 
-    List<GroupMembership> result = service.find(ImmutableMap.of(
-      "user", "user1",
+    GroupMembershipQueryResult queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
       "selected", "all",
       "query", "user"));
+    List<GroupMembership> result = queryResult.groups();
     assertThat(result).hasSize(1);
     check(result.get(0), "sonar-users", true);
 
-    result = service.find(ImmutableMap.of(
-      "user", "user1",
+    queryResult = service.find(ImmutableMap.<String, Object>of(
+      "login", "user1",
       "selected", "all",
       "query", "sonar"));
+    result = queryResult.groups();
     assertThat(result).hasSize(3);
   }