From f5f73537f6b8bf961cd189c93ed2b256dd66c7a2 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 14 May 2013 18:42:51 +0200 Subject: [PATCH] SONAR-4323 add search by free text and support of Select2 --- .../org/sonar/core/user/UserMapper.xml | 3 +++ .../java/org/sonar/core/user/UserDaoTest.java | 20 ++++++++++++++++ .../user/UserDaoTest/selectUsersByText.xml | 24 +++++++++++++++++++ .../org/sonar/api/issue/RubyIssueService.java | 2 ++ .../org/sonar/api/user/RubyUserService.java | 1 + .../java/org/sonar/api/user/UserQuery.java | 22 +++++++++++++++++ .../org/sonar/api/user/UserQueryTest.java | 9 +++++++ .../server/user/DefaultRubyUserService.java | 1 + .../app/controllers/api/users_controller.rb | 14 ++++++++--- .../org/sonar/wsclient/user/UserQuery.java | 10 ++++++++ .../sonar/wsclient/user/UserQueryTest.java | 6 +++++ 11 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 sonar-core/src/test/resources/org/sonar/core/user/UserDaoTest/selectUsersByText.xml diff --git a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml index 5bc41686c67..adfeea60245 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml @@ -42,6 +42,9 @@ and u.active=${_true} + + and (u.login like #{searchTextSql} or u.name like #{searchTextSql}) + order by u.name diff --git a/sonar-core/src/test/java/org/sonar/core/user/UserDaoTest.java b/sonar-core/src/test/java/org/sonar/core/user/UserDaoTest.java index b6665245efe..a91acd2bd66 100644 --- a/sonar-core/src/test/java/org/sonar/core/user/UserDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/user/UserDaoTest.java @@ -101,6 +101,26 @@ public class UserDaoTest extends AbstractDaoTestCase { assertThat(users.get(0).getName()).isEqualTo("Marius"); } + @Test + public void selectUsersByQuery_search_by_login_text() throws Exception { + setupData("selectUsersByText"); + + UserQuery query = UserQuery.builder().searchText("sbr").build(); + List users = dao.selectUsers(query); + assertThat(users).hasSize(1); + assertThat(users.get(0).getLogin()).isEqualTo("sbrandhof"); + } + + @Test + public void selectUsersByQuery_search_by_name_text() throws Exception { + setupData("selectUsersByText"); + + UserQuery query = UserQuery.builder().searchText("Simon").build(); + List users = dao.selectUsers(query); + assertThat(users).hasSize(1); + assertThat(users.get(0).getLogin()).isEqualTo("sbrandhof"); + } + @Test public void selectGroupByName() { setupData("selectGroupByName"); diff --git a/sonar-core/src/test/resources/org/sonar/core/user/UserDaoTest/selectUsersByText.xml b/sonar-core/src/test/resources/org/sonar/core/user/UserDaoTest/selectUsersByText.xml new file mode 100644 index 00000000000..4eb95dae80f --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/user/UserDaoTest/selectUsersByText.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/RubyIssueService.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/RubyIssueService.java index 6f5d6f6a573..61d7ec2bfa2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/RubyIssueService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/RubyIssueService.java @@ -33,6 +33,8 @@ import java.util.Map; */ public interface RubyIssueService extends ServerComponent { + IssueQueryResult find(String issueKey); + /** * Search for issues. *

diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java b/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java index a1e7180a453..cb46f2fa95c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java @@ -38,6 +38,7 @@ public interface RubyUserService extends ServerComponent { *

* Optional parameters are: *

    + *
  • q to match all the logins or names containing this search query
  • *
  • logins, as an array of strings (['simon', 'julien']) or a comma-separated list of logins ('simon,julien')
  • *
  • includeDeactivated as a boolean. By Default deactivated users are excluded from query.
  • *
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/user/UserQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/user/UserQuery.java index d4c84c728b4..027be9f5e60 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/user/UserQuery.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/user/UserQuery.java @@ -19,6 +19,8 @@ */ package org.sonar.api.user; +import org.apache.commons.lang.StringUtils; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.util.Arrays; @@ -33,10 +35,16 @@ public class UserQuery { private final Collection logins; private final boolean includeDeactivated; + private final String searchText; + + // for internal use in MyBatis + final String searchTextSql; private UserQuery(Builder builder) { this.logins = builder.logins; this.includeDeactivated = builder.includeDeactivated; + this.searchText = builder.searchText; + this.searchTextSql = (searchText !=null ? "%" + searchText + "%" : null); } @CheckForNull @@ -48,6 +56,14 @@ public class UserQuery { return includeDeactivated; } + /** + * Search for logins or names containing a given string + */ + @CheckForNull + public String searchText() { + return searchText; + } + public static Builder builder() { return new Builder(); } @@ -55,6 +71,7 @@ public class UserQuery { public static class Builder { private boolean includeDeactivated = false; private Collection logins; + private String searchText; private Builder() { } @@ -75,6 +92,11 @@ public class UserQuery { return this; } + public Builder searchText(@Nullable String s) { + this.searchText = StringUtils.defaultIfBlank(s, null); + return this; + } + public UserQuery build() { if (logins != null && logins.size() >= 1000) { throw new IllegalArgumentException("Max number of logins is 1000. Got " + logins.size()); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/user/UserQueryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/user/UserQueryTest.java index e6dbc92a126..3cbc5822f1c 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/user/UserQueryTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/user/UserQueryTest.java @@ -33,6 +33,8 @@ public class UserQueryTest { public void test_all_actives() throws Exception { assertThat(UserQuery.ALL_ACTIVES.includeDeactivated()).isFalse(); assertThat(UserQuery.ALL_ACTIVES.logins()).isNull(); + assertThat(UserQuery.ALL_ACTIVES.searchText()).isNull(); + assertThat(UserQuery.ALL_ACTIVES.searchTextSql).isNull(); } @Test @@ -65,4 +67,11 @@ public class UserQueryTest { assertThat(e).hasMessage("Max number of logins is 1000. Got 1010"); } } + + @Test + public void test_searchText() throws Exception { + UserQuery query = UserQuery.builder().searchText("sim").build(); + assertThat(query.searchText()).isEqualTo("sim"); + assertThat(query.searchTextSql).isEqualTo("%sim%"); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java b/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java index db042c5a8d5..d561d7230a5 100644 --- a/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java +++ b/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java @@ -52,6 +52,7 @@ public class DefaultRubyUserService implements RubyUserService { builder.includeDeactivated(); } builder.logins(RubyUtils.toStrings(params.get("logins"))); + builder.searchText((String)params.get("q")); return builder.build(); } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/users_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/users_controller.rb index 4adc531cb4f..76f73e5b9d3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/users_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/users_controller.rb @@ -29,9 +29,17 @@ class Api::UsersController < Api::ApiController # def search users = Api.users.find(params) - hash = { - :users => users.map { |user| User.to_hash(user) } - } + + select2_format=(params[:f]=='s2') + if select2_format + hash = { + :more => false, + :results => users.map { |user| {:id => user.login, :text => "#{user.name} (#{user.login})"} } + } + else + hash = {:users => users.map { |user| User.to_hash(user) }} + end + respond_to do |format| format.json { render :json => jsonp(hash) } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java index 49de3a8e506..07f802fb8a9 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java @@ -21,6 +21,7 @@ package org.sonar.wsclient.user; import org.sonar.wsclient.internal.EncodingUtils; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; @@ -49,6 +50,15 @@ public class UserQuery { return this; } + public UserQuery searchText(@Nullable String s) { + if (s != null) { + params.put("q", s); + } else { + params.remove("q"); + } + return this; + } + Map urlParams() { return params; } diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java index 752f85c1da4..fbb9b4d26a1 100644 --- a/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java +++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java @@ -48,4 +48,10 @@ public class UserQueryTest { UserQuery query = UserQuery.create().logins("simon").logins("loic"); assertThat(query.urlParams().get("logins")).isEqualTo("loic"); } + + @Test + public void should_search_by_text() throws Exception { + UserQuery query = UserQuery.create().searchText("sim"); + assertThat(query.urlParams().get("q")).isEqualTo("sim"); + } } -- 2.39.5