From: Zipeng WU Date: Wed, 13 Oct 2021 14:35:52 +0000 (+0200) Subject: SONAR-15514 Create api/qualitygates/search_users service X-Git-Tag: 9.2.0.49834~123 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=029e98315813bf42740329c63f6b54c72044fe92;p=sonarqube.git SONAR-15514 Create api/qualitygates/search_users service --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java index 76b901601a7..a95d8f5235f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java @@ -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 selectByQuery(DbSession dbSession, SearchGroupsQuery query, Pagination pagination) { + public List 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); } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java index b7c23e1fa99..2d0800155cf 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java @@ -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 selectByQuery(@Param("query") SearchGroupsQuery query, @Param("pagination") Pagination pagination); + List selectByQuery(@Param("query") SearchPermissionQuery query, @Param("pagination") Pagination pagination); - int countByQuery(@Param("query") SearchGroupsQuery query); + int countByQuery(@Param("query") SearchPermissionQuery query); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java index e79dc9ecfde..6bc72474a47 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java @@ -19,10 +19,14 @@ */ 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 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); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java index b67289f69dc..3f28de143eb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java @@ -19,7 +19,11 @@ */ 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 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 index 7bfeef9bc2a..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGateGroupsQuery.java +++ /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 { - 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 index 00000000000..c0a556606dc --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/SearchQualityGatePermissionQuery.java @@ -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 { + 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); + } + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java index 8b324e9c898..484a406dd0d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java @@ -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 selectByQuery(DbSession dbSession, SearchQualityProfileGroupsQuery query, Pagination pagination) { + public List selectByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query, Pagination pagination) { return mapper(dbSession).selectByQuery(query, pagination); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java index d8cb1e723e6..e849f4c984b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java @@ -29,9 +29,9 @@ public interface QProfileEditGroupsMapper { List selectByQProfileAndGroups(@Param("qProfileUuid") String qProfileUuid, @Param("groupUuids") List groupUuids); - int countByQuery(@Param("query") SearchQualityProfileGroupsQuery query); + int countByQuery(@Param("query") SearchQualityProfilePermissionQuery query); - List selectByQuery(@Param("query") SearchQualityProfileGroupsQuery query, @Param("pagination") Pagination pagination); + List selectByQuery(@Param("query") SearchQualityProfilePermissionQuery query, @Param("pagination") Pagination pagination); List selectQProfileUuidsByGroups(@Param("groupUuids") List groupUuids); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java index 3b66fbeb452..911867520a1 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java @@ -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 selectByQuery(DbSession dbSession, SearchUsersQuery query, Pagination pagination) { + public List selectByQuery(DbSession dbSession, SearchQualityProfilePermissionQuery query, Pagination pagination) { return mapper(dbSession).selectByQuery(query, pagination); } - public List selectQProfileUuidsByUser(DbSession dbSession,UserDto userDto) { + public List selectQProfileUuidsByUser(DbSession dbSession, UserDto userDto) { return mapper(dbSession).selectQProfileUuidsByUser(userDto.getUuid()); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java index e3b24df061b..fdd09a5cc8b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java @@ -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 selectByQuery(@Param("query") SearchUsersQuery query, @Param("pagination") Pagination pagination); + List selectByQuery(@Param("query") SearchQualityProfilePermissionQuery query, @Param("pagination") Pagination pagination); List 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 index cdbe85eae89..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfileGroupsQuery.java +++ /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 { - 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 index 00000000000..49e7a0e9d7c --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchQualityProfilePermissionQuery.java @@ -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 { + 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 index 7a60fc34e93..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/SearchUsersQuery.java +++ /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 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 index b33ecd5e9f6..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/UserMembershipDto.java +++ /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 index fd45b7beacd..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchGroupsQuery.java +++ /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 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> { - 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 index 00000000000..5670acf270c --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchPermissionQuery.java @@ -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 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> { + 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 index 00000000000..68629141cc2 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/SearchUserMembershipDto.java @@ -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; + } + +} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml index 411e29b1904..f4ce29b422c 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml @@ -32,6 +32,65 @@ ) + + + + + + + + + + FROM users u + LEFT JOIN qgate_user_permissions qup ON qup.user_uuid=u.uuid AND + qup.quality_gate_uuid=#{query.qualityGateUuid, jdbcType=VARCHAR} + + + + AND qup.uuid IS NOT NULL + + + AND qup.uuid IS NULL + + + + AND ( + lower(u.name) like #{query.querySqlLowercase} ESCAPE '/' + or u.login like #{query.querySql} ESCAPE '/') + + AND u.active=${_true} + + diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml index 67daf154734..74b3b512804 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml @@ -23,7 +23,7 @@ - SELECT u.uuid as userUuid, u.name as name, qeu.uuid as uuid ORDER BY u.name ASC @@ -31,24 +31,26 @@ OFFSET #{pagination.offset,jdbcType=INTEGER} - 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 - + u.uuid as userUuid, u.name as name, qeu.uuid as uuid + ) as query where query.number between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER} order by query.name asc - select * from ( - select rownum as rn, t.* from ( - select u.uuid as userUuid, u.name as name, qeu.uuid as uuid - - 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 + + order by u.name ASC + ) t ) t where t.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java index 0acdca11c79..1f8d10b9a86 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java @@ -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 { diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java index 1ce212780c9..406db12f17b 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java @@ -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 diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java index 02ef1d812c0..b90b8e20795 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java @@ -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 { diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java index 4ccf9cb8789..c68922dd5a7 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java @@ -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()); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java index fcd075d46bd..1a37844f61a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java @@ -44,7 +44,8 @@ public class QualityGateWsModule extends Module { ProjectStatusAction.class, GetByProjectAction.class, AddGroupAction.class, - SearchGroupsAction.class + SearchGroupsAction.class, + SearchUsersAction.class ); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java index d93769833ae..202714cd08a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java @@ -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"; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchGroupsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchGroupsAction.java index 2cda1a5d244..fc9d4a709e0 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchGroupsAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchGroupsAction.java @@ -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 index 00000000000..e9bf6b1e4f8 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/SearchUsersAction.java @@ -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 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.
" + + "Requires one of the following permissions:" + + "
    " + + "
  • 'Administer Quality Gates'
  • " + + "
  • Edit right on the specified quality gate
  • " + + "
") + .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 usersMembership = dbClient.qualityGateUserPermissionDao().selectByQuery(dbSession, query, + forPage(wsRequest.getPage()).andSize(wsRequest.getPageSize())); + Map 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(); + } + + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java index e71ef37e67e..79649ba0af1 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java @@ -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()))) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java index 2b0b5263eb4..2eed0de3e68 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java @@ -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 usersMembership = dbClient.qProfileEditUsersDao().selectByQuery(dbSession, query, + List usersMembership = dbClient.qProfileEditUsersDao().selectByQuery(dbSession, query, forPage(wsRequest.getPage()).andSize(wsRequest.getPageSize())); - Map usersById = dbClient.userDao().selectByUuids(dbSession, usersMembership.stream().map(UserMembershipDto::getUserUuid).collect(toList())) + Map 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 index 00000000000..b6f08a35742 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualitygate/ws/search_users-example.json @@ -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 + } + ] +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java index 9c6ab4fd0d2..ec01f5a680b 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java @@ -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 index 00000000000..f1b32a2e6d6 --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/SearchUsersActionTest.java @@ -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); + } +} diff --git a/sonar-ws/src/main/protobuf/ws-qualitygates.proto b/sonar-ws/src/main/protobuf/ws-qualitygates.proto index 5538d4b5922..e9aa2946124 100644 --- a/sonar-ws/src/main/protobuf/ws-qualitygates.proto +++ b/sonar-ws/src/main/protobuf/ws-qualitygates.proto @@ -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;