]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15514 Create api/qualitygates/search_users service
authorZipeng WU <zipeng.wu@sonarsource.com>
Wed, 13 Oct 2021 14:35:52 +0000 (16:35 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 22 Oct 2021 20:03:27 +0000 (20:03 +0000)
33 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGateGroupsQuery.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGatePermissionQuery.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfileGroupsQuery.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfilePermissionQuery.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchUsersQuery.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/UserMembershipDto.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchGroupsQuery.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchPermissionQuery.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchUserMembershipDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchGroupsAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchUsersAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualitygate/ws/search_users-example.json [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/SearchUsersActionTest.java [new file with mode: 0644]
sonar-ws/src/main/protobuf/ws-qualitygates.proto

index 76b901601a7a4cd006c9fd82c2ed0a0bcc41ce36..a95d8f5235f0cb3206ee1d39935e331d5cdc0017 100644 (file)
@@ -27,7 +27,7 @@ import org.sonar.db.DbSession;
 import org.sonar.db.Pagination;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.SearchGroupMembershipDto;
-import org.sonar.db.user.SearchGroupsQuery;
+import org.sonar.db.user.SearchPermissionQuery;
 
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
@@ -61,11 +61,11 @@ public class QualityGateGroupPermissionsDao implements Dao {
     return dbSession.getMapper(QualityGateGroupPermissionsMapper.class);
   }
 
-  public List<SearchGroupMembershipDto> selectByQuery(DbSession dbSession, SearchGroupsQuery query, Pagination pagination) {
+  public List<SearchGroupMembershipDto> selectByQuery(DbSession dbSession, SearchPermissionQuery query, Pagination pagination) {
     return mapper(dbSession).selectByQuery(query, pagination);
   }
 
-  public int countByQuery(DbSession dbSession, SearchGroupsQuery query) {
+  public int countByQuery(DbSession dbSession, SearchPermissionQuery query) {
     return mapper(dbSession).countByQuery(query);
   }
 }
index b7c23e1fa993591a1c509f1e955dcb9a7d0560d6..2d0800155cfd3f470a2c484758ffb8f5f93a2de0 100644 (file)
@@ -23,7 +23,7 @@ import java.util.List;
 import org.apache.ibatis.annotations.Param;
 import org.sonar.db.Pagination;
 import org.sonar.db.user.SearchGroupMembershipDto;
-import org.sonar.db.user.SearchGroupsQuery;
+import org.sonar.db.user.SearchPermissionQuery;
 
 public interface QualityGateGroupPermissionsMapper {
 
@@ -33,7 +33,7 @@ public interface QualityGateGroupPermissionsMapper {
 
   void insert(@Param("dto") QualityGateGroupPermissionsDto dto, @Param("now") long now);
 
-  List<SearchGroupMembershipDto> selectByQuery(@Param("query") SearchGroupsQuery query, @Param("pagination") Pagination pagination);
+  List<SearchGroupMembershipDto> selectByQuery(@Param("query") SearchPermissionQuery query, @Param("pagination") Pagination pagination);
 
-  int countByQuery(@Param("query") SearchGroupsQuery query);
+  int countByQuery(@Param("query") SearchPermissionQuery query);
 }
index e79dc9ecfde2b1a1628ec098360d16cd99de3fb2..6bc72474a475059e9117ce8af8e9df3786afeb31 100644 (file)
  */
 package org.sonar.db.qualitygate;
 
+import java.util.List;
 import javax.annotation.Nullable;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
+import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchPermissionQuery;
+import org.sonar.db.user.SearchUserMembershipDto;
 import org.sonar.db.user.UserDto;
 
 public class QualityGateUserPermissionsDao implements Dao {
@@ -52,6 +56,14 @@ public class QualityGateUserPermissionsDao implements Dao {
     mapper(dbSession).insert(dto, system2.now());
   }
 
+  public List<SearchUserMembershipDto> selectByQuery(DbSession dbSession, SearchPermissionQuery query, Pagination pagination) {
+    return mapper(dbSession).selectByQuery(query, pagination);
+  }
+
+  public int countByQuery(DbSession dbSession, SearchPermissionQuery query) {
+    return mapper(dbSession).countByQuery(query);
+  }
+
   private static QualityGateUserPermissionsMapper mapper(DbSession dbSession) {
     return dbSession.getMapper(QualityGateUserPermissionsMapper.class);
   }
index b67289f69dc6b41c365d57eec296ecc52a51dc18..3f28de143ebd997237be23b86d1d0aadc480e0b4 100644 (file)
  */
 package org.sonar.db.qualitygate;
 
+import java.util.List;
 import org.apache.ibatis.annotations.Param;
+import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchPermissionQuery;
+import org.sonar.db.user.SearchUserMembershipDto;
 
 public interface QualityGateUserPermissionsMapper {
 
@@ -27,4 +31,7 @@ public interface QualityGateUserPermissionsMapper {
 
   void insert(@Param("dto") QualityGateUserPermissionsDto dto, @Param("now") long now);
 
+  List<SearchUserMembershipDto> selectByQuery(@Param("query") SearchPermissionQuery query, @Param("pagination") Pagination pagination);
+
+  int countByQuery(@Param("query") SearchPermissionQuery query);
 }
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGateGroupsQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGateGroupsQuery.java
deleted file mode 100644 (file)
index 7bfeef9..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.qualitygate;
-
-import java.util.Locale;
-import org.sonar.db.user.SearchGroupsQuery;
-
-import static org.sonar.db.DaoUtils.buildLikeValue;
-import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
-
-public class SearchQualityGateGroupsQuery extends SearchGroupsQuery {
-
-  private final String qualityGateUuid;
-
-  public SearchQualityGateGroupsQuery(Builder builder) {
-    this.qualityGateUuid = builder.qualityGate.getUuid();
-    this.query = builder.getQuery();
-    this.membership = builder.getMembership();
-    this.querySqlLowercase = query == null ? null : buildLikeValue(query, BEFORE_AND_AFTER).toLowerCase(Locale.ENGLISH);
-  }
-
-  public String getQualityGateUuid() {
-    return qualityGateUuid;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder extends SearchGroupsQuery.Builder<Builder> {
-    private QualityGateDto qualityGate;
-
-    public Builder setQualityGate(QualityGateDto qualityGate) {
-      this.qualityGate = qualityGate;
-      return this;
-    }
-
-    public SearchQualityGateGroupsQuery build() {
-      initMembership();
-      return new SearchQualityGateGroupsQuery(this);
-    }
-  }
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGatePermissionQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGatePermissionQuery.java
new file mode 100644 (file)
index 0000000..c0a5566
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.qualitygate;
+
+import java.util.Locale;
+import org.sonar.db.user.SearchPermissionQuery;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.db.DaoUtils.buildLikeValue;
+import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
+
+public class SearchQualityGatePermissionQuery extends SearchPermissionQuery {
+
+  private final String qualityGateUuid;
+
+  public SearchQualityGatePermissionQuery(Builder builder) {
+    this.qualityGateUuid = builder.qualityGate.getUuid();
+    this.query = builder.getQuery();
+    this.membership = builder.getMembership();
+    this.querySql = query == null ? null : buildLikeValue(query, BEFORE_AND_AFTER);
+    this.querySqlLowercase = querySql == null ? null : querySql.toLowerCase(Locale.ENGLISH);
+  }
+
+  public String getQualityGateUuid() {
+    return qualityGateUuid;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder extends SearchPermissionQuery.Builder<Builder> {
+    private QualityGateDto qualityGate;
+
+    public Builder setQualityGate(QualityGateDto qualityGate) {
+      this.qualityGate = qualityGate;
+      return this;
+    }
+
+    public SearchQualityGatePermissionQuery build() {
+      requireNonNull(qualityGate, "Quality gate cant be null.");
+      initMembership();
+      return new SearchQualityGatePermissionQuery(this);
+    }
+  }
+}
index 8b324e9c89819b9c2978afc17fc221ded4ace78e..484a406dd0df1d6b27bd8df21a80b064acc443c1 100644 (file)
@@ -51,11 +51,11 @@ public class QProfileEditGroupsDao implements Dao {
       .isEmpty();
   }
 
-  public int countByQuery(DbSession dbSession, SearchQualityProfileGroupsQuery query) {
+  public int countByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query) {
     return mapper(dbSession).countByQuery(query);
   }
 
-  public List<SearchGroupMembershipDto> selectByQuery(DbSession dbSession, SearchQualityProfileGroupsQuery query, Pagination pagination) {
+  public List<SearchGroupMembershipDto> selectByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query, Pagination pagination) {
     return mapper(dbSession).selectByQuery(query, pagination);
   }
 
index d8cb1e723e6981a55fdb9cc8f169f4621a384e24..e849f4c984bd3936e90b52feefc5d0aecf6a82bb 100644 (file)
@@ -29,9 +29,9 @@ public interface QProfileEditGroupsMapper {
 
   List<QProfileEditGroupsDto> selectByQProfileAndGroups(@Param("qProfileUuid") String qProfileUuid, @Param("groupUuids") List<String> groupUuids);
 
-  int countByQuery(@Param("query") SearchQualityProfileGroupsQuery query);
+  int countByQuery(@Param("query") SearchQualityProfilePermissionQuery query);
 
-  List<SearchGroupMembershipDto> selectByQuery(@Param("query") SearchQualityProfileGroupsQuery query, @Param("pagination") Pagination pagination);
+  List<SearchGroupMembershipDto> selectByQuery(@Param("query") SearchQualityProfilePermissionQuery query, @Param("pagination") Pagination pagination);
 
   List<String> selectQProfileUuidsByGroups(@Param("groupUuids") List<String> groupUuids);
 
index 3b66fbeb45242d0d73561fb8ffe6ae7139909207..911867520a1cbd893cfcc19d99c16af38399fdfa 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchUserMembershipDto;
 import org.sonar.db.user.UserDto;
 
 import static org.sonar.core.util.stream.MoreCollectors.toList;
@@ -41,15 +42,15 @@ public class QProfileEditUsersDao implements Dao {
     return mapper(dbSession).selectByQProfileAndUser(profile.getKee(), user.getUuid()) != null;
   }
 
-  public int countByQuery(DbSession dbSession, SearchUsersQuery query) {
+  public int countByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query) {
     return mapper(dbSession).countByQuery(query);
   }
 
-  public List<UserMembershipDto> selectByQuery(DbSession dbSession, SearchUsersQuery query, Pagination pagination) {
+  public List<SearchUserMembershipDto> selectByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query, Pagination pagination) {
     return mapper(dbSession).selectByQuery(query, pagination);
   }
 
-  public List<String> selectQProfileUuidsByUser(DbSession dbSession,UserDto userDto) {
+  public List<String> selectQProfileUuidsByUser(DbSession dbSession, UserDto userDto) {
     return mapper(dbSession).selectQProfileUuidsByUser(userDto.getUuid());
   }
 
index e3b24df061b065cc57cd1c118444107fb9b444e5..fdd09a5cc8b0caa22fb829bdd68643e00d4f9597 100644 (file)
@@ -23,14 +23,15 @@ import java.util.Collection;
 import java.util.List;
 import org.apache.ibatis.annotations.Param;
 import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchUserMembershipDto;
 
 public interface QProfileEditUsersMapper {
 
   QProfileEditUsersDto selectByQProfileAndUser(@Param("qProfileUuid") String qProfileUuid, @Param("userUuid") String userUuid);
 
-  int countByQuery(@Param("query") SearchUsersQuery query);
+  int countByQuery(@Param("query") SearchQualityProfilePermissionQuery query);
 
-  List<UserMembershipDto> selectByQuery(@Param("query") SearchUsersQuery query, @Param("pagination") Pagination pagination);
+  List<SearchUserMembershipDto> selectByQuery(@Param("query") SearchQualityProfilePermissionQuery query, @Param("pagination") Pagination pagination);
 
   List<String> selectQProfileUuidsByUser(@Param("userUuid") String userUuid);
 
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfileGroupsQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfileGroupsQuery.java
deleted file mode 100644 (file)
index cdbe85e..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.qualityprofile;
-
-import java.util.Locale;
-import org.sonar.db.user.SearchGroupsQuery;
-
-import static org.sonar.db.DaoUtils.buildLikeValue;
-import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
-
-public class SearchQualityProfileGroupsQuery extends SearchGroupsQuery {
-
-  private final String qProfileUuid;
-
-  public SearchQualityProfileGroupsQuery(Builder builder) {
-    this.qProfileUuid = builder.profile.getKee();
-    this.query = builder.getQuery();
-    this.membership = builder.getMembership();
-    this.querySqlLowercase = query == null ? null : buildLikeValue(query, BEFORE_AND_AFTER).toLowerCase(Locale.ENGLISH);
-  }
-
-  public String getQProfileUuid() {
-    return qProfileUuid;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder extends SearchGroupsQuery.Builder<Builder> {
-    private QProfileDto profile;
-
-    public Builder setProfile(QProfileDto profile) {
-      this.profile = profile;
-      return this;
-    }
-
-    public SearchQualityProfileGroupsQuery build() {
-      initMembership();
-      return new SearchQualityProfileGroupsQuery(this);
-    }
-  }
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfilePermissionQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfilePermissionQuery.java
new file mode 100644 (file)
index 0000000..49e7a0e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.qualityprofile;
+
+import java.util.Locale;
+import org.sonar.db.user.SearchPermissionQuery;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.db.DaoUtils.buildLikeValue;
+import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
+
+public class SearchQualityProfilePermissionQuery extends SearchPermissionQuery {
+
+  private final String qProfileUuid;
+
+  public SearchQualityProfilePermissionQuery(Builder builder) {
+    this.qProfileUuid = builder.profile.getKee();
+    this.query = builder.getQuery();
+    this.membership = builder.getMembership();
+    this.querySql = query == null ? null : buildLikeValue(query, BEFORE_AND_AFTER);
+    this.querySqlLowercase = querySql == null ? null : querySql.toLowerCase(Locale.ENGLISH);
+  }
+
+  public String getQProfileUuid() {
+    return qProfileUuid;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder extends SearchPermissionQuery.Builder<Builder> {
+    private QProfileDto profile;
+
+    public Builder setProfile(QProfileDto profile) {
+      this.profile = profile;
+      return this;
+    }
+
+    public SearchQualityProfilePermissionQuery build() {
+      requireNonNull(profile, "Quality profile cant be null.");
+      initMembership();
+      return new SearchQualityProfilePermissionQuery(this);
+    }
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchUsersQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchUsersQuery.java
deleted file mode 100644 (file)
index 7a60fc3..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.qualityprofile;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Locale;
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-import static org.sonar.db.DaoUtils.buildLikeValue;
-import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
-
-public class SearchUsersQuery {
-
-  public static final String ANY = "ANY";
-  public static final String IN = "IN";
-  public static final String OUT = "OUT";
-  public static final Set<String> AVAILABLE_MEMBERSHIPS = ImmutableSet.of(ANY, IN, OUT);
-
-  private final String qProfileUuid;
-  private final String query;
-  private final String membership;
-
-  // for internal use in MyBatis
-  final String querySql;
-  final String querySqlLowercase;
-
-  private SearchUsersQuery(Builder builder) {
-    this.qProfileUuid = builder.profile.getKee();
-    this.query = builder.query;
-    this.membership = builder.membership;
-    this.querySql = query == null ? null : buildLikeValue(query, BEFORE_AND_AFTER);
-    this.querySqlLowercase = querySql == null ? null : querySql.toLowerCase(Locale.ENGLISH);
-  }
-
-  public String getQProfileUuid() {
-    return qProfileUuid;
-  }
-
-  public String getMembership() {
-    return membership;
-  }
-
-  @CheckForNull
-  public String getQuery() {
-    return query;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder {
-    private QProfileDto profile;
-    private String query;
-    private String membership;
-
-    private Builder() {
-    }
-
-    public Builder setProfile(QProfileDto profile) {
-      this.profile = profile;
-      return this;
-    }
-
-    public Builder setMembership(@Nullable String membership) {
-      this.membership = membership;
-      return this;
-    }
-
-    public Builder setQuery(@Nullable String s) {
-      this.query = StringUtils.defaultIfBlank(s, null);
-      return this;
-    }
-
-    private void initMembership() {
-      membership = firstNonNull(membership, ANY);
-      checkArgument(AVAILABLE_MEMBERSHIPS.contains(membership),
-        "Membership is not valid (got " + membership + "). Availables values are " + AVAILABLE_MEMBERSHIPS);
-    }
-
-    public SearchUsersQuery build() {
-      requireNonNull(profile, "Quality profile cant be null.");
-      initMembership();
-      return new SearchUsersQuery(this);
-    }
-  }
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/UserMembershipDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/UserMembershipDto.java
deleted file mode 100644 (file)
index b33ecd5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.qualityprofile;
-
-public class UserMembershipDto {
-
-  private String userUuid;
-  // Set by MyBatis
-  private String uuid;
-
-  public String getUserUuid() {
-    return userUuid;
-  }
-
-  public UserMembershipDto setUserUuid(String userUuid) {
-    this.userUuid = userUuid;
-    return this;
-  }
-
-  public boolean isSelected() {
-    return uuid != null;
-  }
-
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchGroupsQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchGroupsQuery.java
deleted file mode 100644 (file)
index fd45b7b..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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 java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
-
-public abstract class SearchGroupsQuery {
-
-  public static final String ANY = "ANY";
-  public static final String IN = "IN";
-  public static final String OUT = "OUT";
-  public static final Set<String> AVAILABLE_MEMBERSHIPS = Set.of(ANY, IN, OUT);
-
-  protected String query;
-  protected String membership;
-
-  // for internal use in MyBatis
-  protected String querySqlLowercase;
-
-  public String getMembership() {
-    return membership;
-  }
-
-  @CheckForNull
-  public String getQuery() {
-    return query;
-  }
-
-  public abstract static class Builder<T extends Builder<T>> {
-    private String query;
-    private String membership;
-
-    public String getQuery(){
-      return query;
-    }
-
-    public T setQuery(@Nullable String s) {
-      this.query = StringUtils.defaultIfBlank(s, null);
-      return self();
-    }
-
-    public String getMembership(){
-      return membership;
-    }
-
-    public T setMembership(@Nullable String membership) {
-      this.membership = membership;
-      return self();
-    }
-
-    public void initMembership() {
-      membership = firstNonNull(membership, ANY);
-      checkArgument(AVAILABLE_MEMBERSHIPS.contains(membership),
-        "Membership is not valid (got " + membership + "). Availables values are " + AVAILABLE_MEMBERSHIPS);
-    }
-
-    @SuppressWarnings("unchecked")
-    final T self() {
-      return (T) this;
-    }
-  }
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchPermissionQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchPermissionQuery.java
new file mode 100644 (file)
index 0000000..5670acf
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkArgument;
+
+public abstract class SearchPermissionQuery {
+
+  public static final String ANY = "ANY";
+  public static final String IN = "IN";
+  public static final String OUT = "OUT";
+  public static final Set<String> AVAILABLE_MEMBERSHIPS = Set.of(ANY, IN, OUT);
+
+  protected String query;
+  protected String membership;
+
+  // for internal use in MyBatis
+  protected String querySql;
+  protected String querySqlLowercase;
+
+  public String getMembership() {
+    return membership;
+  }
+
+  @CheckForNull
+  public String getQuery() {
+    return query;
+  }
+
+  public abstract static class Builder<T extends Builder<T>> {
+    private String query;
+    private String membership;
+
+    public String getQuery(){
+      return query;
+    }
+
+    public T setQuery(@Nullable String s) {
+      this.query = StringUtils.defaultIfBlank(s, null);
+      return self();
+    }
+
+    public String getMembership(){
+      return membership;
+    }
+
+    public T setMembership(@Nullable String membership) {
+      this.membership = membership;
+      return self();
+    }
+
+    public void initMembership() {
+      membership = firstNonNull(membership, ANY);
+      checkArgument(AVAILABLE_MEMBERSHIPS.contains(membership),
+        "Membership is not valid (got " + membership + "). Availables values are " + AVAILABLE_MEMBERSHIPS);
+    }
+
+    @SuppressWarnings("unchecked")
+    final T self() {
+      return (T) this;
+    }
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchUserMembershipDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchUserMembershipDto.java
new file mode 100644 (file)
index 0000000..6862914
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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;
+
+public class SearchUserMembershipDto {
+
+  private String userUuid;
+  // Set by MyBatis
+  private String uuid;
+
+  public SearchUserMembershipDto() {
+    // Do nothing
+  }
+
+  public String getUserUuid() {
+    return userUuid;
+  }
+
+  public SearchUserMembershipDto setUserUuid(String userUuid) {
+    this.userUuid = userUuid;
+    return this;
+  }
+
+  public boolean isSelected() {
+    return uuid != null;
+  }
+
+}
index 411e29b1904a1cfd120937c99bf1ca2f1c4b9463..f4ce29b422c296021f0912c032d5a9513e6ca74d 100644 (file)
     )
   </insert>
 
+  <select id="countByQuery" resultType="int">
+    select count(u.uuid)
+    <include refid="sqlSelectByQuery"/>
+  </select>
+
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto">
+    SELECT u.uuid as userUuid, u.name as name, qup.uuid as uuid
+    <include refid="sqlSelectByQuery"/>
+    ORDER BY u.name ASC
+    LIMIT #{pagination.pageSize,jdbcType=INTEGER}
+    OFFSET #{pagination.offset,jdbcType=INTEGER}
+  </select>
+
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto"
+          databaseId="mssql">
+    select * from (
+    select row_number() over(order by u.name asc) as number,
+    u.uuid as userUuid, u.name as name, qup.uuid as uuid
+    <include refid="sqlSelectByQuery"/>
+    ) as query
+    where
+    query.number between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
+    order by query.name asc
+  </select>
+
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto"
+          databaseId="oracle">
+    select * from (
+    select rownum as rn, t.* from (
+    select u.uuid as userUuid, u.name as name, qup.uuid as uuid
+    <include refid="sqlSelectByQuery"/>
+    order by u.name ASC
+    ) t
+    ) t
+    where
+    t.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
+  </select>
+
+  <sql id="sqlSelectByQuery">
+    FROM users u
+    LEFT JOIN qgate_user_permissions qup ON qup.user_uuid=u.uuid AND
+    qup.quality_gate_uuid=#{query.qualityGateUuid, jdbcType=VARCHAR}
+    <where>
+      <choose>
+        <when test="query.getMembership() == 'IN'">
+          AND qup.uuid IS NOT NULL
+        </when>
+        <when test="query.getMembership() == 'OUT'">
+          AND qup.uuid IS NULL
+        </when>
+      </choose>
+      <if test="query.getQuery() != null">
+        AND (
+        lower(u.name) like #{query.querySqlLowercase} ESCAPE '/'
+        or u.login like #{query.querySql} ESCAPE '/')
+      </if>
+      AND u.active=${_true}
+    </where>
+  </sql>
 
 </mapper>
 
index 67daf154734bcd2b16eb54718f1b0b53f1b18914..74b3b51280453a6eaeff0f782202f5bd960c7ad3 100644 (file)
@@ -23,7 +23,7 @@
     <include refid="sqlSelectByQuery" />
   </select>
 
-  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.qualityprofile.UserMembershipDto">
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto">
     SELECT u.uuid as userUuid, u.name as name, qeu.uuid as uuid
     <include refid="sqlSelectByQuery"/>
     ORDER BY u.name ASC
     OFFSET #{pagination.offset,jdbcType=INTEGER}
   </select>
 
-  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.qualityprofile.UserMembershipDto" databaseId="mssql">
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto"
+          databaseId="mssql">
     select * from (
     select row_number() over(order by u.name asc) as number,
-      u.uuid as userUuid, u.name as name, qeu.uuid as uuid
-      <include refid="sqlSelectByQuery" />
+    u.uuid as userUuid, u.name as name, qeu.uuid as uuid
+    <include refid="sqlSelectByQuery"/>
     ) as query
     where
     query.number between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
     order by query.name asc
   </select>
 
-  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.qualityprofile.UserMembershipDto" databaseId="oracle">
+  <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.user.SearchUserMembershipDto"
+          databaseId="oracle">
     select * from (
-      select rownum as rn, t.* from (
-        select u.uuid as userUuid, u.name as name, qeu.uuid as uuid
-        <include refid="sqlSelectByQuery"/>
-        order by u.name ASC
-      ) t
+    select rownum as rn, t.* from (
+    select u.uuid as userUuid, u.name as name, qeu.uuid as uuid
+    <include refid="sqlSelectByQuery"/>
+    order by u.name ASC
+    ) t
     ) t
     where
     t.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
index 0acdca11c79415b9ca91159c9cc117c5cadedccc..1f8d10b9a86bd5a1a97e0f91d13c98aa2e273216 100644 (file)
@@ -35,10 +35,10 @@ import org.sonar.db.user.SearchGroupMembershipDto;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
-import static org.sonar.db.qualitygate.SearchQualityGateGroupsQuery.builder;
-import static org.sonar.db.user.SearchGroupsQuery.ANY;
-import static org.sonar.db.user.SearchGroupsQuery.IN;
-import static org.sonar.db.user.SearchGroupsQuery.OUT;
+import static org.sonar.db.qualitygate.SearchQualityGatePermissionQuery.builder;
+import static org.sonar.db.user.SearchPermissionQuery.ANY;
+import static org.sonar.db.user.SearchPermissionQuery.IN;
+import static org.sonar.db.user.SearchPermissionQuery.OUT;
 
 public class QualityGateGroupPermissionsDaoTest {
 
index 1ce212780c9cfdf7d0bff81f8fa8a878e41e2581..406db12f17bd219128785c18ce32d0fb46a8da08 100644 (file)
@@ -24,10 +24,17 @@ import org.junit.Test;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchUserMembershipDto;
 import org.sonar.db.user.UserDbTester;
 import org.sonar.db.user.UserDto;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.db.qualitygate.SearchQualityGatePermissionQuery.builder;
+import static org.sonar.db.user.SearchPermissionQuery.ANY;
+import static org.sonar.db.user.SearchPermissionQuery.IN;
+import static org.sonar.db.user.SearchPermissionQuery.OUT;
 
 public class QualityGateUserPermissionsDaoTest {
   @Rule
@@ -54,14 +61,150 @@ public class QualityGateUserPermissionsDaoTest {
 
   @Test
   public void exist() {
-    UserDto user = userDbTester.insertUser();
+    UserDto user1 = userDbTester.insertUser();
+    UserDto user2 = userDbTester.insertUser();
     QualityGateDto qualityGate = qualityGateDbTester.insertQualityGate();
-    QualityGateUserPermissionsDto qualityGateUserPermissions = new QualityGateUserPermissionsDto("uuid", user.getUuid(), qualityGate.getUuid());
+    QualityGateUserPermissionsDto qualityGateUserPermissions = new QualityGateUserPermissionsDto("uuid", user1.getUuid(), qualityGate.getUuid());
     underTest.insert(dbSession, qualityGateUserPermissions);
     dbSession.commit();
 
-    assertThat(underTest.exists(dbSession, qualityGate.getUuid(), user.getUuid())).isTrue();
-    assertThat(underTest.exists(dbSession, qualityGate, user)).isTrue();
+    assertThat(underTest.exists(dbSession, qualityGate.getUuid(), user1.getUuid())).isTrue();
+    assertThat(underTest.exists(dbSession, qualityGate, user1)).isTrue();
+    assertThat(underTest.exists(dbSession, qualityGate.getUuid(), user2.getUuid())).isFalse();
+    assertThat(underTest.exists(dbSession, qualityGate, user2)).isFalse();
+  }
+
+  @Test
+  public void exist_can_handle_null_param_and_return_false() {
+    assertThat(underTest.exists(dbSession, "uuid", null)).isFalse();
+    assertThat(underTest.exists(dbSession, null, "uuid")).isFalse();
+  }
+
+  @Test
+  public void countByQuery() {
+    QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    UserDto user3 = db.users().insertUser();
+    db.qualityGates().addUserPermission(qualityGate, user1);
+    db.qualityGates().addUserPermission(qualityGate, user2);
+
+    assertThat(underTest.countByQuery(db.getSession(), builder()
+      .setQualityGate(qualityGate)
+      .setMembership(ANY).build()))
+      .isEqualTo(3);
+
+    assertThat(underTest.countByQuery(db.getSession(), builder()
+      .setQualityGate(qualityGate)
+      .setMembership(IN).build()))
+      .isEqualTo(2);
+
+    assertThat(underTest.countByQuery(db.getSession(), builder()
+      .setQualityGate(qualityGate)
+      .setMembership(OUT).build()))
+      .isEqualTo(1);
+  }
+
+  @Test
+  public void selectByQuery() {
+    QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    UserDto user3 = db.users().insertUser();
+    db.qualityGates().addUserPermission(qualityGate, user1);
+    db.qualityGates().addUserPermission(qualityGate, user2);
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+      .setQualityGate(qualityGate)
+      .setMembership(ANY).build(), Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
+      .containsExactlyInAnyOrder(
+        tuple(user1.getUuid(), true),
+        tuple(user2.getUuid(), true),
+        tuple(user3.getUuid(), false));
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(IN).build(),
+      Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
+      .containsExactlyInAnyOrder(tuple(user1.getUuid(), true), tuple(user2.getUuid(), true));
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(OUT).build(),
+      Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
+      .containsExactlyInAnyOrder(tuple(user3.getUuid(), false));
+  }
+
+  @Test
+  public void selectByQuery_search_by_name_or_login() {
+    QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser(u -> u.setLogin("user1").setName("John Doe"));
+    UserDto user2 = db.users().insertUser(u -> u.setLogin("user2").setName("John Smith"));
+    UserDto user3 = db.users().insertUser(u -> u.setLogin("user3").setName("Jane Doe"));
+    db.qualityGates().addUserPermission(qualityGate, user1);
+    db.qualityGates().addUserPermission(qualityGate, user2);
+    db.qualityGates().addUserPermission(qualityGate, user3);
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(IN)
+        .setQuery("user2").build(),
+      Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactlyInAnyOrder(user2.getUuid());
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(IN)
+        .setQuery("joh").build(),
+      Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactlyInAnyOrder(user1.getUuid(), user2.getUuid());
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(IN)
+        .setQuery("Doe").build(),
+      Pagination.all()))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactlyInAnyOrder(user1.getUuid(), user3.getUuid());
+  }
+
+  @Test
+  public void selectByQuery_with_paging() {
+    QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser(u -> u.setName("user1"));
+    UserDto user2 = db.users().insertUser(u -> u.setName("user2"));
+    UserDto user3 = db.users().insertUser(u -> u.setName("user3"));
+    db.qualityGates().addUserPermission(qualityGate, user1);
+    db.qualityGates().addUserPermission(qualityGate, user2);
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(ANY)
+        .build(),
+      Pagination.forPage(1).andSize(1)))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactly(user1.getUuid());
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(ANY)
+        .build(),
+      Pagination.forPage(3).andSize(1)))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactly(user3.getUuid());
+
+    assertThat(underTest.selectByQuery(db.getSession(), builder()
+        .setQualityGate(qualityGate)
+        .setMembership(ANY)
+        .build(),
+      Pagination.forPage(1).andSize(10)))
+      .extracting(SearchUserMembershipDto::getUserUuid)
+      .containsExactly(user1.getUuid(), user2.getUuid(), user3.getUuid());
   }
 
 }
\ No newline at end of file
index 02ef1d812c01893b926e65ada4f8472377f15c74..b90b8e207952703e8cb26e102f6a77fc037314fa 100644 (file)
@@ -34,10 +34,10 @@ import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assertions.tuple;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.ANY;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.IN;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.OUT;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.builder;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.builder;
 
 public class QProfileEditGroupsDaoTest {
 
index 4ccf9cb87899f2b84d3ffe4e23890c8829c8ad51..c68922dd5a7e815d0bc648c3cd6d64a1fd560757 100644 (file)
@@ -27,16 +27,17 @@ import org.sonar.api.impl.utils.TestSystem2;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.Pagination;
+import org.sonar.db.user.SearchUserMembershipDto;
 import org.sonar.db.user.UserDto;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assertions.tuple;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.ANY;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.IN;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.OUT;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.builder;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.builder;
 import static org.sonar.test.ExceptionCauseMatcher.hasType;
 
 public class QProfileEditUsersDaoTest {
@@ -103,24 +104,24 @@ public class QProfileEditUsersDaoTest {
     assertThat(underTest.selectByQuery(db.getSession(), builder()
       .setProfile(profile)
       .setMembership(ANY).build(), Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid, UserMembershipDto::isSelected)
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
         .containsExactlyInAnyOrder(
           tuple(user1.getUuid(), true),
           tuple(user2.getUuid(), true),
           tuple(user3.getUuid(), false));
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(IN).build(),
+        .setProfile(profile)
+        .setMembership(IN).build(),
       Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid, UserMembershipDto::isSelected)
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
         .containsExactlyInAnyOrder(tuple(user1.getUuid(), true), tuple(user2.getUuid(), true));
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(OUT).build(),
+        .setProfile(profile)
+        .setMembership(OUT).build(),
       Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid, UserMembershipDto::isSelected)
+      .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected)
         .containsExactlyInAnyOrder(tuple(user3.getUuid(), false));
   }
 
@@ -135,27 +136,27 @@ public class QProfileEditUsersDaoTest {
     db.qualityProfiles().addUserPermission(profile, user3);
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(IN)
-      .setQuery("user2").build(),
+        .setProfile(profile)
+        .setMembership(IN)
+        .setQuery("user2").build(),
       Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactlyInAnyOrder(user2.getUuid());
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(IN)
-      .setQuery("joh").build(),
+        .setProfile(profile)
+        .setMembership(IN)
+        .setQuery("joh").build(),
       Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactlyInAnyOrder(user1.getUuid(), user2.getUuid());
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(IN)
-      .setQuery("Doe").build(),
+        .setProfile(profile)
+        .setMembership(IN)
+        .setQuery("Doe").build(),
       Pagination.all()))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactlyInAnyOrder(user1.getUuid(), user3.getUuid());
   }
 
@@ -169,27 +170,27 @@ public class QProfileEditUsersDaoTest {
     db.qualityProfiles().addUserPermission(profile, user2);
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(ANY)
-      .build(),
+        .setProfile(profile)
+        .setMembership(ANY)
+        .build(),
       Pagination.forPage(1).andSize(1)))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactly(user1.getUuid());
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(ANY)
-      .build(),
+        .setProfile(profile)
+        .setMembership(ANY)
+        .build(),
       Pagination.forPage(3).andSize(1)))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactly(user3.getUuid());
 
     assertThat(underTest.selectByQuery(db.getSession(), builder()
-      .setProfile(profile)
-      .setMembership(ANY)
-      .build(),
+        .setProfile(profile)
+        .setMembership(ANY)
+        .build(),
       Pagination.forPage(1).andSize(10)))
-        .extracting(UserMembershipDto::getUserUuid)
+      .extracting(SearchUserMembershipDto::getUserUuid)
         .containsExactly(user1.getUuid(), user2.getUuid(), user3.getUuid());
   }
 
index fcd075d46bd92e7124797921bf3809dca777e8a3..1a37844f61adbc095f0a605d2066cd5fe044bb69 100644 (file)
@@ -44,7 +44,8 @@ public class QualityGateWsModule extends Module {
       ProjectStatusAction.class,
       GetByProjectAction.class,
       AddGroupAction.class,
-      SearchGroupsAction.class
+      SearchGroupsAction.class,
+      SearchUsersAction.class
     );
   }
 }
index d93769833ae23fe5de6ddc28cd2a47ae2aa29b50..202714cd08ad2ebe32cea5f25af16c9406f83c20 100644 (file)
@@ -32,6 +32,7 @@ public class QualityGatesWsParameters {
   public static final String ACTION_ADD_GROUP = "add_group";
   public static final String ACTION_ADD_USER = "add_user";
   public static final String ACTION_SEARCH_GROUPS = "search_groups";
+  public static final String ACTION_SEARCH_USERS = "search_users";
 
   public static final String PARAM_ANALYSIS_ID = "analysisId";
   public static final String PARAM_BRANCH = "branch";
index 2cda1a5d244ec7664e114882d327ee52e8259301..fc9d4a709e0e12f1d2ace7c2c8dd926b119d24a0 100644 (file)
@@ -28,9 +28,9 @@ import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualitygate.QualityGateDto;
-import org.sonar.db.user.SearchGroupMembershipDto;
-import org.sonar.db.user.SearchGroupsQuery;
 import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.SearchGroupMembershipDto;
+import org.sonar.db.user.SearchPermissionQuery;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Qualitygates;
 
@@ -44,10 +44,10 @@ import static org.sonar.api.server.ws.WebService.SelectionMode.DESELECTED;
 import static org.sonar.api.server.ws.WebService.SelectionMode.fromParam;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.db.Pagination.forPage;
-import static org.sonar.db.qualitygate.SearchQualityGateGroupsQuery.builder;
-import static org.sonar.db.user.SearchGroupsQuery.ANY;
-import static org.sonar.db.user.SearchGroupsQuery.IN;
-import static org.sonar.db.user.SearchGroupsQuery.OUT;
+import static org.sonar.db.qualitygate.SearchQualityGatePermissionQuery.builder;
+import static org.sonar.db.user.SearchPermissionQuery.ANY;
+import static org.sonar.db.user.SearchPermissionQuery.IN;
+import static org.sonar.db.user.SearchPermissionQuery.OUT;
 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_SEARCH_GROUPS;
 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
@@ -95,7 +95,7 @@ public class SearchGroupsAction implements QualityGatesWsAction {
       QualityGateDto gate = wsSupport.getByName(dbSession, wsRequest.getQualityGate());
       wsSupport.checkCanLimitedEdit(dbSession, gate);
 
-      SearchGroupsQuery query = builder()
+      SearchPermissionQuery query = builder()
         .setQualityGate(gate)
         .setQuery(wsRequest.getQuery())
         .setMembership(MEMBERSHIP.get(fromParam(wsRequest.getSelected())))
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchUsersAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchUsersAction.java
new file mode 100644 (file)
index 0000000..e9bf6b1
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.server.qualitygate.ws;
+
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.SelectionMode;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.db.user.SearchPermissionQuery;
+import org.sonar.db.user.SearchUserMembershipDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.issue.AvatarResolver;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.Qualitygates.SearchUsersResponse;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static java.util.Optional.ofNullable;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.server.ws.WebService.Param.SELECTED;
+import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonar.api.server.ws.WebService.SelectionMode.ALL;
+import static org.sonar.api.server.ws.WebService.SelectionMode.DESELECTED;
+import static org.sonar.api.server.ws.WebService.SelectionMode.fromParam;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.db.Pagination.forPage;
+import static org.sonar.db.qualitygate.SearchQualityGatePermissionQuery.builder;
+import static org.sonar.db.user.SearchPermissionQuery.ANY;
+import static org.sonar.db.user.SearchPermissionQuery.IN;
+import static org.sonar.db.user.SearchPermissionQuery.OUT;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_SEARCH_USERS;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class SearchUsersAction implements QualityGatesWsAction {
+
+  private static final Map<SelectionMode, String> MEMBERSHIP = Map.of(SelectionMode.SELECTED, IN, DESELECTED, OUT, ALL, ANY);
+
+  private final DbClient dbClient;
+  private final QualityGatesWsSupport wsSupport;
+  private final AvatarResolver avatarResolver;
+
+  public SearchUsersAction(DbClient dbClient, QualityGatesWsSupport wsSupport, AvatarResolver avatarResolver) {
+    this.dbClient = dbClient;
+    this.wsSupport = wsSupport;
+    this.avatarResolver = avatarResolver;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    WebService.NewAction action = context
+      .createAction(ACTION_SEARCH_USERS)
+      .setDescription("List the users that are allowed to edit a Quality Gate.<br>" +
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Gates'</li>" +
+        "  <li>Edit right on the specified quality gate</li>" +
+        "</ul>")
+      .setHandler(this)
+      .setInternal(true)
+      .addSearchQuery("freddy", "names", "logins")
+      .addSelectionModeParam()
+      .addPagingParams(25)
+      .setResponseExample(getClass().getResource("search_users-example.json"))
+      .setSince("9.2");
+
+    action.createParam(PARAM_GATE_NAME)
+      .setDescription("Quality Gate name")
+      .setRequired(true)
+      .setExampleValue("Recommended quality gate");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    SearchQualityGateUsersRequest wsRequest = buildRequest(request);
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      QualityGateDto gate = wsSupport.getByName(dbSession, wsRequest.getQualityGate());
+      wsSupport.checkCanLimitedEdit(dbSession, gate);
+
+      SearchPermissionQuery query = builder()
+        .setQualityGate(gate)
+        .setQuery(wsRequest.getQuery())
+        .setMembership(MEMBERSHIP.get(fromParam(wsRequest.getSelected())))
+        .build();
+      int total = dbClient.qualityGateUserPermissionDao().countByQuery(dbSession, query);
+      List<SearchUserMembershipDto> usersMembership = dbClient.qualityGateUserPermissionDao().selectByQuery(dbSession, query,
+        forPage(wsRequest.getPage()).andSize(wsRequest.getPageSize()));
+      Map<String, UserDto> usersById = dbClient.userDao().selectByUuids(dbSession, usersMembership.stream().map(SearchUserMembershipDto::getUserUuid).collect(toList()))
+        .stream().collect(uniqueIndex(UserDto::getUuid));
+      writeProtobuf(
+        SearchUsersResponse.newBuilder()
+          .addAllUsers(usersMembership.stream()
+            .map(userMembershipDto -> toUser(usersById.get(userMembershipDto.getUserUuid()), userMembershipDto.isSelected()))
+            .collect(toList()))
+          .setPaging(buildPaging(wsRequest, total)).build(),
+        request, response);
+    }
+  }
+
+  private static SearchQualityGateUsersRequest buildRequest(Request request) {
+    return SearchQualityGateUsersRequest.builder()
+      .setQualityGate(request.mandatoryParam(PARAM_GATE_NAME))
+      .setQuery(request.param(TEXT_QUERY))
+      .setSelected(request.mandatoryParam(SELECTED))
+      .setPage(request.mandatoryParamAsInt(PAGE))
+      .setPageSize(request.mandatoryParamAsInt(PAGE_SIZE))
+      .build();
+  }
+
+  private SearchUsersResponse.User toUser(UserDto user, boolean isSelected) {
+    SearchUsersResponse.User.Builder builder = SearchUsersResponse.User.newBuilder()
+      .setLogin(user.getLogin())
+      .setSelected(isSelected);
+    ofNullable(user.getName()).ifPresent(builder::setName);
+    ofNullable(emptyToNull(user.getEmail())).ifPresent(e -> builder.setAvatar(avatarResolver.create(user)));
+    return builder
+      .build();
+  }
+
+  private static Common.Paging buildPaging(SearchQualityGateUsersRequest wsRequest, int total) {
+    return Common.Paging.newBuilder()
+      .setPageIndex(wsRequest.getPage())
+      .setPageSize(wsRequest.getPageSize())
+      .setTotal(total)
+      .build();
+  }
+
+
+}
index e71ef37e67ed4226f37b013d77c354d25f09d0e4..79649ba0af1d8d8b2aceb2a68d497fa3b35c0791 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.QProfileDto;
-import org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery;
+import org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.SearchGroupMembershipDto;
 import org.sonarqube.ws.Common;
@@ -48,10 +48,10 @@ import static org.sonar.api.server.ws.WebService.SelectionMode.fromParam;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.db.Pagination.forPage;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.ANY;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.IN;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.OUT;
-import static org.sonar.db.qualityprofile.SearchQualityProfileGroupsQuery.builder;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.builder;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_SEARCH_GROUPS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
@@ -108,7 +108,7 @@ public class SearchGroupsAction implements QProfileWsAction {
       QProfileDto profile = wsSupport.getProfile(dbSession, wsRequest.getQualityProfile(), wsRequest.getLanguage());
       wsSupport.checkCanEdit(dbSession, profile);
 
-      SearchQualityProfileGroupsQuery query = builder()
+      SearchQualityProfilePermissionQuery query = builder()
         .setProfile(profile)
         .setQuery(wsRequest.getQuery())
         .setMembership(MEMBERSHIP.get(fromParam(wsRequest.getSelected())))
index 2b0b5263eb463224972571ed25fd103ee482bae6..2eed0de3e683dae2bdd6d2f0dfe90996776f0e7b 100644 (file)
@@ -32,8 +32,8 @@ import org.sonar.api.server.ws.WebService.SelectionMode;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.QProfileDto;
-import org.sonar.db.qualityprofile.SearchUsersQuery;
-import org.sonar.db.qualityprofile.UserMembershipDto;
+import org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery;
+import org.sonar.db.user.SearchUserMembershipDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.issue.AvatarResolver;
 import org.sonarqube.ws.Common;
@@ -52,10 +52,10 @@ import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
 import static org.sonar.db.Pagination.forPage;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.ANY;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.IN;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.OUT;
-import static org.sonar.db.qualityprofile.SearchUsersQuery.builder;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT;
+import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.builder;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_SEARCH_USERS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
@@ -114,15 +114,15 @@ public class SearchUsersAction implements QProfileWsAction {
       QProfileDto profile = wsSupport.getProfile(dbSession, wsRequest.getQualityProfile(), wsRequest.getLanguage());
       wsSupport.checkCanEdit(dbSession, profile);
 
-      SearchUsersQuery query = builder()
+      SearchQualityProfilePermissionQuery query = builder()
         .setProfile(profile)
         .setQuery(wsRequest.getQuery())
         .setMembership(MEMBERSHIP.get(fromParam(wsRequest.getSelected())))
         .build();
       int total = dbClient.qProfileEditUsersDao().countByQuery(dbSession, query);
-      List<UserMembershipDto> usersMembership = dbClient.qProfileEditUsersDao().selectByQuery(dbSession, query,
+      List<SearchUserMembershipDto> usersMembership = dbClient.qProfileEditUsersDao().selectByQuery(dbSession, query,
         forPage(wsRequest.getPage()).andSize(wsRequest.getPageSize()));
-      Map<String, UserDto> usersById = dbClient.userDao().selectByUuids(dbSession, usersMembership.stream().map(UserMembershipDto::getUserUuid).collect(toList()))
+      Map<String, UserDto> usersById = dbClient.userDao().selectByUuids(dbSession, usersMembership.stream().map(SearchUserMembershipDto::getUserUuid).collect(toList()))
         .stream().collect(uniqueIndex(UserDto::getUuid));
       writeProtobuf(
         SearchUsersResponse.newBuilder()
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualitygate/ws/search_users-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualitygate/ws/search_users-example.json
new file mode 100644 (file)
index 0000000..b6f08a3
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "paging": {
+    "pageSize": 25,
+    "total": 2,
+    "pageIndex": 1
+  },
+  "users": [
+    {
+      "login": "admin",
+      "name": "Administrator",
+      "avatar": "59235f35e4763abb0b547bd093562f6e",
+      "selected": true
+    },
+    {
+      "login": "george.orwell",
+      "name": "George Orwell",
+      "avatar": "61a6fd6cba04e9394b35f349c9ca3380",
+      "selected": false
+    }
+  ]
+}
index 9c6ab4fd0d25f161e38b0be700e956c51488d1d1..ec01f5a680ba4cdf7247d1acbe6740c414b5528a 100644 (file)
@@ -30,7 +30,7 @@ public class QualityGateWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new QualityGateWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 20);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 21);
   }
 
 }
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/SearchUsersActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/SearchUsersActionTest.java
new file mode 100644 (file)
index 0000000..f1b32a2
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.server.qualitygate.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbTester;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.issue.AvatarResolver;
+import org.sonar.server.issue.AvatarResolverImpl;
+import org.sonar.server.issue.FakeAvatarResolver;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.Qualitygates.SearchUsersResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.server.ws.WebService.Param.SELECTED;
+import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_GATES;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.MediaTypes.JSON;
+
+public class SearchUsersActionTest {
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create();
+
+  private final QualityGatesWsSupport wsSupport = new QualityGatesWsSupport(db.getDbClient(), userSession, TestComponentFinder.from(db));
+  private AvatarResolver avatarResolver = new FakeAvatarResolver();
+
+  private WsActionTester ws = new WsActionTester(new SearchUsersAction(db.getDbClient(), wsSupport, avatarResolver));
+
+  @Test
+  public void test_definition() {
+    WebService.Action def = ws.getDef();
+    assertThat(def.key()).isEqualTo("search_users");
+    assertThat(def.isPost()).isFalse();
+    assertThat(def.isInternal()).isTrue();
+    assertThat(def.params()).extracting(WebService.Param::key)
+      .containsExactlyInAnyOrder("gateName", "selected", "q", "p", "ps");
+  }
+
+  @Test
+  public void test_example() {
+    avatarResolver = new AvatarResolverImpl();
+    ws = new WsActionTester(new SearchUsersAction(db.getDbClient(), wsSupport, avatarResolver));
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser(u -> u.setLogin("admin").setName("Administrator").setEmail("admin@email.com"));
+    UserDto user2 = db.users().insertUser(u -> u.setLogin("george.orwell").setName("George Orwell").setEmail("george@orwell.com"));
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    String result = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .setMediaType(JSON)
+      .execute()
+      .getInput();
+
+    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
+  }
+
+  @Test
+  public void search_all_users() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser(u -> u.setEmail("user1@email.com"));
+    UserDto user2 = db.users().insertUser(u -> u.setEmail("user2@email.com"));
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList())
+      .extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::getName, SearchUsersResponse.User::getAvatar, SearchUsersResponse.User::getSelected)
+      .containsExactlyInAnyOrder(
+        tuple(user1.getLogin(), user1.getName(), "user1@email.com_avatar", true),
+        tuple(user2.getLogin(), user2.getName(), "user2@email.com_avatar", false));
+  }
+
+  @Test
+  public void search_selected_users() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "selected")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::getName, SearchUsersResponse.User::getSelected)
+      .containsExactlyInAnyOrder(
+        tuple(user1.getLogin(), user1.getName(), true));
+  }
+
+  @Test
+  public void search_deselected_users() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "deselected")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::getName, SearchUsersResponse.User::getSelected)
+      .containsExactlyInAnyOrder(
+        tuple(user2.getLogin(), user2.getName(), false));
+  }
+
+  @Test
+  public void search_by_login() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(TEXT_QUERY, user1.getLogin())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin)
+      .containsExactlyInAnyOrder(user1.getLogin());
+  }
+
+  @Test
+  public void search_by_name() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser(u -> u.setName("John Doe"));
+    UserDto user2 = db.users().insertUser(u -> u.setName("Jane Doe"));
+    UserDto user3 = db.users().insertUser(u -> u.setName("John Smith"));
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(TEXT_QUERY, "ohn")
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin)
+      .containsExactlyInAnyOrder(user1.getLogin(), user3.getLogin());
+  }
+
+  @Test
+  public void user_without_email() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser(u -> u.setEmail(null));
+    db.qualityGates().addUserPermission(gate, user);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::hasAvatar)
+      .containsExactlyInAnyOrder(tuple(user.getLogin(), false));
+  }
+
+  @Test
+  public void paging_search() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user2 = db.users().insertUser(u -> u.setName("user2"));
+    UserDto user3 = db.users().insertUser(u -> u.setName("user3"));
+    UserDto user1 = db.users().insertUser(u -> u.setName("user1"));
+    db.qualityGates().addUserPermission(gate, user1);
+    db.qualityGates().addUserPermission(gate, user2);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    assertThat(ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .setParam(PAGE, "1")
+      .setParam(PAGE_SIZE, "1")
+      .executeProtobuf(SearchUsersResponse.class).getUsersList())
+      .extracting(SearchUsersResponse.User::getLogin)
+      .containsExactly(user1.getLogin());
+
+    assertThat(ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .setParam(PAGE, "3")
+      .setParam(PAGE_SIZE, "1")
+      .executeProtobuf(SearchUsersResponse.class).getUsersList())
+      .extracting(SearchUsersResponse.User::getLogin)
+      .containsExactly(user3.getLogin());
+
+    assertThat(ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .setParam(PAGE, "1")
+      .setParam(PAGE_SIZE, "10")
+      .executeProtobuf(SearchUsersResponse.class).getUsersList())
+      .extracting(SearchUsersResponse.User::getLogin)
+      .containsExactly(user1.getLogin(), user2.getLogin(), user3.getLogin());
+  }
+
+  @Test
+  public void uses_global_permission() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user1 = db.users().insertUser();
+    db.qualityGates().addUserPermission(gate, user1);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin).containsExactlyInAnyOrder(user1.getLogin());
+  }
+
+  @Test
+  public void qp_administers_can_search_users() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin).containsExactlyInAnyOrder(user.getLogin());
+  }
+
+  @Test
+  public void qp_editors_can_search_users() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    UserDto userAllowedToEditProfile = db.users().insertUser();
+    db.qualityGates().addUserPermission(gate, userAllowedToEditProfile);
+    userSession.logIn(userAllowedToEditProfile);
+
+    SearchUsersResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, gate.getName())
+      .setParam(SELECTED, "all")
+      .executeProtobuf(SearchUsersResponse.class);
+
+    assertThat(response.getUsersList()).extracting(SearchUsersResponse.User::getLogin).containsExactlyInAnyOrder(user.getLogin(), userAllowedToEditProfile.getLogin());
+  }
+
+  @Test
+  public void fail_when_quality_gate_does_not_exist() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES);
+
+    TestRequest request = ws.newRequest().setParam(PARAM_GATE_NAME, "unknown");
+
+    assertThatThrownBy(() -> request.execute())
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage("No quality gate has been found for name unknown");
+  }
+
+
+  @Test
+  public void fail_when_not_enough_permission() {
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    userSession.logIn(db.users().insertUser());
+
+    TestRequest request = ws.newRequest().setParam(PARAM_GATE_NAME, gate.getName());
+
+    assertThatThrownBy(() -> request.execute())
+      .isInstanceOf(ForbiddenException.class);
+  }
+}
index 5538d4b5922f2e37fac01c49730b55dc76499280..e9aa2946124292d6c18f898af4920fccbd0edbf2 100644 (file)
@@ -182,6 +182,19 @@ message Actions {
   optional bool manageConditions = 6;
 }
 
+// WS api/qualitygates/search_users
+message SearchUsersResponse {
+  optional sonarqube.ws.commons.Paging paging = 1;
+  repeated User users = 2;
+
+  message User {
+    optional string login = 1;
+    optional string name = 2;
+    optional string avatar = 3;
+    optional bool selected = 4;
+  }
+}
+
 // WS api/qualitygates/search_groups
 message SearchGroupsResponse {
   optional sonarqube.ws.commons.Paging paging = 1;