From: Julien Lancelot Date: Mon, 3 Apr 2017 13:52:25 +0000 (+0200) Subject: SONAR-9016 Use 'Paging' in api/user_groups/search WS X-Git-Tag: 6.4-RC1~418 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dc16c19711d8a5863c37bb60d73eb10071d638a4;p=sonarqube.git SONAR-9016 Use 'Paging' in api/user_groups/search WS --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java index 695300fa2d8..15f70e04fe5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java @@ -24,12 +24,13 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; +import org.sonar.api.server.ws.Change; 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.NewController; import org.sonar.api.server.ws.WebService.Param; -import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.utils.Paging; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -39,13 +40,17 @@ import org.sonar.server.es.SearchOptions; import org.sonar.server.user.UserSession; import static org.apache.commons.lang.StringUtils.defaultIfBlank; +import static org.sonar.api.utils.Paging.forPageIndex; +import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.server.es.SearchOptions.MAX_LIMIT; import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY; +import static org.sonar.server.ws.WsUtils.writeProtobuf; +import static org.sonarqube.ws.WsUserGroups.Group; +import static org.sonarqube.ws.WsUserGroups.SearchWsResponse; public class SearchAction implements UserGroupsWsAction { - private static final String FIELD_ID = "id"; private static final String FIELD_NAME = "name"; private static final String FIELD_DESCRIPTION = "description"; private static final String FIELD_MEMBERS_COUNT = "membersCount"; @@ -71,7 +76,8 @@ public class SearchAction implements UserGroupsWsAction { .setSince("5.2") .addFieldsParam(ALL_FIELDS) .addPagingParams(100, MAX_LIMIT) - .addSearchQuery("sonar-users", "names"); + .addSearchQuery("sonar-users", "names") + .setChangelog(new Change("6.4", "Paging response fields moved to a Paging object")); action.createParam(PARAM_ORGANIZATION_KEY) .setDescription("Key of organization. If not set then groups are searched in default organization.") @@ -95,32 +101,12 @@ public class SearchAction implements UserGroupsWsAction { userSession.checkLoggedIn().checkPermission(ADMINISTER, organization); int limit = dbClient.groupDao().countByQuery(dbSession, organization.getUuid(), query); + Paging paging = forPageIndex(page).withPageSize(pageSize).andTotal(limit); List groups = dbClient.groupDao().selectByQuery(dbSession, organization.getUuid(), query, options.getOffset(), pageSize); List groupIds = groups.stream().map(GroupDto::getId).collect(MoreCollectors.toList(groups.size())); Map userCountByGroup = dbClient.groupMembershipDao().countUsersByGroups(dbSession, groupIds); - - JsonWriter json = response.newJsonWriter().beginObject(); - options.writeJson(json, limit); - writeGroups(json, groups, userCountByGroup, fields); - json.endObject().close(); - } - } - - private static void writeGroups(JsonWriter json, List groups, Map userCountByGroup, Set fields) { - json.name("groups").beginArray(); - for (GroupDto group : groups) { - writeGroup(json, group, userCountByGroup.get(group.getName()), fields); + writeProtobuf(buildResponse(groups, userCountByGroup, fields, paging), request, response); } - json.endArray(); - } - - private static void writeGroup(JsonWriter json, GroupDto group, Integer memberCount, Set fields) { - json.beginObject() - .prop(FIELD_ID, group.getId().toString()) - .prop(FIELD_NAME, fields.contains(FIELD_NAME) ? group.getName() : null) - .prop(FIELD_DESCRIPTION, fields.contains(FIELD_DESCRIPTION) ? group.getDescription() : null) - .prop(FIELD_MEMBERS_COUNT, fields.contains(FIELD_MEMBERS_COUNT) ? memberCount : null) - .endObject(); } private static Set neededFields(Request request) { @@ -133,4 +119,30 @@ public class SearchAction implements UserGroupsWsAction { } return fields; } + + private static SearchWsResponse buildResponse(List groups, Map userCountByGroup, Set fields, Paging paging) { + SearchWsResponse.Builder responseBuilder = SearchWsResponse.newBuilder(); + groups.forEach(group -> responseBuilder.addGroups(toWsGroup(group, userCountByGroup.get(group.getName()), fields))); + responseBuilder.getPagingBuilder() + .setPageIndex(paging.pageIndex()) + .setPageSize(paging.pageSize()) + .setTotal(paging.total()) + .build(); + return responseBuilder.build(); + } + + private static Group toWsGroup(GroupDto group, Integer memberCount, Set fields) { + Group.Builder groupBuilder = Group.newBuilder().setId(group.getId()); + if (fields.contains(FIELD_NAME)) { + groupBuilder.setName(group.getName()); + } + if (fields.contains(FIELD_DESCRIPTION)) { + setNullable(group.getDescription(), groupBuilder::setDescription); + } + if (fields.contains(FIELD_MEMBERS_COUNT)) { + groupBuilder.setMembersCount(memberCount); + } + return groupBuilder.build(); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java index b5b5acf73bf..169661b3f9f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java @@ -19,10 +19,11 @@ */ package org.sonar.server.usergroups.ws; +import com.google.common.base.Throwables; +import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; @@ -33,12 +34,20 @@ import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Common.Paging; +import org.sonarqube.ws.MediaTypes; import static org.apache.commons.lang.StringUtils.capitalize; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.sonar.api.server.ws.WebService.Param.FIELDS; +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.TEXT_QUERY; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.db.user.GroupTesting.newGroupDto; -import static org.sonar.test.JsonAssert.assertJson; +import static org.sonarqube.ws.WsUserGroups.Group; +import static org.sonarqube.ws.WsUserGroups.SearchWsResponse; public class SearchActionTest { @@ -57,14 +66,10 @@ public class SearchActionTest { public void search_empty() throws Exception { loginAsDefaultOrgAdmin(); - String result = newRequest().execute().getInput(); + SearchWsResponse response = call(ws.newRequest()); - assertJson(result).isSimilarTo("{\n" + - " \"p\": 1,\n" + - " \"ps\": 100,\n" + - " \"total\": 0,\n" + - " \"groups\": []\n" + - "}"); + assertThat(response.getGroupsList()).isEmpty(); + assertThat(response.getPaging().getTotal()).isZero(); } @Test @@ -76,17 +81,14 @@ public class SearchActionTest { insertGroup(db.getDefaultOrganization(), "customer3", 0); loginAsDefaultOrgAdmin(); - String result = newRequest().execute().getInput(); - - assertJson(result).isSimilarTo("{\n" + - " \"groups\": [\n" + - " {\"name\": \"admins\", \"description\": \"Admins\", \"membersCount\": 0},\n" + - " {\"name\": \"customer1\", \"description\": \"Customer1\", \"membersCount\": 0},\n" + - " {\"name\": \"customer2\", \"description\": \"Customer2\", \"membersCount\": 0},\n" + - " {\"name\": \"customer3\", \"description\": \"Customer3\", \"membersCount\": 0},\n" + - " {\"name\": \"users\", \"description\": \"Users\", \"membersCount\": 0}\n" + - " ]\n" + - "}"); + SearchWsResponse response = call(ws.newRequest()); + + assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( + tuple("admins", "Admins", 0), + tuple("customer1", "Customer1", 0), + tuple("customer2", "Customer2", 0), + tuple("customer3", "Customer3", 0), + tuple("users", "Users", 0)); } @Test @@ -98,17 +100,14 @@ public class SearchActionTest { insertGroup(db.getDefaultOrganization(), "customer3", 0); loginAsDefaultOrgAdmin(); - String result = newRequest().execute().getInput(); - - assertJson(result).isSimilarTo("{\n" + - " \"groups\": [\n" + - " {\"name\": \"admins\", \"description\": \"Admins\", \"membersCount\": 1},\n" + - " {\"name\": \"customer1\", \"description\": \"Customer1\", \"membersCount\": 0},\n" + - " {\"name\": \"customer2\", \"description\": \"Customer2\", \"membersCount\": 4},\n" + - " {\"name\": \"customer3\", \"description\": \"Customer3\", \"membersCount\": 0},\n" + - " {\"name\": \"users\", \"description\": \"Users\", \"membersCount\": 5}\n" + - " ]\n" + - "}\n"); + SearchWsResponse response = call(ws.newRequest()); + + assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( + tuple("admins", "Admins", 1), + tuple("customer1", "Customer1", 0), + tuple("customer2", "Customer2", 4), + tuple("customer3", "Customer3", 0), + tuple("users", "Users", 5)); } @Test @@ -120,15 +119,12 @@ public class SearchActionTest { insertGroup(db.getDefaultOrganization(), "customer%_%/3", 0); loginAsDefaultOrgAdmin(); - String result = newRequest().setParam(Param.TEXT_QUERY, "tomer%_%/").execute().getInput(); + SearchWsResponse response = call(ws.newRequest().setParam(TEXT_QUERY, "tomer%_%/")); - assertJson(result).ignoreFields("id").isSimilarTo("{\n" + - " \"groups\": [\n" + - " {\"name\": \"customer%_%/1\", \"description\": \"Customer%_%/1\", \"membersCount\": 0},\n" + - " {\"name\": \"customer%_%/2\", \"description\": \"Customer%_%/2\", \"membersCount\": 0},\n" + - " {\"name\": \"customer%_%/3\", \"description\": \"Customer%_%/3\", \"membersCount\": 0}\n" + - " ]\n" + - "}\n"); + assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( + tuple("customer%_%/1", "Customer%_%/1", 0), + tuple("customer%_%/2", "Customer%_%/2", 0), + tuple("customer%_%/3", "Customer%_%/3", 0)); } @Test @@ -140,31 +136,22 @@ public class SearchActionTest { insertGroup(db.getDefaultOrganization(), "customer3", 0); loginAsDefaultOrgAdmin(); - assertJson(newRequest().setParam(Param.PAGE_SIZE, "3").execute().getInput()).isSimilarTo("{\n" + - " \"p\": 1,\n" + - " \"ps\": 3,\n" + - " \"total\": 5,\n" + - " \"groups\": [\n" + - " {\"name\": \"admins\", \"description\": \"Admins\", \"membersCount\": 0},\n" + - " {\"name\": \"customer1\", \"description\": \"Customer1\", \"membersCount\": 0},\n" + - " {\"name\": \"customer2\", \"description\": \"Customer2\", \"membersCount\": 0}\n" + - " ]\n" + - "}\n"); - assertJson(newRequest().setParam(Param.PAGE_SIZE, "3").setParam(Param.PAGE, "2").execute().getInput()).isSimilarTo("{\n" + - " \"p\": 2,\n" + - " \"ps\": 3,\n" + - " \"total\": 5,\n" + - " \"groups\": [\n" + - " {\"name\": \"customer3\", \"description\": \"Customer3\", \"membersCount\": 0},\n" + - " {\"name\": \"users\", \"description\": \"Users\", \"membersCount\": 0}\n" + - " ]\n" + - "}\n"); - assertJson(newRequest().setParam(Param.PAGE_SIZE, "3").setParam(Param.PAGE, "3").execute().getInput()).isSimilarTo("{\n" + - " \"p\": 3,\n" + - " \"ps\": 3,\n" + - " \"total\": 5,\n" + - " \"groups\": []\n" + - "}\n"); + SearchWsResponse response = call(ws.newRequest().setParam(PAGE_SIZE, "3")); + assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 3, 5); + assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( + tuple("admins", "Admins", 0), + tuple("customer1", "Customer1", 0), + tuple("customer2", "Customer2", 0)); + + response = call(ws.newRequest().setParam(PAGE_SIZE, "3").setParam(PAGE, "2")); + assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(2, 3, 5); + assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( + tuple("customer3", "Customer3", 0), + tuple("users", "Users", 0)); + + response = call(ws.newRequest().setParam(PAGE_SIZE, "3").setParam(PAGE, "3")); + assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(3, 3, 5); + assertThat(response.getGroupsList()).isEmpty(); } @Test @@ -172,35 +159,16 @@ public class SearchActionTest { insertGroup(db.getDefaultOrganization(), "sonar-users", 0); loginAsDefaultOrgAdmin(); - assertThat(newRequest().execute().getInput()) - .contains("id") - .contains("name") - .contains("description") - .contains("membersCount"); - - assertThat(newRequest().setParam(Param.FIELDS, "").execute().getInput()) - .contains("id") - .contains("name") - .contains("description") - .contains("membersCount"); - - assertThat(newRequest().setParam(Param.FIELDS, "name").execute().getInput()) - .contains("id") - .contains("name") - .doesNotContain("description") - .doesNotContain("membersCount"); - - assertThat(newRequest().setParam(Param.FIELDS, "description").execute().getInput()) - .contains("id") - .doesNotContain("name") - .contains("description") - .doesNotContain("membersCount"); - - assertThat(newRequest().setParam(Param.FIELDS, "membersCount").execute().getInput()) - .contains("id") - .doesNotContain("name") - .doesNotContain("description") - .contains("membersCount"); + assertThat(call(ws.newRequest()).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) + .containsOnly(tuple(true, true, true, true)); + assertThat(call(ws.newRequest().setParam(FIELDS, "")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) + .containsOnly(tuple(true, true, true, true)); + assertThat(call(ws.newRequest().setParam(FIELDS, "name")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) + .containsOnly(tuple(true, true, false, false)); + assertThat(call(ws.newRequest().setParam(FIELDS, "description")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) + .containsOnly(tuple(true, false, true, false)); + assertThat(call(ws.newRequest().setParam(FIELDS, "membersCount")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) + .containsOnly(tuple(true, false, false, true)); } @Test @@ -212,9 +180,9 @@ public class SearchActionTest { loginAsDefaultOrgAdmin(); userSession.addPermission(ADMINISTER, org); - String result = newRequest().setParam("organization", org.getKey()).execute().getInput(); + SearchWsResponse response = call(ws.newRequest().setParam("organization", org.getKey())); - assertJson(result).isSimilarTo("{\"groups\":[{\"id\":\"" + group.getId() + "\",\"name\":\"users\"}]}\n"); + assertThat(response.getGroupsList()).extracting(Group::getId, Group::getName).containsOnly(tuple(group.getId().longValue(), "users")); } @Test @@ -222,11 +190,16 @@ public class SearchActionTest { userSession.anonymous(); expectedException.expect(UnauthorizedException.class); - newRequest().execute(); + call(ws.newRequest()); } - private TestRequest newRequest() { - return ws.newRequest(); + private SearchWsResponse call(TestRequest request) { + request.setMediaType(MediaTypes.PROTOBUF); + try { + return SearchWsResponse.parseFrom(request.execute().getInputStream()); + } catch (IOException e) { + throw Throwables.propagate(e); + } } private void insertGroup(OrganizationDto org, String name, int numberOfMembers) { diff --git a/server/sonar-web/src/main/js/apps/groups/groups.js b/server/sonar-web/src/main/js/apps/groups/groups.js index e21b53ec6af..8cfd82d3512 100644 --- a/server/sonar-web/src/main/js/apps/groups/groups.js +++ b/server/sonar-web/src/main/js/apps/groups/groups.js @@ -32,9 +32,9 @@ export default Backbone.Collection.extend({ }, parse(r) { - this.total = +r.total; - this.p = +r.p; - this.ps = +r.ps; + this.total = +r.paging.total; + this.p = +r.paging.pageIndex; + this.ps = +r.paging.pageSize; return r.groups; }, diff --git a/sonar-ws/src/main/protobuf/ws-user_groups.proto b/sonar-ws/src/main/protobuf/ws-user_groups.proto index cd57e802bc3..d9fc797f484 100644 --- a/sonar-ws/src/main/protobuf/ws-user_groups.proto +++ b/sonar-ws/src/main/protobuf/ws-user_groups.proto @@ -20,6 +20,8 @@ syntax = "proto2"; package sonarqube.ws.usergroup; +import "ws-commons.proto"; + option java_package = "org.sonarqube.ws"; option java_outer_classname = "WsUserGroups"; option optimize_for = SPEED; @@ -36,7 +38,8 @@ message UpdateWsResponse { // WS api/user_groups/search message SearchWsResponse { - repeated Group groups = 1; + optional sonarqube.ws.commons.Paging paging = 1; + repeated Group groups = 2; } message Group {