You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SearchUsersAction.java 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.qualityprofile.ws;
  21. import com.google.common.collect.ImmutableMap;
  22. import java.util.Arrays;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.function.Function;
  26. import java.util.stream.Collectors;
  27. import org.sonar.api.resources.Language;
  28. import org.sonar.api.resources.Languages;
  29. import org.sonar.api.server.ws.Request;
  30. import org.sonar.api.server.ws.Response;
  31. import org.sonar.api.server.ws.WebService;
  32. import org.sonar.api.server.ws.WebService.SelectionMode;
  33. import org.sonar.db.DbClient;
  34. import org.sonar.db.DbSession;
  35. import org.sonar.db.qualityprofile.QProfileDto;
  36. import org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery;
  37. import org.sonar.db.user.SearchUserMembershipDto;
  38. import org.sonar.db.user.UserDto;
  39. import org.sonar.server.common.avatar.AvatarResolver;
  40. import org.sonarqube.ws.Common;
  41. import org.sonarqube.ws.Qualityprofiles.SearchUsersResponse;
  42. import static com.google.common.base.Strings.emptyToNull;
  43. import static java.util.Optional.ofNullable;
  44. import static org.sonar.api.server.ws.WebService.Param.PAGE;
  45. import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
  46. import static org.sonar.api.server.ws.WebService.Param.SELECTED;
  47. import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
  48. import static org.sonar.api.server.ws.WebService.SelectionMode.ALL;
  49. import static org.sonar.api.server.ws.WebService.SelectionMode.DESELECTED;
  50. import static org.sonar.api.server.ws.WebService.SelectionMode.fromParam;
  51. import static org.sonar.db.Pagination.forPage;
  52. import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY;
  53. import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN;
  54. import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT;
  55. import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.builder;
  56. import static org.sonar.server.ws.WsUtils.writeProtobuf;
  57. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_SEARCH_USERS;
  58. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
  59. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
  60. public class SearchUsersAction implements QProfileWsAction {
  61. private static final Map<SelectionMode, String> MEMBERSHIP = ImmutableMap.of(SelectionMode.SELECTED, IN, DESELECTED, OUT, ALL, ANY);
  62. private final DbClient dbClient;
  63. private final QProfileWsSupport wsSupport;
  64. private final Languages languages;
  65. private final AvatarResolver avatarResolver;
  66. public SearchUsersAction(DbClient dbClient, QProfileWsSupport wsSupport, Languages languages, AvatarResolver avatarResolver) {
  67. this.dbClient = dbClient;
  68. this.wsSupport = wsSupport;
  69. this.languages = languages;
  70. this.avatarResolver = avatarResolver;
  71. }
  72. @Override
  73. public void define(WebService.NewController context) {
  74. WebService.NewAction action = context
  75. .createAction(ACTION_SEARCH_USERS)
  76. .setDescription("List the users that are allowed to edit a Quality Profile.<br>" +
  77. "Requires one of the following permissions:" +
  78. "<ul>" +
  79. " <li>'Administer Quality Profiles'</li>" +
  80. " <li>Edit right on the specified quality profile</li>" +
  81. "</ul>")
  82. .setHandler(this)
  83. .setInternal(true)
  84. .addSearchQuery("freddy", "names", "logins")
  85. .addSelectionModeParam()
  86. .addPagingParams(25)
  87. .setResponseExample(getClass().getResource("search_users-example.json"))
  88. .setSince("6.6");
  89. action.createParam(PARAM_QUALITY_PROFILE)
  90. .setDescription("Quality Profile name")
  91. .setRequired(true)
  92. .setExampleValue("Recommended quality profile");
  93. action
  94. .createParam(PARAM_LANGUAGE)
  95. .setDescription("Quality profile language")
  96. .setRequired(true)
  97. .setPossibleValues(Arrays.stream(languages.all()).map(Language::getKey).collect(Collectors.toSet()));
  98. }
  99. @Override
  100. public void handle(Request request, Response response) throws Exception {
  101. SearchQualityProfileUsersRequest wsRequest = buildRequest(request);
  102. try (DbSession dbSession = dbClient.openSession(false)) {
  103. QProfileDto profile = wsSupport.getProfile(dbSession, wsRequest.getQualityProfile(), wsRequest.getLanguage());
  104. wsSupport.checkCanEdit(dbSession, profile);
  105. SearchQualityProfilePermissionQuery query = builder()
  106. .setProfile(profile)
  107. .setQuery(wsRequest.getQuery())
  108. .setMembership(MEMBERSHIP.get(fromParam(wsRequest.getSelected())))
  109. .build();
  110. int total = dbClient.qProfileEditUsersDao().countByQuery(dbSession, query);
  111. List<SearchUserMembershipDto> usersMembership = dbClient.qProfileEditUsersDao().selectByQuery(dbSession, query,
  112. forPage(wsRequest.getPage()).andSize(wsRequest.getPageSize()));
  113. Map<String, UserDto> usersById = dbClient.userDao().selectByUuids(dbSession, usersMembership.stream().map(SearchUserMembershipDto::getUserUuid).toList())
  114. .stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity()));
  115. writeProtobuf(
  116. SearchUsersResponse.newBuilder()
  117. .addAllUsers(usersMembership.stream()
  118. .map(userMembershipDto -> toUser(usersById.get(userMembershipDto.getUserUuid()), userMembershipDto.isSelected()))
  119. .toList())
  120. .setPaging(buildPaging(wsRequest, total)).build(),
  121. request, response);
  122. }
  123. }
  124. private static SearchQualityProfileUsersRequest buildRequest(Request request) {
  125. return SearchQualityProfileUsersRequest.builder()
  126. .setQualityProfile(request.mandatoryParam(PARAM_QUALITY_PROFILE))
  127. .setLanguage(request.mandatoryParam(PARAM_LANGUAGE))
  128. .setQuery(request.param(TEXT_QUERY))
  129. .setSelected(request.mandatoryParam(SELECTED))
  130. .setPage(request.mandatoryParamAsInt(PAGE))
  131. .setPageSize(request.mandatoryParamAsInt(PAGE_SIZE))
  132. .build();
  133. }
  134. private SearchUsersResponse.User toUser(UserDto user, boolean isSelected) {
  135. SearchUsersResponse.User.Builder builder = SearchUsersResponse.User.newBuilder()
  136. .setLogin(user.getLogin())
  137. .setSelected(isSelected);
  138. ofNullable(user.getName()).ifPresent(builder::setName);
  139. ofNullable(emptyToNull(user.getEmail())).ifPresent(e -> builder.setAvatar(avatarResolver.create(user)));
  140. return builder
  141. .build();
  142. }
  143. private static Common.Paging buildPaging(SearchQualityProfileUsersRequest wsRequest, int total) {
  144. return Common.Paging.newBuilder()
  145. .setPageIndex(wsRequest.getPage())
  146. .setPageSize(wsRequest.getPageSize())
  147. .setTotal(total)
  148. .build();
  149. }
  150. }