diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2019-06-20 17:24:52 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-06-28 08:45:45 +0200 |
commit | 071dd097a358d5750b2cc4be0f4216df111eb2d6 (patch) | |
tree | 671b2ff8b1b6d7ce38b60cd09b3b0ea5009666d3 /server | |
parent | 0d76849de3972733bf0105eda59870cb16560dc5 (diff) | |
download | sonarqube-071dd097a358d5750b2cc4be0f4216df111eb2d6.tar.gz sonarqube-071dd097a358d5750b2cc4be0f4216df111eb2d6.zip |
SONAR-12221 Add pagination to api/qualitygates/search
Diffstat (limited to 'server')
7 files changed, 113 insertions, 147 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDto.java index 3dd29f1e214..b510eef339f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDto.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; public class ProjectQgateAssociationDto { private Long id; + private String key; private String name; private String gateId; @@ -40,6 +41,15 @@ public class ProjectQgateAssociationDto { return this; } + public String getKey() { + return key; + } + + public ProjectQgateAssociationDto setKey(String key) { + this.key = key; + return this; + } + public String getName() { return name; } @@ -59,10 +69,4 @@ public class ProjectQgateAssociationDto { return this; } - public ProjectQgateAssociation toQgateAssociation() { - return new ProjectQgateAssociation() - .setId(id) - .setName(name) - .setMember(gateId != null); - } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.xml index a2a05d73d07..547eeeacc09 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.xml @@ -4,7 +4,7 @@ <mapper namespace="org.sonar.db.qualitygate.ProjectQgateAssociationMapper"> <select id="selectProjects" parameterType="map" resultType="ProjectQgateAssociation"> - SELECT proj.id as id, proj.name as name, prop.text_value as gateId + SELECT proj.id as id, proj.kee as key, proj.name as name, prop.text_value as gateId FROM projects proj LEFT JOIN properties prop ON prop.resource_id=proj.id AND prop.prop_key='sonar.qualitygate' AND prop.text_value = #{query.gateId} where diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDaoTest.java index 8033ad411f9..2f0a99162fa 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDaoTest.java @@ -56,11 +56,11 @@ public class ProjectQgateAssociationDaoTest { .build()); assertThat(result) - .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) + .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getKey, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) .containsExactlyInAnyOrder( - tuple(project1.getId(), project1.name(), qualityGate1.getId().toString()), - tuple(project2.getId(), project2.name(), qualityGate1.getId().toString()), - tuple(project3.getId(), project3.name(), null)); + tuple(project1.getId(), project1.getKey(), project1.name(), qualityGate1.getId().toString()), + tuple(project2.getId(), project2.getKey(), project2.name(), qualityGate1.getId().toString()), + tuple(project3.getId(), project3.getKey(), project3.name(), null)); } @Test diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDtoTest.java deleted file mode 100644 index e63776f3acc..00000000000 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/ProjectQgateAssociationDtoTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ProjectQgateAssociationDtoTest { - - @Test - public void to_assoc_with_project_having_assoc() { - ProjectQgateAssociation project = new ProjectQgateAssociationDto() - .setId(1L) - .setName("polop") - .setGateId("10") - .toQgateAssociation(); - - assertThat(project.id()).isEqualTo(1); - assertThat(project.name()).isEqualTo("polop"); - assertThat(project.isMember()).isTrue(); - } - - @Test - public void to_assoc_with_project_not_having_assoc() { - ProjectQgateAssociation project = new ProjectQgateAssociationDto() - .setId(1L) - .setName("polop") - .setGateId(null) - .toQgateAssociation(); - - assertThat(project.id()).isEqualTo(1); - assertThat(project.name()).isEqualTo("polop"); - assertThat(project.isMember()).isFalse(); - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java index fcf81e8142c..743b0e65370 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java @@ -22,6 +22,7 @@ package org.sonar.server.qualitygate.ws; import com.google.common.io.Resources; import java.util.Collection; import java.util.List; +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; @@ -31,13 +32,13 @@ import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.qualitygate.ProjectQgateAssociation; import org.sonar.db.qualitygate.ProjectQgateAssociationDto; import org.sonar.db.qualitygate.ProjectQgateAssociationQuery; import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Qualitygates; +import static java.util.stream.Collectors.toList; import static org.sonar.api.server.ws.WebService.Param.SELECTED; import static org.sonar.api.utils.Paging.forPageIndex; import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.ANY; @@ -66,6 +67,10 @@ public class SearchAction implements QualityGatesWsAction { "Only authorized projects for current user will be returned.") .setSince("4.3") .setResponseExample(Resources.getResource(this.getClass(), "search-example.json")) + .setChangelog( + new Change("7.9", "New field 'paging' in response"), + new Change("7.9", "New field 'key' returning the project key in 'results' response"), + new Change("7.9", "Field 'more' is deprecated in the response")) .setHandler(this); action.createParam(PARAM_GATE_ID) @@ -97,45 +102,42 @@ public class SearchAction implements QualityGatesWsAction { OrganizationDto organization = wsSupport.getOrganization(dbSession, request); QGateWithOrgDto qualityGate = wsSupport.getByOrganizationAndId(dbSession, organization, request.mandatoryParamAsLong(PARAM_GATE_ID)); - Association associations = find(dbSession, - ProjectQgateAssociationQuery.builder() - .qualityGate(qualityGate) - .membership(request.param(PARAM_QUERY) == null ? request.param(SELECTED) : ANY) - .projectSearch(request.param(PARAM_QUERY)) - .pageIndex(request.paramAsInt(PARAM_PAGE)) - .pageSize(request.paramAsInt(PARAM_PAGE_SIZE)) - .build()); - - Qualitygates.SearchResponse.Builder createResponse = Qualitygates.SearchResponse.newBuilder() - .setMore(associations.hasMoreResults()); - - for (ProjectQgateAssociation project : associations.projects()) { + + ProjectQgateAssociationQuery projectQgateAssociationQuery = ProjectQgateAssociationQuery.builder() + .qualityGate(qualityGate) + .membership(request.param(PARAM_QUERY) == null ? request.param(SELECTED) : ANY) + .projectSearch(request.param(PARAM_QUERY)) + .pageIndex(request.paramAsInt(PARAM_PAGE)) + .pageSize(request.paramAsInt(PARAM_PAGE_SIZE)) + .build(); + List<ProjectQgateAssociationDto> projects = dbClient.projectQgateAssociationDao().selectProjects(dbSession, projectQgateAssociationQuery); + List<ProjectQgateAssociationDto> authorizedProjects = keepAuthorizedProjects(dbSession, projects); + Paging paging = forPageIndex(projectQgateAssociationQuery.pageIndex()) + .withPageSize(projectQgateAssociationQuery.pageSize()) + .andTotal(authorizedProjects.size()); + List<ProjectQgateAssociationDto> paginatedProjects = getPaginatedProjects(authorizedProjects, paging); + + Qualitygates.SearchResponse.Builder createResponse = Qualitygates.SearchResponse.newBuilder().setMore(paging.hasNextPage()); + createResponse.getPagingBuilder() + .setPageIndex(paging.pageIndex()) + .setPageSize(paging.pageSize()) + .setTotal(paging.total()) + .build(); + + for (ProjectQgateAssociationDto project : paginatedProjects) { createResponse.addResultsBuilder() - .setId(project.id()) - .setName(project.name()) - .setSelected(project.isMember()); + .setId(project.getId()) + .setName(project.getName()) + .setKey(project.getKey()) + .setSelected(project.getGateId() != null); } writeProtobuf(createResponse.build(), request, response); } } - private SearchAction.Association find(DbSession dbSession, ProjectQgateAssociationQuery query) { - List<ProjectQgateAssociationDto> projects = dbClient.projectQgateAssociationDao().selectProjects(dbSession, query); - List<ProjectQgateAssociationDto> authorizedProjects = keepAuthorizedProjects(dbSession, projects); - - Paging paging = forPageIndex(query.pageIndex()) - .withPageSize(query.pageSize()) - .andTotal(authorizedProjects.size()); - return new SearchAction.Association(toProjectAssociations(getPaginatedProjects(authorizedProjects, paging)), paging.hasNextPage()); - } - private static List<ProjectQgateAssociationDto> getPaginatedProjects(List<ProjectQgateAssociationDto> projects, Paging paging) { - return projects.stream().skip(paging.offset()).limit(paging.pageSize()).collect(MoreCollectors.toList()); - } - - private static List<ProjectQgateAssociation> toProjectAssociations(List<ProjectQgateAssociationDto> dtos) { - return dtos.stream().map(ProjectQgateAssociationDto::toQgateAssociation).collect(MoreCollectors.toList()); + return projects.stream().skip(paging.offset()).limit(paging.pageSize()).collect(toList()); } private List<ProjectQgateAssociationDto> keepAuthorizedProjects(DbSession dbSession, List<ProjectQgateAssociationDto> projects) { @@ -149,22 +151,4 @@ public class SearchAction implements QualityGatesWsAction { Collection<Long> authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(dbSession, projectIds, userSession.getUserId(), UserRole.USER); return projects.stream().filter(project -> authorizedProjectIds.contains(project.getId())).collect(MoreCollectors.toList()); } - - private static class Association { - private List<ProjectQgateAssociation> projects; - private boolean hasMoreResults; - - private Association(List<ProjectQgateAssociation> projects, boolean hasMoreResults) { - this.projects = projects; - this.hasMoreResults = hasMoreResults; - } - - public List<ProjectQgateAssociation> projects() { - return projects; - } - - public boolean hasMoreResults() { - return hasMoreResults; - } - } } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json index 7ad6eed5a24..97332a0480b 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json @@ -1,15 +1,21 @@ { - "more": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2 + }, "results": [ { "id": 1, "name": "Simple Java project analyzed with the SonarQube Runner", + "key": "somple-java", "selected": true }, { "id": 4, "name": "My Project", - "selected": true + "key": "my-project", + "selected": false } ] } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java index 84768e600b8..453ad38190d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java @@ -70,25 +70,6 @@ public class SearchActionTest { private WsActionTester ws = new WsActionTester(underTest); @Test - public void definition() { - WebService.Action action = ws.getDef(); - - assertThat(action).isNotNull(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.isPost()).isFalse(); - assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()) - .extracting(WebService.Param::key, WebService.Param::isRequired) - .containsExactlyInAnyOrder( - tuple("gateId", true), - tuple("query", false), - tuple("organization", false), - tuple("selected", false), - tuple("page", false), - tuple("pageSize", false)); - } - - @Test public void search_projects_of_a_quality_gate_from_an_organization() { OrganizationDto organization = db.organizations().insert(); ComponentDto project = db.components().insertPublicProject(organization); @@ -101,9 +82,8 @@ public class SearchActionTest { .executeProtobuf(SearchResponse.class); assertThat(response.getResultsList()) - .extracting(Result::getId, Result::getName) - .containsExactlyInAnyOrder(tuple(project.getId(), project.name())); - assertThat(response.getMore()).isFalse(); + .extracting(Result::getId, Result::getKey, Result::getName) + .containsExactlyInAnyOrder(tuple(project.getId(), project.getKey(), project.name())); } @Test @@ -120,7 +100,6 @@ public class SearchActionTest { assertThat(response.getResultsList()) .extracting(Result::getId, Result::getName) .containsExactlyInAnyOrder(tuple(project.getId(), project.name())); - assertThat(response.getMore()).isFalse(); } @Test @@ -151,10 +130,10 @@ public class SearchActionTest { .executeProtobuf(SearchResponse.class); assertThat(response.getResultsList()) - .extracting(Result::getName, Result::getSelected) + .extracting(Result::getName, Result::getKey, Result::getSelected) .containsExactlyInAnyOrder( - tuple(associatedProject.name(), true), - tuple(unassociatedProject.name(), false)); + tuple(associatedProject.name(), associatedProject.getKey(), true), + tuple(unassociatedProject.name(), unassociatedProject.getKey(), false)); } @Test @@ -309,25 +288,53 @@ public class SearchActionTest { } @Test - public void more_is_true_when_not_all_project_fit_in_page_size() { + public void test_pagination_on_many_pages() { OrganizationDto organization = db.organizations().insert(); QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); for (int i = 0; i < 20; i++) { ComponentDto project = db.components().insertPublicProject(organization); db.qualityGates().associateProjectToQualityGate(project, qualityGate); } + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + + SearchResponse response = ws.newRequest() + .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_PAGE_SIZE, valueOf(5)) + .setParam(PARAM_PAGE, valueOf(2)) + .executeProtobuf(SearchResponse.class); + assertThat(response) + .extracting(SearchResponse::getMore, + searchResponse -> searchResponse.getPaging().getPageIndex(), + searchResponse -> searchResponse.getPaging().getPageSize(), + searchResponse -> searchResponse.getPaging().getTotal()) + .contains(true, 2, 5, 20); + } + + @Test + public void test_pagination_on_one_page() { + OrganizationDto organization = db.organizations().insert(); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); + for (int i = 0; i < 20; i++) { + ComponentDto project = db.components().insertPublicProject(organization); + db.qualityGates().associateProjectToQualityGate(project, qualityGate); + } userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); SearchResponse response = ws.newRequest() .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) .setParam(PARAM_ORGANIZATION, organization.getKey()) - .setParam(PARAM_PAGE_SIZE, valueOf(10)) + .setParam(PARAM_PAGE_SIZE, valueOf(100)) .setParam(PARAM_PAGE, valueOf(1)) .executeProtobuf(SearchResponse.class); - assertThat(response.getMore()).isTrue(); - assertThat(response.getResultsCount()).isEqualTo(10); + assertThat(response) + .extracting(SearchResponse::getMore, + searchResponse -> searchResponse.getPaging().getPageIndex(), + searchResponse -> searchResponse.getPaging().getPageSize(), + searchResponse -> searchResponse.getPaging().getTotal()) + .contains(false, 1, 100, 20); } @Test @@ -380,4 +387,23 @@ public class SearchActionTest { .executeProtobuf(SearchResponse.class); } + @Test + public void definition() { + WebService.Action action = ws.getDef(); + + assertThat(action).isNotNull(); + assertThat(action.isInternal()).isFalse(); + assertThat(action.isPost()).isFalse(); + assertThat(action.responseExampleAsString()).isNotEmpty(); + assertThat(action.params()) + .extracting(WebService.Param::key, WebService.Param::isRequired) + .containsExactlyInAnyOrder( + tuple("gateId", true), + tuple("query", false), + tuple("organization", false), + tuple("selected", false), + tuple("page", false), + tuple("pageSize", false)); + } + } |