package org.sonar.db.organization; | package org.sonar.db.organization; | ||||
import java.util.List; | |||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.Set; | import java.util.Set; | ||||
import org.sonar.db.Dao; | import org.sonar.db.Dao; | ||||
return Optional.ofNullable(mapper(dbSession).select(organizationUuid, userId)); | return Optional.ofNullable(mapper(dbSession).select(organizationUuid, userId)); | ||||
} | } | ||||
public List<String> selectLoginsByOrganizationUuid(DbSession dbSession, String organizationUuid) { | |||||
return mapper(dbSession).selectLogins(organizationUuid); | |||||
} | |||||
public void insert(DbSession dbSession, OrganizationMemberDto organizationMemberDto) { | public void insert(DbSession dbSession, OrganizationMemberDto organizationMemberDto) { | ||||
mapper(dbSession).insert(organizationMemberDto); | mapper(dbSession).insert(organizationMemberDto); | ||||
} | } |
package org.sonar.db.organization; | package org.sonar.db.organization; | ||||
import java.util.List; | |||||
import java.util.Set; | import java.util.Set; | ||||
import org.apache.ibatis.annotations.Param; | import org.apache.ibatis.annotations.Param; | ||||
Set<String> selectOrganizationUuidsByUser(@Param("userId") int userId); | Set<String> selectOrganizationUuidsByUser(@Param("userId") int userId); | ||||
List<String> selectLogins(String organizationUuid); | |||||
void insert(OrganizationMemberDto organizationMember); | void insert(OrganizationMemberDto organizationMember); | ||||
void delete(@Param("organizationUuid") String organizationUuid, @Param("userId") Integer userId); | void delete(@Param("organizationUuid") String organizationUuid, @Param("userId") Integer userId); |
import com.google.common.collect.ImmutableMap; | import com.google.common.collect.ImmutableMap; | ||||
import com.google.common.collect.Maps; | import com.google.common.collect.Maps; | ||||
import com.google.common.collect.Multimap; | import com.google.common.collect.Multimap; | ||||
import com.google.common.collect.Multiset; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
return mapper(dbSession).selectGroupIdsByUserId(userId); | return mapper(dbSession).selectGroupIdsByUserId(userId); | ||||
} | } | ||||
public Multiset<String> countGroupByLoginsAndOrganization(DbSession dbSession, Collection<String> logins, String organizationUuid) { | |||||
Multimap<String, String> result = ArrayListMultimap.create(); | |||||
executeLargeInputs( | |||||
logins, | |||||
input -> { | |||||
List<LoginGroup> groupMemberships = mapper(dbSession).selectGroupsByLoginsAndOrganization(input, organizationUuid); | |||||
for (LoginGroup membership : groupMemberships) { | |||||
result.put(membership.login(), membership.groupName()); | |||||
} | |||||
return groupMemberships; | |||||
}); | |||||
return result.keys(); | |||||
} | |||||
public Multimap<String, String> selectGroupsByLogins(DbSession session, Collection<String> logins) { | public Multimap<String, String> selectGroupsByLogins(DbSession session, Collection<String> logins) { | ||||
Multimap<String, String> result = ArrayListMultimap.create(); | Multimap<String, String> result = ArrayListMultimap.create(); | ||||
executeLargeInputs( | executeLargeInputs( |
List<LoginGroup> selectGroupsByLogins(@Param("logins") List<String> logins); | List<LoginGroup> selectGroupsByLogins(@Param("logins") List<String> logins); | ||||
List<LoginGroup> selectGroupsByLoginsAndOrganization(@Param("logins") List<String> logins, @Param("organizationUuid") String organizationUuid); | |||||
List<Integer> selectGroupIdsByUserId(@Param("userId") int userId); | List<Integer> selectGroupIdsByUserId(@Param("userId") int userId); | ||||
} | } |
and om.user_id = #{userId, jdbcType=INTEGER} | and om.user_id = #{userId, jdbcType=INTEGER} | ||||
</select> | </select> | ||||
<select id="selectLogins" resultType="string"> | |||||
select u.login | |||||
from organization_members om | |||||
inner join users u on om.user_id = u.id | |||||
where om.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} | |||||
</select> | |||||
<select id="selectOrganizationUuidsByUser" resultType="String"> | <select id="selectOrganizationUuidsByUser" resultType="String"> | ||||
select om.organization_uuid as "organizationUuid" | select om.organization_uuid as "organizationUuid" | ||||
from organization_members om | from organization_members om | ||||
) | ) | ||||
</insert> | </insert> | ||||
<delete id="delete" parameterType="map"> | |||||
<delete id="delete"> | |||||
delete from organization_members | delete from organization_members | ||||
where | where | ||||
organization_uuid = #{organizationUuid, jdbcType=VARCHAR} | organization_uuid = #{organizationUuid, jdbcType=VARCHAR} |
ORDER BY u.login, g.name, g.id | ORDER BY u.login, g.name, g.id | ||||
</select> | </select> | ||||
<select id="selectGroupsByLoginsAndOrganization" parameterType="map" resultType="org.sonar.db.user.LoginGroup"> | |||||
SELECT u.login as login, g.name as groupName | |||||
FROM users u | |||||
LEFT JOIN groups_users gu ON gu.user_id=u.id | |||||
INNER JOIN groups g ON gu.group_id=g.id | |||||
<where> | |||||
u.login in | |||||
<foreach collection="logins" open="(" close=")" item="login" separator=","> | |||||
#{login} | |||||
</foreach> | |||||
and g.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} | |||||
</where> | |||||
ORDER BY u.login, g.name, g.id | |||||
</select> | |||||
<sql id="userCommonClauses"> | <sql id="userCommonClauses"> | ||||
FROM users u | FROM users u | ||||
LEFT JOIN groups_users gu ON gu.user_id=u.id AND gu.group_id=#{groupId} | LEFT JOIN groups_users gu ON gu.user_id=u.id AND gu.group_id=#{groupId} |
package org.sonar.db.organization; | package org.sonar.db.organization; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import org.apache.ibatis.exceptions.PersistenceException; | import org.apache.ibatis.exceptions.PersistenceException; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.user.UserDto; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.assertj.core.api.Assertions.entry; | import static org.assertj.core.api.Assertions.entry; | ||||
assertThat(underTest.select(dbSession, "O2", 512)).isNotPresent(); | assertThat(underTest.select(dbSession, "O2", 512)).isNotPresent(); | ||||
} | } | ||||
@Test | |||||
public void select_logins() { | |||||
OrganizationDto organization = db.organizations().insert(); | |||||
OrganizationDto anotherOrganization = db.organizations().insert(); | |||||
UserDto user = db.users().insertUser(); | |||||
UserDto anotherUser = db.users().insertUser(); | |||||
UserDto userInAnotherOrganization = db.users().insertUser(); | |||||
db.organizations().addMember(organization, user); | |||||
db.organizations().addMember(organization, anotherUser); | |||||
db.organizations().addMember(anotherOrganization, userInAnotherOrganization); | |||||
List<String> result = underTest.selectLoginsByOrganizationUuid(dbSession, organization.getUuid()); | |||||
assertThat(result).containsOnly(user.getLogin(), anotherUser.getLogin()); | |||||
} | |||||
@Test | @Test | ||||
public void select_organization_uuids_by_user_id() { | public void select_organization_uuids_by_user_id() { | ||||
OrganizationDto organizationDto1 = db.organizations().insert(); | OrganizationDto organizationDto1 = db.organizations().insert(); |
} | } | ||||
@Test | @Test | ||||
public void count_groups_by_login() { | |||||
public void count_groups_by_logins() { | |||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | dbTester.prepareDbUnit(getClass(), "shared.xml"); | ||||
assertThat(underTest.selectGroupsByLogins(dbTester.getSession(), Arrays.<String>asList()).keys()).isEmpty(); | assertThat(underTest.selectGroupsByLogins(dbTester.getSession(), Arrays.<String>asList()).keys()).isEmpty(); |
package org.sonar.server.organization.ws; | package org.sonar.server.organization.ws; | ||||
import com.google.common.collect.Multiset; | |||||
import com.google.common.collect.Ordering; | |||||
import com.google.common.hash.Hashing; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.List; | |||||
import java.util.Locale; | |||||
import java.util.Optional; | |||||
import javax.annotation.Nullable; | |||||
import org.sonar.api.server.ws.Request; | import org.sonar.api.server.ws.Request; | ||||
import org.sonar.api.server.ws.Response; | import org.sonar.api.server.ws.Response; | ||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.api.server.ws.WebService.Param; | |||||
import org.sonar.api.server.ws.WebService.SelectionMode; | |||||
import org.sonar.core.util.stream.Collectors; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.organization.OrganizationDto; | |||||
import org.sonar.db.permission.OrganizationPermission; | |||||
import org.sonar.db.user.UserDto; | |||||
import org.sonar.server.es.SearchOptions; | |||||
import org.sonar.server.es.SearchResult; | |||||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||||
import org.sonar.server.user.UserSession; | |||||
import org.sonar.server.user.index.UserDoc; | |||||
import org.sonar.server.user.index.UserIndex; | |||||
import org.sonar.server.user.index.UserQuery; | |||||
import org.sonarqube.ws.Common; | |||||
import org.sonarqube.ws.Organizations.SearchMembersWsResponse; | |||||
import org.sonarqube.ws.Organizations.User; | |||||
import static com.google.common.base.Preconditions.checkArgument; | |||||
import static org.sonar.core.util.Protobuf.setNullable; | |||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | ||||
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; | |||||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||||
public class SearchMembersAction implements OrganizationsWsAction { | public class SearchMembersAction implements OrganizationsWsAction { | ||||
private final DbClient dbClient; | |||||
private final UserIndex userIndex; | |||||
private final DefaultOrganizationProvider organizationProvider; | |||||
private final UserSession userSession; | |||||
public SearchMembersAction(DbClient dbClient, UserIndex userIndex, DefaultOrganizationProvider organizationProvider, UserSession userSession) { | |||||
this.dbClient = dbClient; | |||||
this.userIndex = userIndex; | |||||
this.organizationProvider = organizationProvider; | |||||
this.userSession = userSession; | |||||
} | |||||
@Override | @Override | ||||
public void define(WebService.NewController context) { | public void define(WebService.NewController context) { | ||||
WebService.NewAction action = context.createAction("search_members") | WebService.NewAction action = context.createAction("search_members") | ||||
.setDescription("Search members of an organization") | |||||
.setResponseExample(getClass().getResource("search_members-example.json")) | .setResponseExample(getClass().getResource("search_members-example.json")) | ||||
.setSince("6.4") | .setSince("6.4") | ||||
.setInternal(true) | .setInternal(true) | ||||
.setHandler(this); | .setHandler(this); | ||||
action.addSelectionModeParam(); | |||||
action.addSearchQuery("freddy", "names", "logins"); | |||||
action.addSearchQuery("orwe", "names", "logins"); | |||||
action.addPagingParams(50, MAX_LIMIT); | action.addPagingParams(50, MAX_LIMIT); | ||||
action.createParam(Param.SELECTED) | |||||
.setDescription("Depending on the value, show only selected items (selected=selected) or deselected items (selected=deselected).") | |||||
.setInternal(true) | |||||
.setDefaultValue(SelectionMode.SELECTED.value()) | |||||
.setPossibleValues(SelectionMode.SELECTED.value(), SelectionMode.DESELECTED.value()); | |||||
action.createParam("organization") | action.createParam("organization") | ||||
.setDescription("Organization key") | .setDescription("Organization key") | ||||
.setInternal(true) | .setInternal(true) | ||||
@Override | @Override | ||||
public void handle(Request request, Response response) throws Exception { | public void handle(Request request, Response response) throws Exception { | ||||
// TODO | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
OrganizationDto organization = getOrganization(dbSession, request.param("organization")); | |||||
List<String> memberLogins = dbClient.organizationMemberDao().selectLoginsByOrganizationUuid(dbSession, organization.getUuid()); | |||||
UserQuery.Builder userQuery = buildUserQuery(request, memberLogins); | |||||
SearchOptions searchOptions = buildSearchOptions(request); | |||||
SearchResult<UserDoc> searchResults = userIndex.search(userQuery.build(), searchOptions); | |||||
List<String> orderedLogins = searchResults.getDocs().stream().map(UserDoc::login).collect(Collectors.toList()); | |||||
List<UserDto> users = dbClient.userDao().selectByLogins(dbSession, orderedLogins).stream() | |||||
.sorted(Ordering.explicit(orderedLogins).onResultOf(UserDto::getLogin)) | |||||
.collect(Collectors.toList()); | |||||
Multiset<String> groupCountByLogin = null; | |||||
if (userSession.hasPermission(OrganizationPermission.ADMINISTER, organization)) { | |||||
groupCountByLogin = dbClient.groupMembershipDao().countGroupByLoginsAndOrganization(dbSession, orderedLogins, organization.getUuid()); | |||||
} | |||||
Common.Paging wsPaging = buildWsPaging(request, searchResults); | |||||
SearchMembersWsResponse wsResponse = buildResponse(users, wsPaging, groupCountByLogin); | |||||
writeProtobuf(wsResponse, request, response); | |||||
} | |||||
} | |||||
private static SearchMembersWsResponse buildResponse(List<UserDto> users, Common.Paging wsPaging, @Nullable Multiset<String> groupCountByLogin) { | |||||
SearchMembersWsResponse.Builder response = SearchMembersWsResponse.newBuilder(); | |||||
User.Builder wsUser = User.newBuilder(); | |||||
users.stream() | |||||
.map(userDto -> { | |||||
String login = userDto.getLogin(); | |||||
wsUser | |||||
.clear() | |||||
.setLogin(login) | |||||
.setName(userDto.getName()); | |||||
setNullable(userDto.getEmail(), text -> wsUser.setAvatar(hash(text))); | |||||
setNullable(groupCountByLogin, count -> wsUser.setGroupCount(groupCountByLogin.count(login))); | |||||
return wsUser; | |||||
}) | |||||
.forEach(response::addUsers); | |||||
response.setPaging(wsPaging); | |||||
return response.build(); | |||||
} | |||||
private static UserQuery.Builder buildUserQuery(Request request, List<String> memberLogins) { | |||||
UserQuery.Builder userQuery = UserQuery.builder(); | |||||
String textQuery = request.param(Param.TEXT_QUERY); | |||||
checkArgument(textQuery == null || textQuery.length() >= 2, "Query length must be greater than or equal to 2"); | |||||
userQuery.setTextQuery(textQuery); | |||||
SelectionMode selectionMode = SelectionMode.fromParam(request.mandatoryParam(Param.SELECTED)); | |||||
if (SelectionMode.DESELECTED.equals(selectionMode)) { | |||||
userQuery.setExcludedLogins(memberLogins); | |||||
} else { | |||||
userQuery.setLogins(memberLogins); | |||||
} | |||||
return userQuery; | |||||
} | |||||
private static SearchOptions buildSearchOptions(Request request) { | |||||
int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); | |||||
checkArgument(pageSize <= SearchOptions.MAX_LIMIT, "Page size must lower than or equal to %s", SearchOptions.MAX_LIMIT); | |||||
return new SearchOptions().setPage(request.mandatoryParamAsInt(Param.PAGE), pageSize); | |||||
} | |||||
private static String hash(String text) { | |||||
return Hashing.md5().hashString(text.toLowerCase(Locale.ENGLISH), StandardCharsets.UTF_8).toString(); | |||||
} | |||||
private static Common.Paging buildWsPaging(Request request, SearchResult<UserDoc> searchResults) { | |||||
return Common.Paging.newBuilder() | |||||
.setPageIndex(request.mandatoryParamAsInt(Param.PAGE)) | |||||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||||
.setTotal((int) searchResults.getTotal()) | |||||
.build(); | |||||
} | |||||
private OrganizationDto getOrganization(DbSession dbSession, @Nullable String organizationParam) { | |||||
String organizationKey = Optional.ofNullable(organizationParam) | |||||
.orElseGet(organizationProvider.get()::getKey); | |||||
return checkFoundWithOptional( | |||||
dbClient.organizationDao().selectByKey(dbSession, organizationKey), | |||||
"No organization with key '%s'", organizationKey); | |||||
} | } | ||||
} | } |
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Optional; | |||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | |||||
import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||
import org.elasticsearch.action.get.GetRequestBuilder; | import org.elasticsearch.action.get.GetRequestBuilder; | ||||
import org.elasticsearch.action.get.GetResponse; | import org.elasticsearch.action.get.GetResponse; | ||||
return EsUtils.scroll(esClient, response.getScrollId(), DOC_CONVERTER); | return EsUtils.scroll(esClient, response.getScrollId(), DOC_CONVERTER); | ||||
} | } | ||||
public SearchResult<UserDoc> search(@Nullable String searchText, SearchOptions options) { | |||||
public SearchResult<UserDoc> search(UserQuery userQuery, SearchOptions options) { | |||||
SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.INDEX_TYPE_USER) | SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.INDEX_TYPE_USER) | ||||
.setSize(options.getLimit()) | .setSize(options.getLimit()) | ||||
.setFrom(options.getOffset()) | .setFrom(options.getOffset()) | ||||
.addSort(FIELD_NAME, SortOrder.ASC); | .addSort(FIELD_NAME, SortOrder.ASC); | ||||
BoolQueryBuilder filter = boolQuery() | |||||
.must(termQuery(FIELD_ACTIVE, true)); | |||||
BoolQueryBuilder filter = boolQuery().must(termQuery(FIELD_ACTIVE, true)); | |||||
userQuery.getLogins().ifPresent( | |||||
logins -> filter.must(termsQuery(FIELD_LOGIN, userQuery.getLogins().get()))); | |||||
userQuery.getExcludedLogins().ifPresent( | |||||
excludedLogins -> filter.mustNot(termsQuery(FIELD_LOGIN, userQuery.getExcludedLogins().get()))); | |||||
QueryBuilder query; | |||||
if (StringUtils.isEmpty(searchText)) { | |||||
query = matchAllQuery(); | |||||
} else { | |||||
query = QueryBuilders.multiMatchQuery(searchText, | |||||
QueryBuilder esQuery = matchAllQuery(); | |||||
Optional<String> textQuery = userQuery.getTextQuery(); | |||||
if (textQuery.isPresent()) { | |||||
esQuery = QueryBuilders.multiMatchQuery(textQuery.get(), | |||||
FIELD_LOGIN, | FIELD_LOGIN, | ||||
USER_SEARCH_GRAMS_ANALYZER.subField(FIELD_LOGIN), | USER_SEARCH_GRAMS_ANALYZER.subField(FIELD_LOGIN), | ||||
FIELD_NAME, | FIELD_NAME, | ||||
.operator(MatchQueryBuilder.Operator.AND); | .operator(MatchQueryBuilder.Operator.AND); | ||||
} | } | ||||
request.setQuery(boolQuery().must(query).filter(filter)); | |||||
request.setQuery(boolQuery().must(esQuery).filter(filter)); | |||||
return new SearchResult<>(request.get(), DOC_CONVERTER); | return new SearchResult<>(request.get(), DOC_CONVERTER); | ||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2017 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.user.index; | |||||
import com.google.common.collect.ImmutableList; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
import javax.annotation.Nullable; | |||||
import javax.annotation.concurrent.Immutable; | |||||
import static org.apache.commons.lang.StringUtils.isBlank; | |||||
@Immutable | |||||
public class UserQuery { | |||||
private final String textQuery; | |||||
private final List<String> logins; | |||||
private final List<String> excludedLogins; | |||||
private UserQuery(Builder builder) { | |||||
this.textQuery = builder.textQuery; | |||||
this.logins = builder.logins == null ? null : ImmutableList.copyOf(builder.logins); | |||||
this.excludedLogins = builder.excludedLogins == null ? null : ImmutableList.copyOf(builder.excludedLogins); | |||||
} | |||||
public Optional<String> getTextQuery() { | |||||
return Optional.ofNullable(textQuery); | |||||
} | |||||
public Optional<List<String>> getLogins() { | |||||
return Optional.ofNullable(logins); | |||||
} | |||||
public Optional<List<String>> getExcludedLogins() { | |||||
return Optional.ofNullable(excludedLogins); | |||||
} | |||||
public static Builder builder() { | |||||
return new Builder(); | |||||
} | |||||
public static class Builder { | |||||
private String textQuery; | |||||
private List<String> logins; | |||||
private List<String> excludedLogins; | |||||
private Builder() { | |||||
// enforce factory method | |||||
} | |||||
public UserQuery build() { | |||||
return new UserQuery(this); | |||||
} | |||||
public Builder setTextQuery(@Nullable String textQuery) { | |||||
this.textQuery = isBlank(textQuery) ? null : textQuery; | |||||
return this; | |||||
} | |||||
public Builder setLogins(@Nullable List<String> logins) { | |||||
this.logins = logins; | |||||
return this; | |||||
} | |||||
public Builder setExcludedLogins(@Nullable List<String> excludedLogins) { | |||||
this.excludedLogins = excludedLogins; | |||||
return this; | |||||
} | |||||
} | |||||
} |
import org.sonar.server.es.SearchResult; | import org.sonar.server.es.SearchResult; | ||||
import org.sonar.server.user.index.UserDoc; | import org.sonar.server.user.index.UserDoc; | ||||
import org.sonar.server.user.index.UserIndex; | import org.sonar.server.user.index.UserIndex; | ||||
import org.sonar.server.user.index.UserQuery; | |||||
import static com.google.common.base.MoreObjects.firstNonNull; | import static com.google.common.base.MoreObjects.firstNonNull; | ||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | ||||
SearchOptions options = new SearchOptions() | SearchOptions options = new SearchOptions() | ||||
.setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE)); | .setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE)); | ||||
List<String> fields = request.paramAsStrings(Param.FIELDS); | List<String> fields = request.paramAsStrings(Param.FIELDS); | ||||
SearchResult<UserDoc> result = userIndex.search(request.param(Param.TEXT_QUERY), options); | |||||
String textQuery = request.param(Param.TEXT_QUERY); | |||||
SearchResult<UserDoc> result = userIndex.search(UserQuery.builder().setTextQuery(textQuery).build(), options); | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
List<String> logins = Lists.transform(result.getDocs(), UserDocToLogin.INSTANCE); | List<String> logins = Lists.transform(result.getDocs(), UserDocToLogin.INSTANCE); |
{ | { | ||||
"users": [ | |||||
{ | |||||
"login": "ada.lovelace", | |||||
"name": "Ada Lovelace", | |||||
"avatar": "680b0001b4952664631511a81a4edc59" | |||||
}, | |||||
{ | |||||
"login": "grace.hopper", | |||||
"name": "Grace Hopper", | |||||
"avatar": "36d90b0b8cc8639e4960e46116dce02c" | |||||
} | |||||
], | |||||
"paging": { | |||||
"pageIndex": 1, | |||||
"pageSize": 50, | |||||
"total": 2 | |||||
} | |||||
} | } |
package org.sonar.server.organization.ws; | package org.sonar.server.organization.ws; | ||||
import com.google.common.base.Throwables; | |||||
import java.io.IOException; | |||||
import java.util.stream.IntStream; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.ExpectedException; | |||||
import org.sonar.api.config.MapSettings; | |||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.api.server.ws.WebService.Param; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.db.organization.OrganizationDto; | |||||
import org.sonar.db.permission.OrganizationPermission; | |||||
import org.sonar.db.user.GroupDto; | |||||
import org.sonar.db.user.UserDto; | |||||
import org.sonar.server.es.EsTester; | |||||
import org.sonar.server.exceptions.NotFoundException; | |||||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||||
import org.sonar.server.organization.TestDefaultOrganizationProvider; | |||||
import org.sonar.server.tester.UserSessionRule; | |||||
import org.sonar.server.user.index.UserIndex; | |||||
import org.sonar.server.user.index.UserIndexDefinition; | |||||
import org.sonar.server.user.index.UserIndexer; | |||||
import org.sonar.server.ws.TestRequest; | |||||
import org.sonar.server.ws.WsActionTester; | import org.sonar.server.ws.WsActionTester; | ||||
import org.sonarqube.ws.Common.Paging; | |||||
import org.sonarqube.ws.MediaTypes; | |||||
import org.sonarqube.ws.Organizations.SearchMembersWsResponse; | |||||
import org.sonarqube.ws.Organizations.User; | |||||
import org.sonarqube.ws.client.organization.SearchMembersWsRequest; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.assertj.core.api.Assertions.tuple; | |||||
import static org.sonar.core.util.Protobuf.setNullable; | |||||
import static org.sonar.test.JsonAssert.assertJson; | |||||
public class SearchMembersActionTest { | public class SearchMembersActionTest { | ||||
private WsActionTester ws = new WsActionTester(new SearchMembersAction()); | |||||
@Rule | |||||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||||
@Rule | |||||
public ExpectedException expectedException = ExpectedException.none(); | |||||
@Rule | |||||
public EsTester es = new EsTester(new UserIndexDefinition(new MapSettings())); | |||||
@Rule | |||||
public DbTester db = DbTester.create(); | |||||
private DbClient dbClient = db.getDbClient(); | |||||
private DefaultOrganizationProvider organizationProvider = TestDefaultOrganizationProvider.from(db); | |||||
private UserIndexer indexer = new UserIndexer(dbClient, es.client()); | |||||
private WsActionTester ws = new WsActionTester(new SearchMembersAction(dbClient, new UserIndex(es.client()), organizationProvider, userSession)); | |||||
private SearchMembersWsRequest request = new SearchMembersWsRequest(); | |||||
@Test | |||||
public void empty_response() { | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).isEmpty(); | |||||
assertThat(result.getPaging()) | |||||
.extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal) | |||||
.containsExactly(1, 50, 0); | |||||
} | |||||
@Test | |||||
public void search_members_of_default_organization() { | |||||
OrganizationDto defaultOrganization = db.getDefaultOrganization(); | |||||
OrganizationDto anotherOrganization = db.organizations().insert(); | |||||
UserDto user = insertUser(); | |||||
UserDto anotherUser = insertUser(); | |||||
UserDto userInAnotherOrganization = insertUser(); | |||||
db.organizations().addMember(defaultOrganization, user); | |||||
db.organizations().addMember(defaultOrganization, anotherUser); | |||||
db.organizations().addMember(anotherOrganization, userInAnotherOrganization); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()) | |||||
.extracting(User::getLogin, User::getName) | |||||
.containsOnly( | |||||
tuple(user.getLogin(), user.getName()), | |||||
tuple(anotherUser.getLogin(), anotherUser.getName())); | |||||
} | |||||
@Test | |||||
public void search_members_of_specified_organization() { | |||||
OrganizationDto organization = db.organizations().insert(); | |||||
OrganizationDto anotherOrganization = db.organizations().insert(); | |||||
UserDto user = insertUser(); | |||||
UserDto anotherUser = insertUser(); | |||||
UserDto userInAnotherOrganization = insertUser(); | |||||
db.organizations().addMember(organization, user); | |||||
db.organizations().addMember(organization, anotherUser); | |||||
db.organizations().addMember(anotherOrganization, userInAnotherOrganization); | |||||
request.setOrganization(organization.getKey()); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()) | |||||
.extracting(User::getLogin, User::getName) | |||||
.containsOnly( | |||||
tuple(user.getLogin(), user.getName()), | |||||
tuple(anotherUser.getLogin(), anotherUser.getName())); | |||||
} | |||||
@Test | |||||
public void return_avatar() { | |||||
UserDto user = db.users().insertUser(u -> u.setEmail("email@domain.com")); | |||||
indexer.index(user.getLogin()); | |||||
db.organizations().addMember(db.getDefaultOrganization(), user); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsers(0).getAvatar()).isEqualTo("7328fddefd53de471baeb6e2b764f78a"); | |||||
} | |||||
@Test | |||||
public void do_not_return_group_count_if_no_admin_permission() { | |||||
UserDto user = insertUser(); | |||||
GroupDto group = db.users().insertGroup(); | |||||
db.users().insertMember(group, user); | |||||
db.organizations().addMember(db.getDefaultOrganization(), user); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsers(0).hasGroupCount()).isFalse(); | |||||
} | |||||
@Test | |||||
public void return_group_counts_if_org_admin() { | |||||
userSession.addPermission(OrganizationPermission.ADMINISTER, db.getDefaultOrganization()); | |||||
UserDto user = insertUser(); | |||||
UserDto anotherUser = insertUser(); | |||||
IntStream.range(0, 10) | |||||
.mapToObj(i -> db.users().insertGroup()) | |||||
.forEach(g -> db.users().insertMembers(g, user)); | |||||
OrganizationDto anotherOrganization = db.organizations().insert(); | |||||
GroupDto anotherGroup = db.users().insertGroup(anotherOrganization); | |||||
db.users().insertMember(anotherGroup, user); | |||||
db.organizations().addMember(db.getDefaultOrganization(), user); | |||||
db.organizations().addMember(db.getDefaultOrganization(), anotherUser); | |||||
db.organizations().addMember(anotherOrganization, user); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).extracting(User::getLogin, User::getGroupCount).containsOnly( | |||||
tuple(user.getLogin(), 10), | |||||
tuple(anotherUser.getLogin(), 0)); | |||||
} | |||||
@Test | |||||
public void search_non_members() { | |||||
OrganizationDto defaultOrganization = db.getDefaultOrganization(); | |||||
OrganizationDto anotherOrganization = db.organizations().insert(); | |||||
UserDto user = insertUser(); | |||||
UserDto anotherUser = insertUser(); | |||||
UserDto userInAnotherOrganization = insertUser(); | |||||
db.organizations().addMember(anotherOrganization, user); | |||||
db.organizations().addMember(anotherOrganization, anotherUser); | |||||
db.organizations().addMember(defaultOrganization, userInAnotherOrganization); | |||||
request.setSelected(WebService.SelectionMode.DESELECTED.value()); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()) | |||||
.extracting(User::getLogin, User::getName) | |||||
.containsOnly( | |||||
tuple(user.getLogin(), user.getName()), | |||||
tuple(anotherUser.getLogin(), anotherUser.getName())); | |||||
} | |||||
@Test | |||||
public void search_members_pagination() { | |||||
IntStream.range(0, 10).forEach(i -> { | |||||
UserDto userDto = db.users().insertUser(user -> user.setName("USER_" + i)); | |||||
db.organizations().addMember(db.getDefaultOrganization(), userDto); | |||||
indexer.index(userDto.getLogin()); | |||||
}); | |||||
request.setPage(2).setPageSize(3); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).extracting(User::getName) | |||||
.containsExactly("USER_3", "USER_4", "USER_5"); | |||||
assertThat(result.getPaging()) | |||||
.extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal) | |||||
.containsExactly(2, 3, 10); | |||||
} | |||||
@Test | |||||
public void search_members_by_name() { | |||||
IntStream.range(0, 10).forEach(i -> { | |||||
UserDto userDto = db.users().insertUser(user -> user.setName("USER_" + i)); | |||||
db.organizations().addMember(db.getDefaultOrganization(), userDto); | |||||
indexer.index(userDto.getLogin()); | |||||
}); | |||||
request.setQuery("_9"); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).extracting(User::getName).containsExactly("USER_9"); | |||||
} | |||||
@Test | |||||
public void search_members_by_login() { | |||||
IntStream.range(0, 10).forEach(i -> { | |||||
UserDto userDto = db.users().insertUser(user -> user.setLogin("USER_" + i)); | |||||
db.organizations().addMember(db.getDefaultOrganization(), userDto); | |||||
indexer.index(userDto.getLogin()); | |||||
}); | |||||
request.setQuery("_9"); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).extracting(User::getLogin).containsExactly("USER_9"); | |||||
} | |||||
@Test | |||||
public void search_members_by_email() { | |||||
IntStream.range(0, 10).forEach(i -> { | |||||
UserDto userDto = db.users().insertUser(user -> user | |||||
.setLogin("L" + i) | |||||
.setEmail("USER_" + i + "@email.com")); | |||||
db.organizations().addMember(db.getDefaultOrganization(), userDto); | |||||
indexer.index(userDto.getLogin()); | |||||
}); | |||||
request.setQuery("_9"); | |||||
SearchMembersWsResponse result = call(); | |||||
assertThat(result.getUsersList()).extracting(User::getLogin).containsExactly("L9"); | |||||
} | |||||
@Test | |||||
public void json_example() { | |||||
UserDto ada = db.users().insertUser(u -> u.setLogin("ada.lovelace").setName("Ada Lovelace").setEmail("ada@lovelace.com")); | |||||
indexer.index(ada.getLogin()); | |||||
UserDto grace = db.users().insertUser(u -> u.setLogin("grace.hopper").setName("Grace Hopper").setEmail("grace@hopper.com")); | |||||
indexer.index(grace.getLogin()); | |||||
db.organizations().addMember(db.getDefaultOrganization(), ada); | |||||
db.organizations().addMember(db.getDefaultOrganization(), grace); | |||||
String result = ws.newRequest().execute().getInput(); | |||||
assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString()); | |||||
} | |||||
@Test | |||||
public void fail_if_organization_is_unknown() { | |||||
request.setOrganization("ORGA 42"); | |||||
expectedException.expect(NotFoundException.class); | |||||
expectedException.expectMessage("No organization with key 'ORGA 42'"); | |||||
call(); | |||||
} | |||||
@Test | |||||
public void fail_if_page_size_greater_than_500() { | |||||
request.setPageSize(501); | |||||
expectedException.expect(IllegalArgumentException.class); | |||||
expectedException.expectMessage("Page size must lower than or equal to 500"); | |||||
call(); | |||||
} | |||||
@Test | |||||
public void fail_if_query_length_lower_than_2() { | |||||
request.setQuery("a"); | |||||
expectedException.expect(IllegalArgumentException.class); | |||||
expectedException.expectMessage("Query length must be greater than or equal to 2"); | |||||
call(); | |||||
} | |||||
@Test | @Test | ||||
public void definition() { | public void definition() { | ||||
WebService.Action action = ws.getDef(); | WebService.Action action = ws.getDef(); | ||||
assertThat(action.key()).isEqualTo("search_members"); | assertThat(action.key()).isEqualTo("search_members"); | ||||
assertThat(action.params()).extracting(WebService.Param::key) | |||||
.containsOnly("q", "selected", "p", "ps", "organization"); | |||||
assertThat(action.params()).extracting(Param::key).containsOnly("q", "selected", "p", "ps", "organization"); | |||||
assertThat(action.description()).isNotEmpty(); | |||||
assertThat(action.responseExampleAsString()).isNotEmpty(); | assertThat(action.responseExampleAsString()).isNotEmpty(); | ||||
assertThat(action.since()).isEqualTo("6.4"); | assertThat(action.since()).isEqualTo("6.4"); | ||||
assertThat(action.isInternal()).isTrue(); | assertThat(action.isInternal()).isTrue(); | ||||
assertThat(action.isPost()).isFalse(); | assertThat(action.isPost()).isFalse(); | ||||
assertThat(action.param("organization").isInternal()).isTrue(); | |||||
Param selected = action.param("selected"); | |||||
assertThat(selected.possibleValues()).containsOnly("selected", "deselected"); | |||||
assertThat(selected.isInternal()).isTrue(); | |||||
assertThat(selected.defaultValue()).isEqualTo("selected"); | |||||
} | |||||
private UserDto insertUser() { | |||||
UserDto userDto = db.users().insertUser(); | |||||
indexer.index(userDto.getLogin()); | |||||
return userDto; | |||||
} | |||||
private SearchMembersWsResponse call() { | |||||
TestRequest wsRequest = ws.newRequest() | |||||
.setMediaType(MediaTypes.PROTOBUF); | |||||
setNullable(request.getOrganization(), o -> wsRequest.setParam("organization", o)); | |||||
setNullable(request.getQuery(), q -> wsRequest.setParam(Param.TEXT_QUERY, q)); | |||||
setNullable(request.getPage(), p -> wsRequest.setParam(Param.PAGE, String.valueOf(p))); | |||||
setNullable(request.getPageSize(), ps -> wsRequest.setParam(Param.PAGE_SIZE, String.valueOf(ps))); | |||||
setNullable(request.getSelected(), s -> wsRequest.setParam(Param.SELECTED, s)); | |||||
try { | |||||
return SearchMembersWsResponse.parseFrom(wsRequest.execute().getInputStream()); | |||||
} catch (IOException e) { | |||||
throw Throwables.propagate(e); | |||||
} | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.server.user.index; | package org.sonar.server.user.index; | ||||
import java.util.Arrays; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import org.sonar.api.config.MapSettings; | import org.sonar.api.config.MapSettings; | ||||
import org.sonar.server.es.EsTester; | import org.sonar.server.es.EsTester; | ||||
import org.sonar.server.es.SearchOptions; | import org.sonar.server.es.SearchOptions; | ||||
import org.sonar.server.es.SearchResult; | |||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static java.util.Collections.emptyList; | |||||
import static java.util.Collections.singletonList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.sonar.server.user.index.UserIndexDefinition.INDEX_TYPE_USER; | import static org.sonar.server.user.index.UserIndexDefinition.INDEX_TYPE_USER; | ||||
private UserIndex underTest; | private UserIndex underTest; | ||||
private UserQuery.Builder userQuery = UserQuery.builder(); | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
underTest = new UserIndex(esTester.client()); | underTest = new UserIndex(esTester.client()); | ||||
@Test | @Test | ||||
public void searchUsers() throws Exception { | public void searchUsers() throws Exception { | ||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER1_LOGIN, Arrays.asList("user_1", "u1")).setEmail("email1")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER1_LOGIN, asList("user_1", "u1")).setEmail("email1")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER2_LOGIN, Collections.<String>emptyList()).setEmail("email2")); | esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER2_LOGIN, Collections.<String>emptyList()).setEmail("email2")); | ||||
assertThat(underTest.search(null, new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search("user", new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search("ser", new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search(USER1_LOGIN, new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search(USER2_LOGIN, new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search("mail", new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search("EMAIL1", new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search(userQuery.build(), new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search(userQuery.setTextQuery("user").build(), new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search(userQuery.setTextQuery("ser").build(), new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search(userQuery.setTextQuery(USER1_LOGIN).build(), new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search(userQuery.setTextQuery(USER2_LOGIN).build(), new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search(userQuery.setTextQuery("mail").build(), new SearchOptions()).getDocs()).hasSize(2); | |||||
assertThat(underTest.search(userQuery.setTextQuery("EMAIL1").build(), new SearchOptions()).getDocs()).hasSize(1); | |||||
} | |||||
@Test | |||||
public void search_users_filter_logins() throws Exception { | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER1_LOGIN, asList("user_1", "u1")).setEmail("email1")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER2_LOGIN, emptyList()).setEmail("email2")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser("user3", emptyList()).setEmail("email2")); | |||||
SearchResult<UserDoc> result = underTest.search(userQuery.setLogins(asList(USER1_LOGIN, USER2_LOGIN)).build(), new SearchOptions()); | |||||
assertThat(result.getDocs()).hasSize(2) | |||||
.extracting(UserDoc::login) | |||||
.containsOnly(USER1_LOGIN, USER2_LOGIN); | |||||
} | |||||
@Test | |||||
public void search_users_filter_logins_that_match_exactly() { | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser("USER_1", asList("user_1", "u1")).setEmail("email1")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser("UsEr_1", emptyList()).setEmail("email2")); | |||||
assertThat(underTest.search(userQuery.setLogins(singletonList("USER_1")).build(), new SearchOptions()).getDocs()).hasSize(1); | |||||
assertThat(underTest.search(userQuery.setLogins(singletonList("USER_")).build(), new SearchOptions()).getDocs()).isEmpty(); | |||||
assertThat(underTest.search(userQuery.setLogins(emptyList()).build(), new SearchOptions()).getDocs()).isEmpty(); | |||||
} | |||||
@Test | |||||
public void search_users_exclude_logins() throws Exception { | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER1_LOGIN, asList("user_1", "u1")).setEmail("email1")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser(USER2_LOGIN, emptyList()).setEmail("email2")); | |||||
esTester.putDocuments(INDEX_TYPE_USER.getIndex(), INDEX_TYPE_USER.getType(), newUser("user3", emptyList()).setEmail("email2")); | |||||
assertThat(underTest.search(userQuery.setExcludedLogins(emptyList()).build(), new SearchOptions()).getDocs()).hasSize(3); | |||||
assertThat(underTest.search(userQuery.setExcludedLogins(asList(USER1_LOGIN, USER2_LOGIN, "user3")).build(), new SearchOptions()).getDocs()).isEmpty(); | |||||
assertThat(underTest.search(userQuery.setExcludedLogins(asList(USER1_LOGIN, USER2_LOGIN)).build(), new SearchOptions()).getDocs()).hasSize(1); | |||||
} | } | ||||
private static UserDoc newUser(String login, List<String> scmAccounts) { | private static UserDoc newUser(String login, List<String> scmAccounts) { |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2017 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.sonarqube.ws.client.organization; | |||||
import javax.annotation.CheckForNull; | |||||
import javax.annotation.Nullable; | |||||
public class SearchMembersWsRequest { | |||||
private String organization; | |||||
private String selected; | |||||
private String query; | |||||
private Integer page; | |||||
private Integer pageSize; | |||||
@CheckForNull | |||||
public String getOrganization() { | |||||
return organization; | |||||
} | |||||
public SearchMembersWsRequest setOrganization(@Nullable String organization) { | |||||
this.organization = organization; | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public String getSelected() { | |||||
return selected; | |||||
} | |||||
public SearchMembersWsRequest setSelected(@Nullable String selected) { | |||||
this.selected = selected; | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public String getQuery() { | |||||
return query; | |||||
} | |||||
public SearchMembersWsRequest setQuery(@Nullable String query) { | |||||
this.query = query; | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public Integer getPage() { | |||||
return page; | |||||
} | |||||
public SearchMembersWsRequest setPage(@Nullable Integer page) { | |||||
this.page = page; | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public Integer getPageSize() { | |||||
return pageSize; | |||||
} | |||||
public SearchMembersWsRequest setPageSize(@Nullable Integer pageSize) { | |||||
this.pageSize = pageSize; | |||||
return this; | |||||
} | |||||
} |
package sonarqube.ws.organizations; | package sonarqube.ws.organizations; | ||||
import "ws-commons.proto"; | |||||
option java_package = "org.sonarqube.ws"; | option java_package = "org.sonarqube.ws"; | ||||
option java_outer_classname = "Organizations"; | option java_outer_classname = "Organizations"; | ||||
option optimize_for = SPEED; | option optimize_for = SPEED; | ||||
repeated Organization organizations = 1; | repeated Organization organizations = 1; | ||||
} | } | ||||
// WS api/organizations/search_members | |||||
message SearchMembersWsResponse { | |||||
optional sonarqube.ws.commons.Paging paging = 1; | |||||
repeated User users = 2; | |||||
} | |||||
message Organization { | message Organization { | ||||
optional string key = 1; | optional string key = 1; | ||||
optional string name = 2; | optional string name = 2; | ||||
optional string avatar = 5; | optional string avatar = 5; | ||||
optional bool guarded = 6; | optional bool guarded = 6; | ||||
} | } | ||||
message User { | |||||
optional string login = 1; | |||||
optional string name = 2; | |||||
optional string avatar = 3; | |||||
optional int32 groupCount = 4; | |||||
} |