From 8ec53d2ec4a53d29bc8406173e7b6051d58a0950 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 5 Dec 2016 17:41:10 +0100 Subject: [PATCH] SONAR-8450 Extract logic to get qgate from project in QualityGateFinder --- .../server/qualitygate/QualityGateFinder.java | 105 ++++++++++++++++++ .../server/qualitygate/QualityGateModule.java | 1 + .../qualitygate/ws/GetByProjectAction.java | 36 ++---- .../qualitygate/QualityGateFinderTest.java | 104 +++++++++++++++++ .../qualitygate/QualityGateModuleTest.java | 2 +- .../ws/GetByProjectActionTest.java | 8 +- .../src/test/java/org/sonar/db/DbTester.java | 7 ++ .../db/qualitygate/QualityGateDbTester.java | 23 ++++ 8 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java new file mode 100644 index 00000000000..1d670ad8e98 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java @@ -0,0 +1,105 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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; + +import java.util.Optional; +import javax.annotation.CheckForNull; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.property.PropertyDto; +import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.server.exceptions.NotFoundException; + +import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY; + +public class QualityGateFinder { + + private final DbClient dbClient; + + public QualityGateFinder(DbClient dbClient) { + this.dbClient = dbClient; + } + + /** + * Return effective quality gate of a project. + * + * It will first try to get the quality gate explicitly defined on a project, if none it will try to return default quality gate. + * As it's possible to have no default quality gate, this method can return {@link Optional#empty()} + */ + public Optional getQualityGate(DbSession dbSession, long componentId) { + Optional qualityGateId = dbClient.projectQgateAssociationDao().selectQGateIdByComponentId(dbSession, componentId); + if (qualityGateId.isPresent()) { + return Optional.of(new QualityGateData(selectOrFailById(dbSession, qualityGateId.get()), false)); + } else { + QualityGateDto defaultQualityGate = getDefault(dbSession); + if (defaultQualityGate == null) { + return Optional.empty(); + } + return Optional.of(new QualityGateData(defaultQualityGate, true)); + } + } + + @CheckForNull + private QualityGateDto getDefault(DbSession dbSession) { + Long defaultId = getDefaultId(dbSession); + if (defaultId == null) { + return null; + } + return selectOrFailById(dbSession, defaultId); + } + + private QualityGateDto selectOrFailById(DbSession dbSession, long qualityGateId) { + QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectById(dbSession, qualityGateId); + if (qualityGateDto == null) { + throw new NotFoundException(String.format("No quality gate has been found for id %s", qualityGateId)); + } + return qualityGateDto; + } + + @CheckForNull + private Long getDefaultId(DbSession dbSession) { + PropertyDto defaultQgate = dbClient.propertiesDao().selectGlobalProperty(dbSession, SONAR_QUALITYGATE_PROPERTY); + if (defaultQgate == null || isBlank(defaultQgate.getValue())) { + // For the moment, it's possible to have no default quality gate, but it will change with SONAR-8507 + return null; + } + return Long.valueOf(defaultQgate.getValue()); + } + + public static class QualityGateData { + private final QualityGateDto qualityGate; + private final boolean isDefault; + + private QualityGateData(QualityGateDto qualityGate, boolean isDefault) { + this.qualityGate = qualityGate; + this.isDefault = isDefault; + } + + public QualityGateDto getQualityGate() { + return qualityGate; + } + + public boolean isDefault() { + return isDefault; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java index c2a225eec0e..896eaa64c69 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java @@ -48,6 +48,7 @@ public class QualityGateModule extends Module { QualityGateUpdater.class, QualityGateConditionsUpdater.class, QgateProjectFinder.class, + QualityGateFinder.class, // WS QualityGatesWs.class, ListAction.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java index c51422284ad..977e9120e9c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java @@ -31,7 +31,8 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder.ParamNames; -import org.sonar.server.qualitygate.QualityGates; +import org.sonar.server.qualitygate.QualityGateFinder; +import org.sonar.server.qualitygate.QualityGateFinder.QualityGateData; import org.sonar.server.user.UserSession; import org.sonarqube.ws.WsQualityGates.GetByProjectWsResponse; @@ -47,13 +48,13 @@ public class GetByProjectAction implements QualityGatesWsAction { private final UserSession userSession; private final DbClient dbClient; private final ComponentFinder componentFinder; - private final QualityGates qualityGates; + private final QualityGateFinder qualityGateFinder; - public GetByProjectAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, QualityGates qualityGates) { + public GetByProjectAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, QualityGateFinder qualityGateFinder) { this.userSession = userSession; this.dbClient = dbClient; this.componentFinder = componentFinder; - this.qualityGates = qualityGates; + this.qualityGateFinder = qualityGateFinder; } @Override @@ -80,7 +81,7 @@ public class GetByProjectAction implements QualityGatesWsAction { DbSession dbSession = dbClient.openSession(false); try { ComponentDto project = getProject(dbSession, request.param(PARAM_PROJECT_ID), request.param(PARAM_PROJECT_KEY)); - QualityGateData data = getQualityGate(dbSession, project.getId()); + Optional data = qualityGateFinder.getQualityGate(dbSession, project.getId()); writeProtobuf(buildResponse(data), request, response); } finally { @@ -99,37 +100,20 @@ public class GetByProjectAction implements QualityGatesWsAction { return project; } - private static GetByProjectWsResponse buildResponse(QualityGateData data) { - if (!data.qualityGate.isPresent()) { + private static GetByProjectWsResponse buildResponse(Optional data) { + if (!data.isPresent()) { return GetByProjectWsResponse.getDefaultInstance(); } - QualityGateDto qualityGate = data.qualityGate.get(); + QualityGateDto qualityGate = data.get().getQualityGate(); GetByProjectWsResponse.Builder response = GetByProjectWsResponse.newBuilder(); response.getQualityGateBuilder() .setId(String.valueOf(qualityGate.getId())) .setName(qualityGate.getName()) - .setDefault(data.isDefault); + .setDefault(data.get().isDefault()); return response.build(); } - private QualityGateData getQualityGate(DbSession dbSession, long componentId) { - Optional qualityGateId = dbClient.projectQgateAssociationDao().selectQGateIdByComponentId(dbSession, componentId); - - return qualityGateId.isPresent() - ? new QualityGateData(Optional.ofNullable(dbClient.qualityGateDao().selectById(dbSession, qualityGateId.get())), false) - : new QualityGateData(Optional.ofNullable(qualityGates.getDefault()), true); - } - - private static class QualityGateData { - private final Optional qualityGate; - private final boolean isDefault; - - private QualityGateData(Optional qualityGate, boolean isDefault) { - this.qualityGate = qualityGate; - this.isDefault = isDefault; - } - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java new file mode 100644 index 00000000000..9826a19d555 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java @@ -0,0 +1,104 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.server.exceptions.NotFoundException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTesting.newProjectDto; + +public class QualityGateFinderTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + private DbSession dbSession = dbTester.getSession(); + + private QualityGateFinder underTest = new QualityGateFinder(dbTester.getDbClient()); + + @Test + public void return_default_quality_gate_for_project() { + ComponentDto project = dbTester.components().insertComponent(newProjectDto()); + QualityGateDto dbQualityGate = dbTester.qualityGates().createDefaultQualityGate("Sonar way"); + + Optional result = underTest.getQualityGate(dbSession, project.getId()); + + assertThat(result).isPresent(); + assertThat(result.get().getQualityGate().getId()).isEqualTo(dbQualityGate.getId()); + assertThat(result.get().isDefault()).isTrue(); + } + + @Test + public void return_project_quality_gate_over_default() { + ComponentDto project = dbTester.components().insertComponent(newProjectDto()); + dbTester.qualityGates().createDefaultQualityGate("Sonar way"); + QualityGateDto dbQualityGate = dbTester.qualityGates().insertQualityGate("My team QG"); + dbTester.qualityGates().associateProjectToQualityGate(project, dbQualityGate); + + Optional result = underTest.getQualityGate(dbSession, project.getId()); + + assertThat(result).isPresent(); + assertThat(result.get().getQualityGate().getId()).isEqualTo(dbQualityGate.getId()); + assertThat(result.get().isDefault()).isFalse(); + } + + @Test + public void return_nothing_when_no_default_qgate_and_no_qgate_defined_for_project() { + ComponentDto project = dbTester.components().insertComponent(newProjectDto()); + + Optional result = underTest.getQualityGate(dbSession, project.getId()); + + assertThat(result).isNotPresent(); + } + + @Test + public void fail_when_default_qgate_defined_in_properties_does_not_exists() throws Exception { + ComponentDto project = dbTester.components().insertComponent(newProjectDto()); + QualityGateDto dbQualityGate = dbTester.qualityGates().createDefaultQualityGate("Sonar way"); + dbTester.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); + + expectedException.expect(NotFoundException.class); + underTest.getQualityGate(dbSession, project.getId()); + } + + @Test + public void fail_when_project_qgate_defined_in_properties_does_not_exists() throws Exception { + ComponentDto project = dbTester.components().insertComponent(newProjectDto()); + QualityGateDto dbQualityGate = dbTester.qualityGates().insertQualityGate("My team QG"); + dbTester.qualityGates().associateProjectToQualityGate(project, dbQualityGate); + dbTester.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); + + expectedException.expect(NotFoundException.class); + underTest.getQualityGate(dbSession, project.getId()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java index bb8ad41e8b7..6d1b2c1b592 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java @@ -30,6 +30,6 @@ public class QualityGateModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new QualityGateModule().configure(container); - assertThat(container.size()).isEqualTo(22 + 2); + assertThat(container.size()).isEqualTo(23 + 2); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java index d07e2cec6f9..57cae11cdc5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java @@ -26,7 +26,6 @@ import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.measures.MetricFinder; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; import org.sonar.core.permission.GlobalPermissions; @@ -40,9 +39,8 @@ import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.qualitygate.QualityGates; +import org.sonar.server.qualitygate.QualityGateFinder; import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.user.UserSession; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.MediaTypes; @@ -51,7 +49,6 @@ import org.sonarqube.ws.WsQualityGates.GetByProjectWsResponse; import static com.google.common.base.Throwables.propagate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; import static org.sonar.db.component.ComponentTesting.newProjectDto; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID; @@ -69,8 +66,7 @@ public class GetByProjectActionTest { DbSession dbSession = db.getSession(); private WsActionTester ws = new WsActionTester( - new GetByProjectAction(userSession, dbClient, new ComponentFinder(dbClient), - new QualityGates(dbClient, mock(MetricFinder.class), mock(UserSession.class)))); + new GetByProjectAction(userSession, dbClient, new ComponentFinder(dbClient), new QualityGateFinder(dbClient))); @Test public void json_example() { diff --git a/sonar-db/src/test/java/org/sonar/db/DbTester.java b/sonar-db/src/test/java/org/sonar/db/DbTester.java index 2f0e95ec461..e3fde304403 100644 --- a/sonar-db/src/test/java/org/sonar/db/DbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/DbTester.java @@ -69,6 +69,7 @@ import org.sonar.db.organization.OrganizationDbTester; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.permission.template.PermissionTemplateDbTester; +import org.sonar.db.qualitygate.QualityGateDbTester; import org.sonar.db.user.RootFlagAssertions; import org.sonar.db.user.UserDbTester; @@ -100,6 +101,7 @@ public class DbTester extends ExternalResource { private final ComponentDbTester componentTester; private final OrganizationDbTester organizationTester; private final PermissionTemplateDbTester permissionTemplateTester; + private final QualityGateDbTester qualityGateDbTester; private final RootFlagAssertions rootFlagAssertions; private DbTester(System2 system2, @Nullable String schemaPath) { @@ -110,6 +112,7 @@ public class DbTester extends ExternalResource { this.componentTester = new ComponentDbTester(this); this.organizationTester = new OrganizationDbTester(this); this.permissionTemplateTester = new PermissionTemplateDbTester(this); + this.qualityGateDbTester = new QualityGateDbTester(this); this.rootFlagAssertions = new RootFlagAssertions(this); } @@ -182,6 +185,10 @@ public class DbTester extends ExternalResource { return permissionTemplateTester; } + public QualityGateDbTester qualityGates() { + return qualityGateDbTester; + } + public RootFlagAssertions rootFlag() { return rootFlagAssertions; } diff --git a/sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java b/sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java index 5f61358b9a7..d088ec56474 100644 --- a/sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java @@ -22,6 +22,8 @@ package org.sonar.db.qualitygate; 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.property.PropertyDto; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; @@ -46,4 +48,25 @@ public class QualityGateDbTester { db.commit(); return updatedUser; } + + public void associateProjectToQualityGate(ComponentDto component, QualityGateDto qualityGate) { + dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() + .setKey("sonar.qualitygate") + .setResourceId(component.getId()) + .setValue(String.valueOf(qualityGate.getId()))); + db.commit(); + } + + public QualityGateDto createDefaultQualityGate(String qualityGateName) { + QualityGateDto defaultQGate = insertQualityGate(qualityGateName); + setDefaultQualityGate(defaultQGate); + return defaultQGate; + } + + public void setDefaultQualityGate(QualityGateDto qualityGate) { + dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() + .setKey("sonar.qualitygate") + .setValue(String.valueOf(qualityGate.getId()))); + db.commit(); + } } -- 2.39.5