From 6c468901399f7ebfad132e66081c33f04f568735 Mon Sep 17 00:00:00 2001 From: Guillaume Jambet Date: Thu, 7 Dec 2017 16:59:56 +0100 Subject: [PATCH] SONAR-10134 Bringing Organization to Search Action --- .../qualitygate/QgateProjectFinder.java | 23 ++- .../server/qualitygate/ws/SearchAction.java | 69 ++++--- ...xample-search.json => search-example.json} | 0 .../qualitygate/QgateProjectFinderTest.java | 134 +++++++------ .../qualitygate/ws/QualityGatesWsTest.java | 148 --------------- .../qualitygate/ws/SearchActionTest.java | 177 ++++++++++++++++++ .../src/main/protobuf/ws-qualitygates.proto | 12 ++ 7 files changed, 325 insertions(+), 238 deletions(-) rename server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/{example-search.json => search-example.json} (100%) delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QgateProjectFinder.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QgateProjectFinder.java index 4d9e79bd748..4f561bd8d5a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QgateProjectFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QgateProjectFinder.java @@ -27,6 +27,7 @@ import org.sonar.api.web.UserRole; 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.ProjectQgateAssociationDao; import org.sonar.db.qualitygate.ProjectQgateAssociationDto; @@ -52,21 +53,19 @@ public class QgateProjectFinder { this.associationDao = dbClient.projectQgateAssociationDao(); } - public Association find(ProjectQgateAssociationQuery query) { - try (DbSession dbSession = dbClient.openSession(false)) { - getQualityGateId(dbSession, query.gateId()); - List projects = associationDao.selectProjects(dbSession, query); - List authorizedProjects = keepAuthorizedProjects(dbSession, projects); + public Association find(DbSession dbSession, OrganizationDto organization, ProjectQgateAssociationQuery query) { + getQualityGateId(dbSession, organization, query.gateId()); + List projects = associationDao.selectProjects(dbSession, query); + List authorizedProjects = keepAuthorizedProjects(dbSession, projects); - Paging paging = forPageIndex(query.pageIndex()) - .withPageSize(query.pageSize()) - .andTotal(authorizedProjects.size()); - return new Association(toProjectAssociations(getPaginatedProjects(authorizedProjects, paging)), paging.hasNextPage()); - } + Paging paging = forPageIndex(query.pageIndex()) + .withPageSize(query.pageSize()) + .andTotal(authorizedProjects.size()); + return new Association(toProjectAssociations(getPaginatedProjects(authorizedProjects, paging)), paging.hasNextPage()); } - private Long getQualityGateId(DbSession dbSession, String gateId) { - return checkFound(qualitygateDao.selectById(dbSession, Long.valueOf(gateId)), "Quality gate '" + gateId + "' does not exists.").getId(); + private Long getQualityGateId(DbSession dbSession, OrganizationDto organization, String gateId) { + return checkFound(qualitygateDao.selectByOrganizationAndId(dbSession, organization, Long.valueOf(gateId)), "Quality gate '" + gateId + "' does not exists.").getId(); } private static List getPaginatedProjects(List projects, Paging paging) { 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 285d723bec7..67d4e1b905c 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 @@ -23,18 +23,32 @@ import com.google.common.io.Resources; 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.Param; -import org.sonar.api.utils.text.JsonWriter; +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.ProjectQgateAssociationQuery; import org.sonar.server.qualitygate.QgateProjectFinder; +import org.sonarqube.ws.Qualitygates; + +import static org.sonar.api.server.ws.WebService.Param.SELECTED; +import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.ANY; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE_SIZE; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_QUERY; +import static org.sonar.server.ws.WsUtils.writeProtobuf; public class SearchAction implements QualityGatesWsAction { + private final DbClient dbClient; private final QgateProjectFinder projectFinder; + private final QualityGatesWsSupport wsSupport; - public SearchAction(QgateProjectFinder projectFinder) { + public SearchAction(DbClient dbClient, QgateProjectFinder projectFinder, QualityGatesWsSupport wsSupport) { + this.dbClient = dbClient; this.projectFinder = projectFinder; + this.wsSupport = wsSupport; } @Override @@ -43,47 +57,60 @@ public class SearchAction implements QualityGatesWsAction { .setDescription("Search for projects associated (or not) to a quality gate.
" + "Only authorized projects for current user will be returned.") .setSince("4.3") - .setResponseExample(Resources.getResource(this.getClass(), "example-search.json")) + .setResponseExample(Resources.getResource(this.getClass(), "search-example.json")) .setHandler(this); - action.createParam(QualityGatesWsParameters.PARAM_GATE_ID) + action.createParam(PARAM_GATE_ID) .setDescription("Quality Gate ID") .setRequired(true) .setExampleValue("1"); - action.createParam(QualityGatesWsParameters.PARAM_QUERY) + action.createParam(PARAM_QUERY) .setDescription("To search for projects containing this string. If this parameter is set, \"selected\" is set to \"all\".") .setExampleValue("abc"); action.addSelectionModeParam(); - action.createParam(QualityGatesWsParameters.PARAM_PAGE) + action.createParam(PARAM_PAGE) .setDescription("Page number") .setDefaultValue("1") .setExampleValue("2"); - action.createParam(QualityGatesWsParameters.PARAM_PAGE_SIZE) + action.createParam(PARAM_PAGE_SIZE) .setDescription("Page size") .setExampleValue("10"); + + wsSupport.createOrganizationParam(action); } @Override public void handle(Request request, Response response) { - QgateProjectFinder.Association associations = projectFinder.find(ProjectQgateAssociationQuery.builder() - .gateId(request.mandatoryParam(QualityGatesWsParameters.PARAM_GATE_ID)) - .membership(request.param(QualityGatesWsParameters.PARAM_QUERY) == null ? request.param(Param.SELECTED) : ProjectQgateAssociationQuery.ANY) - .projectSearch(request.param(QualityGatesWsParameters.PARAM_QUERY)) - .pageIndex(request.paramAsInt(QualityGatesWsParameters.PARAM_PAGE)) - .pageSize(request.paramAsInt(QualityGatesWsParameters.PARAM_PAGE_SIZE)) - .build()); - try (JsonWriter writer = response.newJsonWriter()) { - writer.beginObject().prop("more", associations.hasMoreResults()); - writer.name("results").beginArray(); + + try (DbSession dbSession = dbClient.openSession(false)) { + + OrganizationDto organization = wsSupport.getOrganization(dbSession, request); + + QgateProjectFinder.Association associations = projectFinder.find(dbSession, organization, + ProjectQgateAssociationQuery.builder() + .gateId(request.mandatoryParam(PARAM_GATE_ID)) + .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()) { - writer.beginObject().prop("id", project.id()).prop("name", project.name()).prop(Param.SELECTED, project.isMember()).endObject(); + createResponse.addResultsBuilder() + .setId(project.id()) + .setName(project.name()) + .setSelected(project.isMember()); } - writer.endArray().endObject().close(); + + writeProtobuf(createResponse.build(), request, response); + } } - } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/example-search.json b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json similarity index 100% rename from server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/example-search.json rename to server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/search-example.json diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QgateProjectFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QgateProjectFinderTest.java index fee90f42eba..2e0e79bafe0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QgateProjectFinderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QgateProjectFinderTest.java @@ -23,19 +23,16 @@ import com.google.common.base.Function; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.System2; -import org.sonar.api.web.UserRole; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.ProjectQgateAssociation; @@ -48,6 +45,9 @@ import org.sonar.server.tester.UserSessionRule; import static com.google.common.collect.FluentIterable.from; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.web.UserRole.USER; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.component.ComponentTesting.newPublicProjectDto; import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.IN; import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.OUT; import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.builder; @@ -67,19 +67,16 @@ public class QgateProjectFinderTest { private DbClient dbClient = dbTester.getDbClient(); private DbSession dbSession = dbTester.getSession(); private ComponentDbTester componentDbTester = new ComponentDbTester(dbTester); - private QualityGateDto qGate; private QgateProjectFinder underTest = new QgateProjectFinder(dbClient, userSession); - @Before - public void setUp() throws Exception { - qGate = new QualityGateDto().setName("Default Quality Gate").setUuid(Uuids.createFast()); - dbClient.qualityGateDao().insert(dbSession, qGate); - dbTester.commit(); - } - @Test public void return_empty_association() { - Association result = underTest.find( + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + + Association result = underTest.find(dbSession, organization, builder() .gateId(Long.toString(qGate.getId())) .build()); @@ -89,12 +86,17 @@ public class QgateProjectFinderTest { @Test public void return_all_projects() { - OrganizationDto org = dbTester.organizations().insert(); - ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); - ComponentDto unassociatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); - associateProjectToQualitGate(associatedProject.getId()); + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + ComponentDto unassociatedProject = newPublicProjectDto(organization); + dbTester.components().insertComponent(unassociatedProject); + ComponentDto associatedProject = newPublicProjectDto(organization); + dbTester.components().insertComponent(associatedProject); + associateProjectToQualitGate(associatedProject, qGate); - Association result = underTest.find( + Association result = underTest.find(dbSession, organization, builder() .gateId(Long.toString(qGate.getId())) .build()); @@ -108,12 +110,18 @@ public class QgateProjectFinderTest { @Test public void return_only_associated_project() { - OrganizationDto org = dbTester.organizations().insert(); - ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); - insertProject(ComponentTesting.newPublicProjectDto(org)); - associateProjectToQualitGate(associatedProject.getId()); + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + ComponentDto project1 = newPublicProjectDto(organization); + dbTester.components().insertComponent(project1); + ComponentDto associatedProject = project1; + ComponentDto project = newPublicProjectDto(organization); + dbTester.components().insertComponent(project); + associateProjectToQualitGate(associatedProject, qGate); - Association result = underTest.find( + Association result = underTest.find(dbSession, organization, builder() .membership(IN) .gateId(Long.toString(qGate.getId())) @@ -126,12 +134,17 @@ public class QgateProjectFinderTest { @Test public void return_only_unassociated_project() { - OrganizationDto org = dbTester.organizations().insert(); - ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); - ComponentDto unassociatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); - associateProjectToQualitGate(associatedProject.getId()); + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + ComponentDto unAssociatedProject = newPublicProjectDto(organization); + dbTester.components().insertComponent(unAssociatedProject); + ComponentDto associatedProject = newPublicProjectDto(organization); + dbTester.components().insertComponent(associatedProject); + associateProjectToQualitGate(associatedProject, qGate); - Association result = underTest.find( + Association result = underTest.find(dbSession, organization, builder() .membership(OUT) .gateId(Long.toString(qGate.getId())) @@ -139,53 +152,65 @@ public class QgateProjectFinderTest { Map projectsById = projectsById(result.projects()); assertThat(projectsById).hasSize(1); - verifyProject(projectsById.get(unassociatedProject.getId()), false, unassociatedProject.name()); + verifyProject(projectsById.get(unAssociatedProject.getId()), false, unAssociatedProject.name()); } @Test public void return_only_authorized_projects() { + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); UserDto user = dbTester.users().insertUser("a_login"); - OrganizationDto organizationDto = dbTester.organizations().insert(); - ComponentDto project1 = componentDbTester.insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto)); - componentDbTester.insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto)); + ComponentDto project = componentDbTester.insertComponent(newPrivateProjectDto(organization)); + componentDbTester.insertComponent(newPrivateProjectDto(organization)); // User can only see project 1 - dbTester.users().insertProjectPermissionOnUser(user, UserRole.USER, project1); + dbTester.users().insertProjectPermissionOnUser(user, USER, project); userSession.logIn(user.getLogin()).setUserId(user.getId()); - Association result = underTest.find( + Association result = underTest.find(dbSession, organization, builder() .gateId(Long.toString(qGate.getId())) .build()); - verifyProjects(result, project1.getId()); + verifyProjects(result, project.getId()); } @Test public void do_not_verify_permissions_if_user_is_root() { - OrganizationDto org = dbTester.organizations().insert(); - ComponentDto project = componentDbTester.insertPrivateProject(org); + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + ComponentDto project = componentDbTester.insertPrivateProject(organization); ProjectQgateAssociationQuery query = builder() .gateId(Long.toString(qGate.getId())) .build(); userSession.logIn().setNonRoot(); - verifyProjects(underTest.find(query)); + verifyProjects(underTest.find(dbSession, organization, query)); userSession.logIn().setRoot(); - verifyProjects(underTest.find(query), project.getId()); + verifyProjects(underTest.find(dbSession, organization, query), project.getId()); } @Test public void test_paging() throws Exception { - OrganizationDto org = dbTester.organizations().insert(); - ComponentDto project1 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 1")); - ComponentDto project2 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 2")); - ComponentDto project3 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 3")); - associateProjectToQualitGate(project1.getId()); + OrganizationDto organization = dbTester.organizations().insert(); + QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, + qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); + dbTester.commit(); + ComponentDto project1 = newPublicProjectDto(organization).setName("Project 1"); + dbTester.components().insertComponent(project1); + associateProjectToQualitGate(project1, qGate); + ComponentDto project2 = newPublicProjectDto(organization).setName("Project 2"); + dbTester.components().insertComponent(project2); + ComponentDto project3 = newPublicProjectDto(organization).setName("Project 3"); + dbTester.components().insertComponent(project3); // Return partial result on first page - verifyPaging(underTest.find( + verifyPaging(underTest.find(dbSession, organization, builder().gateId(Long.toString(qGate.getId())) .pageIndex(1) .pageSize(1) @@ -193,7 +218,7 @@ public class QgateProjectFinderTest { true, project1.getId()); // Return partial result on second page - verifyPaging(underTest.find( + verifyPaging(underTest.find(dbSession, organization, builder().gateId(Long.toString(qGate.getId())) .pageIndex(2) .pageSize(1) @@ -201,7 +226,7 @@ public class QgateProjectFinderTest { true, project2.getId()); // Return partial result on first page - verifyPaging(underTest.find( + verifyPaging(underTest.find(dbSession, organization, builder().gateId(Long.toString(qGate.getId())) .pageIndex(1) .pageSize(2) @@ -209,7 +234,7 @@ public class QgateProjectFinderTest { true, project1.getId(), project2.getId()); // Return all result on first page - verifyPaging(underTest.find( + verifyPaging(underTest.find(dbSession, organization, builder().gateId(Long.toString(qGate.getId())) .pageIndex(1) .pageSize(3) @@ -217,7 +242,7 @@ public class QgateProjectFinderTest { false, project1.getId(), project2.getId(), project3.getId()); // Return no result as page index is off limit - verifyPaging(underTest.find( + verifyPaging(underTest.find(dbSession, organization, builder().gateId(Long.toString(qGate.getId())) .pageIndex(3) .pageSize(3) @@ -228,7 +253,7 @@ public class QgateProjectFinderTest { @Test public void fail_on_unknown_quality_gate() { expectedException.expect(NotFoundException.class); - underTest.find(builder().gateId("123").build()); + underTest.find(dbSession, dbTester.organizations().insert(), builder().gateId("123").build()); } private void verifyProject(ProjectQgateAssociation project, boolean expectedMembership, String expectedName) { @@ -245,19 +270,14 @@ public class QgateProjectFinderTest { assertThat(association.projects()).extracting("id").containsOnly(expectedProjectIds); } - private void associateProjectToQualitGate(long projectId) { + private void associateProjectToQualitGate(ComponentDto component, QualityGateDto qualityGate) { dbClient.propertiesDao().saveProperty( new PropertyDto().setKey(SONAR_QUALITYGATE_PROPERTY) - .setResourceId(projectId) - .setValue(Long.toString(qGate.getId()))); + .setResourceId(component.getId()) + .setValue(Long.toString(qualityGate.getId()))); dbTester.commit(); } - private ComponentDto insertProject(ComponentDto project) { - dbTester.components().insertComponent(project); - return project; - } - private static Map projectsById(List projects) { return from(projects).uniqueIndex(ProjectToId.INSTANCE); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java deleted file mode 100644 index 0a6b0377215..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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.qualitygate.ws; - -import com.google.common.collect.ImmutableList; -import java.util.List; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.server.ws.Change; -import org.sonar.api.server.ws.WebService.Action; -import org.sonar.api.server.ws.WebService.Controller; -import org.sonar.db.qualitygate.ProjectQgateAssociation; -import org.sonar.db.qualitygate.ProjectQgateAssociationQuery; -import org.sonar.server.qualitygate.QgateProjectFinder; -import org.sonar.server.qualitygate.QgateProjectFinder.Association; -import org.sonar.server.qualitygate.QualityGates; -import org.sonar.server.ws.RemovedWebServiceHandler; -import org.sonar.server.ws.WsTester; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -// TODO split testcases in action tests -// TODO restore -@RunWith(MockitoJUnitRunner.class) -@Ignore -public class QualityGatesWsTest { - - @Mock - private QualityGates qGates; - - @Mock - private QgateProjectFinder projectFinder; - - WsTester tester; - - @Before - public void setUp() { - tester = new WsTester(new QualityGatesWs( - new SearchAction(projectFinder), - new CreateAction(null, null, null, null), - new SetAsDefaultAction(qGates))); - } - - @Test - public void define_ws() { - Controller controller = tester.controller("api/qualitygates"); - assertThat(controller).isNotNull(); - assertThat(controller.path()).isEqualTo("api/qualitygates"); - assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(6); - - Action setDefault = controller.action("set_as_default"); - assertThat(setDefault).isNotNull(); - assertThat(setDefault.handler()).isNotNull(); - assertThat(setDefault.since()).isEqualTo("4.3"); - assertThat(setDefault.isPost()).isTrue(); - assertThat(setDefault.param("id")).isNotNull(); - assertThat(setDefault.isInternal()).isFalse(); - - Action unsetDefault = controller.action("unset_default"); - assertThat(unsetDefault).isNotNull(); - assertThat(unsetDefault.handler()).isNotNull(); - assertThat(unsetDefault.since()).isEqualTo("4.3"); - assertThat(unsetDefault.deprecatedSince()).isEqualTo("7.0"); - assertThat(unsetDefault.changelog()) - .extracting(Change::getVersion, Change::getDescription) - .containsOnly( - tuple("7.0", "Unset a quality gate is no more authorized")); - assertThat(unsetDefault.isPost()).isTrue(); - assertThat(unsetDefault.handler()).isEqualTo(RemovedWebServiceHandler.INSTANCE); - assertThat(unsetDefault.responseExample()).isEqualTo(RemovedWebServiceHandler.INSTANCE.getResponseExample()); - assertThat(unsetDefault.isInternal()).isFalse(); - } - - @Test - public void search_with_query() throws Exception { - long gateId = 12345L; - Association assoc = mock(Association.class); - when(assoc.hasMoreResults()).thenReturn(true); - List projects = ImmutableList.of( - new ProjectQgateAssociation().setId(42L).setName("Project One").setMember(false), - new ProjectQgateAssociation().setId(24L).setName("Project Two").setMember(true)); - when(assoc.projects()).thenReturn(projects); - when(projectFinder.find(any(ProjectQgateAssociationQuery.class))).thenReturn(assoc); - - tester.newGetRequest("api/qualitygates", "search") - .setParam("gateId", Long.toString(gateId)) - .setParam("query", "Project") - .execute() - .assertJson("{\"more\":true,\"results\":[" - + "{\"id\":42,\"name\":\"Project One\",\"selected\":false}," - + "{\"id\":24,\"name\":\"Project Two\",\"selected\":true}" - + "]}"); - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ProjectQgateAssociationQuery.class); - verify(projectFinder).find(queryCaptor.capture()); - ProjectQgateAssociationQuery query = queryCaptor.getValue(); - assertThat(query.membership()).isEqualTo(ProjectQgateAssociationQuery.ANY); - } - - @Test - public void search_nominal() throws Exception { - long gateId = 12345L; - Association assoc = mock(Association.class); - when(assoc.hasMoreResults()).thenReturn(true); - List projects = ImmutableList.of( - new ProjectQgateAssociation().setId(24L).setName("Project Two").setMember(true)); - when(assoc.projects()).thenReturn(projects); - when(projectFinder.find(any(ProjectQgateAssociationQuery.class))).thenReturn(assoc); - - tester.newGetRequest("api/qualitygates", "search") - .setParam("gateId", Long.toString(gateId)) - .execute() - .assertJson("{\"more\":true,\"results\":[" - + "{\"id\":24,\"name\":\"Project Two\",\"selected\":true}" - + "]}"); - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ProjectQgateAssociationQuery.class); - verify(projectFinder).find(queryCaptor.capture()); - ProjectQgateAssociationQuery query = queryCaptor.getValue(); - assertThat(query.membership()).isEqualTo(ProjectQgateAssociationQuery.IN); - } -} 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 new file mode 100644 index 00000000000..ebc4997eb21 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SearchActionTest.java @@ -0,0 +1,177 @@ +/* + * 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.qualitygate.ws; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.property.PropertyDto; +import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.organization.TestDefaultOrganizationProvider; +import org.sonar.server.qualitygate.QgateProjectFinder; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Qualitygates.SearchResponse; + +import static java.lang.String.format; +import static java.lang.String.valueOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.sonar.api.utils.System2.INSTANCE; +import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ORGANIZATION; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE_SIZE; + +public class SearchActionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + @Rule + public DbTester db = DbTester.create(INSTANCE); + + private DbClient dbClient = db.getDbClient(); + private DbSession dbSession = db.getSession(); + private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); + + private QgateProjectFinder projectFinder = new QgateProjectFinder(dbClient, userSession); + private QualityGatesWsSupport wsSupport = new QualityGatesWsSupport(dbClient, userSession, defaultOrganizationProvider); + + private SearchAction underTest = new SearchAction(dbClient, projectFinder, wsSupport); + 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); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project, qualityGate); + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + + SearchResponse response = ws.newRequest() + .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) + .setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) + .executeProtobuf(SearchResponse.class); + + assertThat(response).isNotNull(); + assertThat(response.getMore()).isFalse(); + assertThat(response.getResultsCount()).isEqualTo(1); + assertThat(response.getResults(0).getId()).isEqualTo(project.getId()); + assertThat(response.getResults(0).getName().substring(4)).isEqualTo(project.getKey().substring(3)); + } + + @Test + public void search_on_default_organization_when_none_is_provided() { + OrganizationDto defaultOrganization = db.getDefaultOrganization(); + + ComponentDto project = db.components().insertPublicProject(defaultOrganization); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(defaultOrganization); + associateProjectToQualityGate(project, qualityGate); + userSession.addPermission(ADMINISTER_QUALITY_GATES, defaultOrganization); + + SearchResponse response = ws.newRequest() + .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) + .executeProtobuf(SearchResponse.class); + + assertThat(response).isNotNull(); + assertThat(response.getMore()).isFalse(); + assertThat(response.getResultsCount()).isEqualTo(1); + assertThat(response.getResults(0).getId()).isEqualTo(project.getId()); + assertThat(response.getResults(0).getName().substring(4)).isEqualTo(project.getKey().substring(3)); + } + + @Test + public void fail_when_quality_gates_does_not_belong_to_organization() { + OrganizationDto organization = db.organizations().insert(); + OrganizationDto otherOrganization = db.organizations().insert(); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(otherOrganization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Quality gate '%s' does not exists.", qualityGate.getId())); + + ws.newRequest() + .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) + .setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) + .executeProtobuf(SearchResponse.class); + + } + + @Test + public void more_is_true_when_not_all_project_fit_in_page_size() { + OrganizationDto organization = db.organizations().insert(); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); + for (int i = 0; i < 20; i++) { + ComponentDto project1 = db.components().insertPublicProject(); + associateProjectToQualityGate(project1, qualityGate); + } + + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + + SearchResponse response = ws.newRequest() + .setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) + .setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) + .setParam(PARAM_PAGE_SIZE, valueOf(10)) + .setParam(PARAM_PAGE, valueOf(1)) + .executeProtobuf(SearchResponse.class); + + assertThat(response).isNotNull(); + assertThat(response.getMore()).isTrue(); + assertThat(response.getResultsCount()).isEqualTo(10); + } + + private void associateProjectToQualityGate(ComponentDto componentDto, QualityGateDto qualityGateDto) { + dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() + .setKey("sonar.qualitygate") + .setResourceId(componentDto.getId()) + .setValue(valueOf(qualityGateDto.getId()))); + db.commit(); + } +} diff --git a/sonar-ws/src/main/protobuf/ws-qualitygates.proto b/sonar-ws/src/main/protobuf/ws-qualitygates.proto index 08e1c4570dd..c9b63db5776 100644 --- a/sonar-ws/src/main/protobuf/ws-qualitygates.proto +++ b/sonar-ws/src/main/protobuf/ws-qualitygates.proto @@ -136,6 +136,18 @@ message ShowWsResponse { } } +// GET api/qualitygates/search +message SearchResponse { + optional bool more = 1; + repeated Result results = 2; + + message Result { + optional int64 id = 1; + optional string name = 2; + optional bool selected = 3; + } +} + // GET api/qualitygates/list message ListWsResponse { repeated QualityGate qualitygates = 1; -- 2.39.5