*/
package org.sonar.db.user;
+import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.impl.utils.TestSystem2;
-import org.sonar.api.user.UserQuery;
import org.sonar.api.utils.DateUtils;
import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbClient;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
db.users().insertUser(user -> user.setLogin("user").setName("User"));
db.users().insertUser(user -> user.setLogin("inactive_user").setName("Disabled").setActive(false));
- List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().includeDeactivated().build());
+ List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().build());
assertThat(users).hasSize(2);
}
db.users().insertUser(user -> user.setLogin("user").setName("User"));
db.users().insertUser(user -> user.setLogin("inactive_user").setName("Disabled").setActive(false));
- List<UserDto> users = underTest.selectUsers(session, UserQuery.ALL_ACTIVES);
+ List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().isActive(true).build());
assertThat(users).extracting(UserDto::getName).containsExactlyInAnyOrder("User");
}
@Test
- public void selectUsersByQuery_filter_by_login() {
- db.users().insertUser(user -> user.setLogin("user").setName("User"));
- db.users().insertUser(user -> user.setLogin("inactive_user").setName("Disabled").setActive(false));
+ public void selectUsersByQuery_whenSearchTextMatchPartOfTheLoginCaseInsensitively_findsTheRightResults() {
+ db.users().insertUser(user -> user.setLogin("tata"));
+ UserDto userToFind = db.users().insertUser(user -> user.setLogin("simon"));
+ UserDto userToFind2 = db.users().insertUser(user -> user.setLogin("ToSimonTo"));
- List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().logins("user", "john").build());
+ UserQuery query = UserQuery.builder().searchText("Simon").build();
+ List<UserDto> users = underTest.selectUsers(session, query);
- assertThat(users).extracting(UserDto::getName).containsExactlyInAnyOrder("User");
+ assertThat(users).usingRecursiveFieldByFieldElementComparator().containsOnly(userToFind, userToFind2);
+ assertThat(underTest.countUsers(session, query)).isEqualTo(2);
}
@Test
- public void selectUsersByQuery_search_by_login_text() {
- db.users().insertUser(user -> user.setLogin("user").setName("User"));
- db.users().insertUser(user -> user.setLogin("sbrandhof").setName("Simon Brandhof"));
+ public void selectUsersByQuery_whenSearchTextMatchPartOfTheNameCaseInsensitively_findsTheRightResults() {
+ db.users().insertUser(user -> user.setName("tata"));
+ UserDto userToFind = db.users().insertUser(user -> user.setName("simon"));
+ UserDto userToFind2 = db.users().insertUser(user -> user.setName("ToSimonTo"));
- List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().searchText("sbr").build());
+ UserQuery query = UserQuery.builder().searchText("Simon").build();
+ List<UserDto> users = underTest.selectUsers(session, query);
- assertThat(users).extracting(UserDto::getLogin).containsExactlyInAnyOrder("sbrandhof");
+ assertThat(users).usingRecursiveFieldByFieldElementComparator().containsOnly(userToFind, userToFind2);
+ assertThat(underTest.countUsers(session, query)).isEqualTo(2);
}
@Test
- public void selectUsersByQuery_search_by_name_text() {
- db.users().insertUser(user -> user.setLogin("user").setName("User"));
- db.users().insertUser(user -> user.setLogin("sbrandhof").setName("Simon Brandhof"));
+ public void selectUsersByQuery_whenSearchTextMatchPartOfTheEmailCaseInsensitively_findsTheRightResults() {
+ db.users().insertUser(user -> user.setEmail("user@user.com"));
+ UserDto userToFind = db.users().insertUser(user -> user.setEmail("simon@brandhof.com"));
+ UserDto userToFind2 = db.users().insertUser(user -> user.setEmail("tagadasimon2@brandhof.com"));
- List<UserDto> users = underTest.selectUsers(session, UserQuery.builder().searchText("Simon").build());
+ UserQuery query = UserQuery.builder().searchText("Simon").build();
+ List<UserDto> users = underTest.selectUsers(session, query);
- assertThat(users).extracting(UserDto::getLogin).containsExactlyInAnyOrder("sbrandhof");
+ assertThat(users).usingRecursiveFieldByFieldElementComparator().containsOnly(userToFind, userToFind2);
+ assertThat(underTest.countUsers(session, query)).isEqualTo(2);
}
@Test
assertThat(users).isEmpty();
}
+ @DataProvider
+ public static Object[][] paginationTestCases() {
+ return new Object[][] {
+ {100, 1, 5},
+ {100, 3, 18},
+ {2075, 41, 50},
+ {0, 2, 5},
+ };
+ }
+
+ @Test
+ @UseDataProvider("paginationTestCases")
+ public void selectUsers_whenUsingPagination_findsTheRightResults(int numberOfUsersToGenerate, int offset, int limit) {
+ Map<String, UserDto> allUsers = generateUsers(numberOfUsersToGenerate);
+
+ UserQuery query = UserQuery.builder().build();
+ List<UserDto> users = underTest.selectUsers(session, query, offset, limit);
+
+ Set<UserDto> expectedUsers = getExpectedUsers(offset, limit, allUsers);
+
+ assertThat(users).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrderElementsOf(expectedUsers);
+ assertThat(underTest.countUsers(session, query)).isEqualTo(numberOfUsersToGenerate);
+ }
+
+ private Map<String, UserDto> generateUsers(int numberOfUsersToGenerate) {
+ if (numberOfUsersToGenerate == 0) {
+ return emptyMap();
+ }
+ return IntStream.range(1000, 1000 + numberOfUsersToGenerate)
+ .mapToObj(i -> db.users().insertUser(user -> user.setLogin(i + "_user").setName(i + "_name")))
+ .collect(toMap(UserDto::getName, Function.identity()));
+ }
+
+ private static Set<UserDto> getExpectedUsers(int offset, int limit, Map<String, UserDto> allUsers) {
+ if (allUsers.isEmpty()) {
+ return emptySet();
+ }
+ return IntStream.range(1000 + (offset - 1) * limit, 1000 + offset * limit)
+ .mapToObj(i -> allUsers.get(i + "_name"))
+ .collect(Collectors.toSet());
+ }
@Test
public void insert_user_with_default_values() {
import java.util.function.Function;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
-import org.sonar.api.user.UserQuery;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
+import org.sonar.db.Pagination;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.audit.model.UserNewValue;
import org.sonar.db.component.ComponentDto;
}
public List<UserDto> selectUsers(DbSession dbSession, UserQuery query) {
- return mapper(dbSession).selectUsers(query);
+ return mapper(dbSession).selectUsers(query, Pagination.all());
+ }
+
+ public List<UserDto> selectUsers(DbSession dbSession, UserQuery query, int offset, int limit) {
+ return mapper(dbSession).selectUsers(query, Pagination.forPage(offset).andSize(limit));
+ }
+
+ public int countUsers(DbSession dbSession, UserQuery userQuery) {
+ return mapper(dbSession).countByQuery(userQuery);
}
public List<UserTelemetryDto> selectUsersForTelemetry(DbSession dbSession) {
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ResultHandler;
-import org.sonar.api.user.UserQuery;
+import org.sonar.db.Pagination;
public interface UserMapper {
@CheckForNull
UserDto selectUserByLogin(String login);
- List<UserDto> selectUsers(UserQuery query);
+ List<UserDto> selectUsers(@Param("query") UserQuery query, @Param("pagination") Pagination pagination);
+
+ int countByQuery(@Param("query") UserQuery query);
List<UserTelemetryDto> selectUsersForTelemetry();
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info 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;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+
+public class UserQuery {
+ private final String searchText;
+ private final Boolean isActive;
+
+ public UserQuery(@Nullable String searchText, @Nullable Boolean isActive) {
+ this.searchText = searchTextToSearchTextSql(searchText);
+ this.isActive = isActive;
+ }
+
+ private static String searchTextToSearchTextSql(@Nullable String text) {
+ String sql = null;
+ if (text != null) {
+ sql = StringUtils.replace(text, "%", "/%");
+ sql = StringUtils.replace(sql, "_", "/_");
+ sql = "%" + sql + "%";
+ }
+ return sql;
+ }
+
+ @CheckForNull
+ private String getSearchText() {
+ return searchText;
+ }
+
+ @CheckForNull
+ private Boolean isActive() {
+ return isActive;
+ }
+
+ public static UserQueryBuilder builder() {
+ return new UserQueryBuilder();
+ }
+
+ public static final class UserQueryBuilder {
+ private String searchText;
+ private Boolean isActive;
+
+ private UserQueryBuilder() {
+ }
+
+ public UserQueryBuilder searchText(@Nullable String searchText) {
+ this.searchText = searchText;
+ return this;
+ }
+
+ public UserQueryBuilder isActive(@Nullable Boolean isActive) {
+ this.isActive = isActive;
+ return this;
+ }
+
+ public UserQuery build() {
+ return new UserQuery(searchText, isActive);
+ }
+ }
+}
SELECT
<include refid="userColumns"/>
FROM users u
- <where>
- <if test="logins != null and logins.size() > 0">
- u.login IN
- <foreach item="login" index="index" collection="logins" open="(" separator="," close=")">
- #{login, jdbcType=VARCHAR}
- </foreach>
- </if>
- <if test="includeDeactivated==false">
- AND u.active=${_true}
+ <include refid="searchByQueryWhereClause"/>
+ ORDER BY u.login
+ limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
+ </select>
+
+ <select id="selectUsers" parameterType="map" resultType="User" databaseId="mssql">
+ SELECT
+ <include refid="userColumns"/>
+ FROM
+ (SELECT
+ u.uuid,
+ u.login,
+ u.name,
+ u.email,
+ u.active,
+ u.scm_accounts,
+ u.salt,
+ u.crypted_password,
+ u.hash_method,
+ u.external_id,
+ u.external_login,
+ u.external_identity_provider,
+ u.user_local,
+ u.reset_password,
+ u.homepage_type,
+ u.homepage_parameter,
+ u.last_connection_date,
+ u.last_sonarlint_connection,
+ u.created_at,
+ u.updated_at
+ FROM users u
+ <include refid="searchByQueryWhereClause"/>
+ ORDER BY u.login
+ offset #{pagination.offset} rows
+ fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only
+ ) u
+ </select>
+
+ <select id="selectUsers" parameterType="map" resultType="User" databaseId="oracle">
+ SELECT
+ <include refid="userColumns"/>
+ FROM
+ (SELECT rownum as rn, t.* from (
+ SELECT
+ u.uuid,
+ u.login,
+ u.name,
+ u.email,
+ u.active,
+ u.scm_accounts,
+ u.salt,
+ u.crypted_password,
+ u.hash_method,
+ u.external_id,
+ u.external_login,
+ u.external_identity_provider,
+ u.user_local,
+ u.reset_password,
+ u.homepage_type,
+ u.homepage_parameter,
+ u.last_connection_date,
+ u.last_sonarlint_connection,
+ u.created_at,
+ u.updated_at
+ FROM users u
+ <include refid="searchByQueryWhereClause"/>
+ ORDER BY u.login ASC
+ ) t
+ ) u
+ WHERE
+ u.rn BETWEEN #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
+ ORDER BY u.rn ASC
+ </select>
+
+ <select id="countByQuery" parameterType="map" resultType="int">
+ SELECT count(1)
+ FROM users u
+ <include refid="searchByQueryWhereClause"/>
+ </select>
+
+ <sql id="searchByQueryWhereClause">
+ <where>
+ <if test="query.isActive != null">
+ u.active=#{query.isActive, jdbcType=BOOLEAN}
</if>
- <if test="searchText != null">
- AND (u.login LIKE #{searchTextSql, jdbcType=VARCHAR} ESCAPE '/' OR u.name LIKE #{searchTextSql, jdbcType=VARCHAR} ESCAPE '/')
+ <if test="query.searchText != null">
+ AND (
+ (lower(u.login) LIKE lower(#{query.searchText, jdbcType=VARCHAR}) ESCAPE '/')
+ OR (lower(u.name) LIKE lower(#{query.searchText, jdbcType=VARCHAR}) ESCAPE '/')
+ OR (lower(u.email) LIKE lower(#{query.searchText, jdbcType=VARCHAR}) ESCAPE '/')
+ )
</if>
</where>
- ORDER BY u.name
- </select>
+
+ </sql>
<select id="selectUsersForTelemetry" parameterType="map" resultType="UserTelemetry">
SELECT
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
-import org.sonar.api.user.UserQuery;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserQuery;
import org.sonar.server.es.EsClient;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.EsUtils;
}
private void verifyThatUserIsAnonymized(String uuid) {
- List<UserDto> users = dbClient.userDao().selectUsers(db.getSession(), UserQuery.builder().includeDeactivated().build());
+ List<UserDto> users = dbClient.userDao().selectUsers(db.getSession(), UserQuery.builder().isActive(false).build());
assertThat(users).hasSize(1);
UserDto anonymized = dbClient.userDao().selectByUuid(db.getSession(), uuid);
import org.junit.Test;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
-import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
-import org.sonar.server.es.EsTester;
import org.sonar.server.issue.AvatarResolverImpl;
import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Common.Paging;
import org.sonarqube.ws.Users.SearchWsResponse;
public class SearchActionIT {
- @Rule
- public EsTester es = EsTester.create();
-
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
public DbTester db = DbTester.create();
private ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
- private UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
- private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
- private WsActionTester ws = new WsActionTester(new SearchAction(userSession, index, db.getDbClient(), new AvatarResolverImpl(), managedInstanceService));
+ private WsActionTester ws = new WsActionTester(new SearchAction(userSession, db.getDbClient(), new AvatarResolverImpl(), managedInstanceService));
@Test
public void search_for_all_active_users() {
UserDto user2 = db.users().insertUser();
UserDto user3 = db.users().insertUser(u -> u.setActive(false));
- userIndexer.indexAll();
userSession.logIn();
SearchWsResponse response = ws.newRequest()
public void search_deactivated_users() {
UserDto user1 = db.users().insertUser(u -> u.setActive(false));
UserDto user2 = db.users().insertUser(u -> u.setActive(true));
- userIndexer.indexAll();
userSession.logIn();
SearchWsResponse response = ws.newRequest()
.setEmail("user@mail.com")
.setLocal(true)
.setScmAccounts(singletonList("user1")));
- userIndexer.indexAll();
assertThat(ws.newRequest()
.setParam("q", "user-%_%-")
@Test
public void return_avatar() {
UserDto user = db.users().insertUser(u -> u.setEmail("john@doe.com"));
- userIndexer.indexAll();
userSession.logIn();
SearchWsResponse response = ws.newRequest()
UserDto nonManagedUser = db.users().insertUser(u -> u.setEmail("john@doe.com"));
UserDto managedUser = db.users().insertUser(u -> u.setEmail("externalUser@doe.com"));
mockUsersAsManaged(managedUser.getUuid());
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
SearchWsResponse response = ws.newRequest()
@Test
public void return_scm_accounts() {
UserDto user = db.users().insertUser(u -> u.setScmAccounts(asList("john1", "john2")));
- userIndexer.indexAll();
+
userSession.logIn();
SearchWsResponse response = ws.newRequest()
UserDto user = db.users().insertUser();
db.users().insertToken(user);
db.users().insertToken(user);
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
assertThat(ws.newRequest()
@Test
public void return_email_only_when_system_administer() {
UserDto user = db.users().insertUser();
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
assertThat(ws.newRequest()
@Test
public void return_user_not_having_email() {
UserDto user = db.users().insertUser(u -> u.setEmail(null));
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
SearchWsResponse response = ws.newRequest()
GroupDto group3 = db.users().insertGroup("group3");
db.users().insertMember(group1, user);
db.users().insertMember(group2, user);
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
assertThat(ws.newRequest()
@Test
public void return_external_information() {
UserDto user = db.users().insertUser();
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
SearchWsResponse response = ws.newRequest()
@Test
public void return_external_identity_only_when_system_administer() {
UserDto user = db.users().insertUser();
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
assertThat(ws.newRequest()
db.users().insertToken(user);
GroupDto group = db.users().insertGroup();
db.users().insertMember(group, user);
- userIndexer.indexAll();
userSession.anonymous();
SearchWsResponse response = ws.newRequest()
UserDto userWithLastConnectionDate = db.users().insertUser();
db.users().updateLastConnectionDate(userWithLastConnectionDate, 10_000_000_000L);
UserDto userWithoutLastConnectionDate = db.users().insertUser();
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
SearchWsResponse response = ws.newRequest()
GroupDto group = db.users().insertGroup();
db.users().insertMember(group, user);
UserDto otherUser = db.users().insertUser();
- userIndexer.indexAll();
userSession.logIn(user);
assertThat(ws.newRequest().setParam("q", user.getLogin())
tuple(user.getLogin(), user.getName(), false, false, true, true, true, false, false, false));
}
+ @Test
+ public void search_whenNoPagingInformationProvided_setsDefaultValues() {
+ userSession.logIn();
+ IntStream.rangeClosed(0, 9).forEach(i -> db.users().insertUser(u -> u.setLogin("user-" + i).setName("User " + i)));
+
+ SearchWsResponse response = ws.newRequest()
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getPaging().getTotal()).isEqualTo(10);
+ assertThat(response.getPaging().getPageIndex()).isEqualTo(1);
+ assertThat(response.getPaging().getPageSize()).isEqualTo(50);
+ }
+
@Test
public void search_with_paging() {
userSession.logIn();
IntStream.rangeClosed(0, 9).forEach(i -> db.users().insertUser(u -> u.setLogin("user-" + i).setName("User " + i)));
- userIndexer.indexAll();
SearchWsResponse response = ws.newRequest()
.setParam(Param.PAGE_SIZE, "5")
db.users().insertToken(simon);
db.users().insertToken(simon);
db.users().insertToken(fmallet);
- userIndexer.indexAll();
userSession.logIn().setSystemAdministrator();
String response = ws.newRequest().execute().getInput();
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserQuery;
import org.sonar.server.es.SearchOptions;
-import org.sonar.server.es.SearchResult;
import org.sonar.server.issue.AvatarResolver;
import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.user.UserSession;
-import org.sonar.server.user.index.UserDoc;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.index.UserQuery;
import org.sonarqube.ws.Users;
import org.sonarqube.ws.Users.SearchWsResponse;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.emptyToNull;
+import static java.util.Comparator.comparing;
import static java.lang.Boolean.TRUE;
import static java.util.Optional.ofNullable;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
private static final int MAX_PAGE_SIZE = 500;
private final UserSession userSession;
- private final UserIndex userIndex;
private final DbClient dbClient;
private final AvatarResolver avatarResolver;
private final ManagedInstanceService managedInstanceService;
- public SearchAction(UserSession userSession, UserIndex userIndex, DbClient dbClient, AvatarResolver avatarResolver,
+ public SearchAction(UserSession userSession, DbClient dbClient, AvatarResolver avatarResolver,
ManagedInstanceService managedInstanceService) {
this.userSession = userSession;
- this.userIndex = userIndex;
this.dbClient = dbClient;
this.avatarResolver = avatarResolver;
this.managedInstanceService = managedInstanceService;
"Field 'lastConnectionDate' is only updated every hour, so it may not be accurate, for instance when a user authenticates many times in less than one hour.")
.setSince("3.6")
.setChangelog(
+ new Change("10.0", "'q' parameter values is now always performing a case insensitive match"),
new Change("10.0", "Response includes 'managed' field."),
new Change("9.7", "New parameter 'deactivated' to optionally search for deactivated users"),
new Change("7.7", "New field 'lastConnectionDate' is added to response"),
action.createParam(TEXT_QUERY)
.setMinimumLength(2)
.setDescription("Filter on login, name and email.<br />" +
- "This parameter can either be case sensitive and perform an exact match, or case insensitive and perform a partial match (contains), depending on the scenario:<br />" +
- "<ul>" +
- " <li>" +
- " If the search query is <em>less or equal to 15 characters</em>, then the query is <em>case insensitive</em>, and will match any login, name, or email, that " +
- " <em>contains</em> the search query." +
- " </li>" +
- " <li>" +
- " If the search query is <em>greater than 15 characters</em>, then the query becomes <em>case sensitive</em>, and will match any login, name, or email, that " +
- " <em>exactly matches</em> the search query." +
- " </li>" +
- "</ul>");
+ "This parameter can either perform an exact match, or a partial match (contains), it is case insensitive.");
action.createParam(DEACTIVATED_PARAM)
.setSince("9.7")
.setDescription("Return deactivated users instead of active users")
}
private Users.SearchWsResponse doHandle(SearchRequest request) {
- SearchOptions options = new SearchOptions().setPage(request.getPage(), request.getPageSize());
- SearchResult<UserDoc> result = userIndex.search(UserQuery.builder().setActive(!request.isDeactivated()).setTextQuery(request.getQuery()).build(), options);
+ UserQuery userQuery = buildUserQuery(request);
try (DbSession dbSession = dbClient.openSession(false)) {
- List<String> logins = result.getDocs().stream().map(UserDoc::login).collect(toList());
+ List<UserDto> users = fetchUsersAndSortByLogin(request, dbSession, userQuery);
+ int totalUsers = dbClient.userDao().countUsers(dbSession, userQuery);
+
+ List<String> logins = users.stream().map(UserDto::getLogin).collect(toList());
Multimap<String, String> groupsByLogin = dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, logins);
- List<UserDto> users = dbClient.userDao().selectByOrderedLogins(dbSession, logins);
Map<String, Integer> tokenCountsByLogin = dbClient.userTokenDao().countTokensByUsers(dbSession, users);
Map<String, Boolean> userUuidToIsManaged = managedInstanceService.getUserUuidToManaged(dbSession, getUserUuids(users));
- Paging paging = forPageIndex(request.getPage()).withPageSize(request.getPageSize()).andTotal((int) result.getTotal());
+ Paging paging = forPageIndex(request.getPage()).withPageSize(request.getPageSize()).andTotal(totalUsers);
return buildResponse(users, groupsByLogin, tokenCountsByLogin, userUuidToIsManaged, paging);
}
}
return users.stream().map(UserDto::getUuid).collect(Collectors.toSet());
}
+ private static UserQuery buildUserQuery(SearchRequest request) {
+ return UserQuery.builder()
+ .isActive(!request.isDeactivated())
+ .searchText(request.getQuery())
+ .build();
+ }
+
+ private List<UserDto> fetchUsersAndSortByLogin(SearchRequest request, DbSession dbSession, UserQuery userQuery) {
+ return dbClient.userDao().selectUsers(dbSession, userQuery, request.getPage(), request.getPageSize())
+ .stream()
+ .sorted(comparing(UserDto::getLogin))
+ .toList();
+ }
+
private SearchWsResponse buildResponse(List<UserDto> users, Multimap<String, String> groupsByLogin, Map<String, Integer> tokenCountsByLogin,
Map<String, Boolean> userUuidToIsManaged, Paging paging) {
SearchWsResponse.Builder responseBuilder = newBuilder();
this.deactivated = builder.deactivated;
}
- @CheckForNull
public Integer getPage() {
return page;
}
- @CheckForNull
public Integer getPageSize() {
return pageSize;
}
// enforce factory method use
}
- public Builder setPage(@Nullable Integer page) {
+ public Builder setPage(Integer page) {
this.page = page;
return this;
}
- public Builder setPageSize(@Nullable Integer pageSize) {
+ public Builder setPageSize(Integer pageSize) {
this.pageSize = pageSize;
return this;
}