diff options
21 files changed, 427 insertions, 44 deletions
diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index b4553e23eaa..af7c91f6c75 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -313,6 +313,14 @@ CREATE TABLE "ORG_QUALITY_GATES" ( ); CREATE UNIQUE INDEX "UNIQ_ORG_QUALITY_GATES" ON "ORG_QUALITY_GATES" ("ORGANIZATION_UUID","QUALITY_GATE_UUID"); +CREATE TABLE "PROJECT_QGATES" ( +"PROJECT_UUID" VARCHAR(40) NOT NULL, +"QUALITY_GATE_UUID" VARCHAR(40) NOT NULL, + +CONSTRAINT "PK_PROJECT_QGATES" PRIMARY KEY ("PROJECT_UUID") +); +CREATE UNIQUE INDEX "UNIQ_PROJECT_QGATES" ON "PROJECT_QGATES" ("PROJECT_UUID", "QUALITY_GATE_UUID"); + CREATE TABLE "PROPERTIES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "PROP_KEY" VARCHAR(512) NOT NULL, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDao.java index 1cd23ae5d46..9e9152935fe 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationDao.java @@ -40,8 +40,32 @@ public class ProjectQgateAssociationDao implements Dao { return id == null ? Optional.empty() : Optional.of(Long.valueOf(id)); } + /** + * @return quality gate uuid if a specific Quality Gate has been defined for the given component uuid. <br> + * Returns <code>{@link Optional#empty()}</code> otherwise (ex: default quality gate applies) + */ + public Optional<String> selectQGateUuidByComponentUuid(DbSession dbSession, String componentUuid) { + String uuid = mapper(dbSession).selectQGateUuidByComponentUuid(componentUuid); + return Optional.ofNullable(uuid); + } + private static ProjectQgateAssociationMapper mapper(DbSession session) { return session.getMapper(ProjectQgateAssociationMapper.class); } + public void deleteByProjectUuid(DbSession dbSession, String projectUuid) { + mapper(dbSession).deleteByProjectUuid(projectUuid); + } + + public void deleteByQGateUuid(DbSession dbSession, String qGateUuid) { + mapper(dbSession).deleteByQGateUuid(qGateUuid); + } + + public void insertProjectQGateAssociation(DbSession dbSession, String projectUuid, String qGateUuid) { + mapper(dbSession).insertProjectQGateAssociation(projectUuid, qGateUuid); + } + + public void updateProjectQGateAssociation(DbSession dbSession, String projectUuid, String qGateUuid) { + mapper(dbSession).updateProjectQGateAssociation(projectUuid, qGateUuid); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.java index d1f8ab689f5..4d282c6a091 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/ProjectQgateAssociationMapper.java @@ -29,4 +29,16 @@ public interface ProjectQgateAssociationMapper { @CheckForNull String selectQGateIdByComponentId(long componentId); + + @CheckForNull + String selectQGateUuidByComponentUuid(String componentUuid); + + void deleteByProjectUuid(String projectUuid); + + void insertProjectQGateAssociation(@Param("projectUuid") String projectUuid, @Param("qGateUuid") String qGateUuid); + + void deleteByQGateUuid(String qGateUuid); + + void updateProjectQGateAssociation(@Param("projectUuid") String projectUuid, @Param("qGateUuid") String qGateUuid); + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateDao.java index b73dd5ceb96..cc9e7a2b415 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateDao.java @@ -101,4 +101,8 @@ public class QualityGateDao implements Dao { private static QualityGateMapper mapper(DbSession session) { return session.getMapper(QualityGateMapper.class); } + + public QualityGateDto selectByProjectUuid(DbSession dbSession, String uuid) { + return mapper(dbSession).selectByProjectUuid(uuid); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateMapper.java index bdcc9c121f4..490f3bbbadc 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateMapper.java @@ -56,4 +56,6 @@ public interface QualityGateMapper { void update(QualityGateDto qGate); void ensureOneBuiltInQualityGate(String builtInQualityName); + + QualityGateDto selectByProjectUuid(@Param("projectUuid") String projectUuid); } 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 5d9239182ed..ac1a2960ac8 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 @@ -36,4 +36,46 @@ </where> </select> + <select id="selectQGateUuidByComponentUuid" parameterType="String" resultType="string"> + SELECT quality_gate_uuid + FROM project_qgates + <where> + AND project_uuid=#{componentUuid} + </where> + </select> + + <delete id="deleteByProjectUuid" parameterType="String"> + DELETE + FROM project_qgates + WHERE + project_uuid=#{uuid,jdbcType=VARCHAR} + </delete> + + <delete id="deleteByQGateUuid" parameterType="String"> + DELETE + FROM project_qgates + WHERE + quality_gate_uuid=#{uuid,jdbcType=VARCHAR} + </delete> + + <insert id="insertProjectQGateAssociation" parameterType="map"> + INSERT into project_qgates + ( + project_uuid, + quality_gate_uuid + ) + VALUES ( + #{projectUuid,jdbcType=VARCHAR}, + #{qGateUuid,jdbcType=VARCHAR} + ) + </insert> + + <update id="updateProjectQGateAssociation" parameterType="map"> + UPDATE project_qgates + SET + quality_gate_uuid=#{qGateUuid,jdbcType=VARCHAR} + WHERE + project_uuid = #{projectUuid,jdbcType=VARCHAR} + </update> + </mapper> diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateMapper.xml index f7ea50278a6..4d46494c2ff 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateMapper.xml @@ -76,6 +76,15 @@ qg.id = #{id, jdbcType=BIGINT} </select> + <select id="selectByProjectUuid" parameterType="Map" resultType="org.sonar.db.qualitygate.QualityGateDto"> + SELECT + <include refid="gateColumns"/> + FROM + quality_gates qg + INNER JOIN + project_qgates pqg ON pqg.quality_gate_uuid = qg.uuid AND pqg.project_uuid = #{projectUuid, jdbcType=VARCHAR} + </select> + <select id="selectById" parameterType="long" resultType="QualityGate"> select <include refid="gateColumns"/> 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 2f0a99162fa..77dae851284 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 @@ -77,17 +77,17 @@ public class ProjectQgateAssociationDaoTest { .qualityGate(qualityGate) .membership(ProjectQgateAssociationQuery.IN) .build())) - .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) - .containsExactlyInAnyOrder( - tuple(project1.getId(), project1.name(), qualityGate.getId().toString()), - tuple(project2.getId(), project2.name(), qualityGate.getId().toString())); + .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) + .containsExactlyInAnyOrder( + tuple(project1.getId(), project1.name(), qualityGate.getId().toString()), + tuple(project2.getId(), project2.name(), qualityGate.getId().toString())); assertThat(underTest.selectProjects(dbSession, ProjectQgateAssociationQuery.builder() .qualityGate(qualityGate) .membership(ProjectQgateAssociationQuery.OUT) .build())) - .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) - .containsExactlyInAnyOrder(tuple(project3.getId(), project3.name(), null)); + .extracting(ProjectQgateAssociationDto::getId, ProjectQgateAssociationDto::getName, ProjectQgateAssociationDto::getGateId) + .containsExactlyInAnyOrder(tuple(project3.getId(), project3.name(), null)); } @Test @@ -104,15 +104,15 @@ public class ProjectQgateAssociationDaoTest { .qualityGate(qualityGate) .projectSearch("one") .build())) - .extracting(ProjectQgateAssociationDto::getId) - .containsExactlyInAnyOrder(project1.getId()); + .extracting(ProjectQgateAssociationDto::getId) + .containsExactlyInAnyOrder(project1.getId()); assertThat(underTest.selectProjects(dbSession, ProjectQgateAssociationQuery.builder() .qualityGate(qualityGate) .projectSearch("project") .build())) - .extracting(ProjectQgateAssociationDto::getId) - .containsExactlyInAnyOrder(project1.getId(), project2.getId(), project3.getId()); + .extracting(ProjectQgateAssociationDto::getId) + .containsExactlyInAnyOrder(project1.getId(), project2.getId(), project3.getId()); } @Test @@ -126,8 +126,8 @@ public class ProjectQgateAssociationDaoTest { assertThat(underTest.selectProjects(dbSession, ProjectQgateAssociationQuery.builder() .qualityGate(qualityGate) .build())) - .extracting(ProjectQgateAssociationDto::getId) - .containsExactly(project1.getId(), project3.getId(), project2.getId()); + .extracting(ProjectQgateAssociationDto::getId) + .containsExactly(project1.getId(), project3.getId(), project2.getId()); } @Test @@ -174,4 +174,63 @@ public class ProjectQgateAssociationDaoTest { assertThat(result).contains(qualityGate1.getId()); } + @Test + public void select_qgate_uuid_by_component_uuid() { + OrganizationDto organization = db.organizations().insert(); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + db.qualityGates().associateProjectToQualityGate(project, qualityGate); + + Optional<String> qGateUuid = underTest.selectQGateUuidByComponentUuid(dbSession, project.uuid()); + + assertThat(qGateUuid).contains(qualityGate.getUuid()); + } + + + @Test + public void delete_by_project_uuid() { + OrganizationDto organization = db.organizations().insert(); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + db.qualityGates().associateProjectToQualityGate(project, qualityGate); + + underTest.deleteByProjectUuid(dbSession, project.uuid()); + + Optional<String> deletedQualityGate = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + + assertThat(deletedQualityGate).isEmpty(); + } + + @Test + public void delete_by_qgate_uuid() { + OrganizationDto organization = db.organizations().insert(); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + db.qualityGates().associateProjectToQualityGate(project, qualityGate); + + underTest.deleteByQGateUuid(dbSession, qualityGate.getUuid()); + + Optional<String> deletedQualityGate = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + + assertThat(deletedQualityGate).isEmpty(); + } + + @Test + public void update_project_qgate_association() { + OrganizationDto organization = db.organizations().insert(); + QGateWithOrgDto firstQualityGate = db.qualityGates().insertQualityGate(organization); + QGateWithOrgDto secondQualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + db.qualityGates().associateProjectToQualityGate(project, firstQualityGate); + + underTest.updateProjectQGateAssociation(dbSession, project.uuid(), secondQualityGate.getUuid()); + + Optional<String> updatedQualityGateUuid = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + + assertThat(updatedQualityGateUuid).contains(secondQualityGate.getUuid()); + } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDaoTest.java index 63e588e2249..824390141e5 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDaoTest.java @@ -27,6 +27,7 @@ import org.sonar.api.utils.System2; import org.sonar.core.util.Uuids; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import static java.lang.String.format; @@ -111,6 +112,19 @@ public class QualityGateDaoTest { } @Test + public void select_by_uuid() { + QGateWithOrgDto dto = qualityGateDbTester.insertQualityGate(db.getDefaultOrganization(), g -> g.setName("QG Name").setBuiltIn(false)); + QualityGateDto qualityGateToAssociate = underTest.selectById(dbSession, dto.getId()); + ComponentDto project = db.components().insertPrivateProject(); + + qualityGateDbTester.associateProjectToQualityGate(project, qualityGateToAssociate); + + QualityGateDto qualityGateFromSelect = underTest.selectByProjectUuid(dbSession, project.uuid()); + + assertThat(qualityGateFromSelect.getUuid()).isEqualTo(qualityGateToAssociate.getUuid()); + } + + @Test public void select_by_organization_and_uuid() { OrganizationDto organization = db.organizations().insert(); QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java index 872e495e950..01727b4c215 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java @@ -21,6 +21,7 @@ package org.sonar.db.qualitygate; import java.util.Arrays; import java.util.Date; +import java.util.Optional; import java.util.function.Consumer; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; @@ -74,6 +75,9 @@ public class QualityGateDbTester { .setKey("sonar.qualitygate") .setResourceId(component.getId()) .setValue(String.valueOf(qualityGate.getId()))); + + dbClient.projectQgateAssociationDao().insertProjectQGateAssociation(dbSession, component.uuid(), qualityGate.getUuid()); + db.commit(); } @@ -105,4 +109,8 @@ public class QualityGateDbTester { db.commit(); return condition; } + + public Optional<String> selectQGateUuidByComponentUuid(String uuid) { + return dbClient.projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, uuid); + } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTable.java new file mode 100644 index 00000000000..f8a7615e062 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTable.java @@ -0,0 +1,79 @@ +/* + * 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.server.platform.db.migration.version.v80; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.SupportsBlueGreen; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +@SupportsBlueGreen +public class CreateProjectQualityGatesTable extends DdlChange { + + private static final String TABLE_NAME = "project_qgates"; + + private static final VarcharColumnDef PROJECT_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("project_uuid") + .setIsNullable(false) + .setLimit(UUID_SIZE) + .build(); + private static final VarcharColumnDef QUALITY_GATE_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("quality_gate_uuid") + .setIsNullable(false) + .setLimit(UUID_SIZE) + .build(); + + public CreateProjectQualityGatesTable(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + if (tableExists()) { + return; + } + context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME) + .addPkColumn(PROJECT_UUID_COLUMN) + .addColumn(QUALITY_GATE_UUID_COLUMN) + .build()); + + context.execute(new CreateIndexBuilder() + .setTable(TABLE_NAME) + .setName("uniq_project_qgates") + .setUnique(true) + .addColumn(PROJECT_UUID_COLUMN) + .addColumn(QUALITY_GATE_UUID_COLUMN) + .build()); + } + + private boolean tableExists() throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + return DatabaseUtils.tableExists(TABLE_NAME, connection); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java index a2f5956c7bb..f3f0a818d1e 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java @@ -26,6 +26,7 @@ public class DbVersion80 implements DbVersion { @Override public void addSteps(MigrationStepRegistry registry) { registry - .add(3000, "Set Organizations#guarded column nullable", MakeOrganizationsGuardedNullable.class); + .add(3000, "Set Organizations#guarded column nullable", MakeOrganizationsGuardedNullable.class) + .add(3001, "Create ProjectQualityGates table", CreateProjectQualityGatesTable.class); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTableTest.java new file mode 100644 index 00000000000..72b507502b0 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/CreateProjectQualityGatesTableTest.java @@ -0,0 +1,55 @@ +/* + * 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.server.platform.db.migration.version.v80; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +import static java.sql.Types.VARCHAR; + +public class CreateProjectQualityGatesTableTest { + private static final String TABLE_NAME = "project_qgates"; + + @Rule + public CoreDbTester dbTester = CoreDbTester.createEmpty(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private CreateProjectQualityGatesTable underTest = new CreateProjectQualityGatesTable(dbTester.database()); + + @Test + public void table_has_been_created() throws SQLException { + underTest.execute(); + + dbTester.assertTableExists(TABLE_NAME); + dbTester.assertPrimaryKey(TABLE_NAME, "pk_project_qgates", "project_uuid"); + dbTester.assertUniqueIndex(TABLE_NAME, "uniq_project_qgates", "project_uuid", "quality_gate_uuid"); + + dbTester.assertColumnDefinition(TABLE_NAME, "project_uuid", VARCHAR, 40, false); + dbTester.assertColumnDefinition(TABLE_NAME, "quality_gate_uuid", VARCHAR, 40, false); + + //script should not fail if executed twice + underTest.execute(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java index 9e64b803eea..3552b825d63 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java @@ -21,7 +21,6 @@ package org.sonar.server.platform.db.migration.version.v80; import org.junit.Test; import org.sonar.server.platform.db.migration.version.DbVersion; -import org.sonar.server.platform.db.migration.version.v79.DbVersion79; import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationCount; import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber; @@ -36,7 +35,7 @@ public class DbVersion80Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 1); + verifyMigrationCount(underTest, 2); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java index 8fa1bc68464..93658d2aa27 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java @@ -22,14 +22,11 @@ package org.sonar.server.qualitygate; import org.sonar.core.util.UuidFactory; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; import static com.google.common.base.Preconditions.checkArgument; -import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY; import static org.sonar.server.util.Validation.IS_ALREADY_USED_MESSAGE; public class QualityGateUpdater { @@ -78,17 +75,6 @@ public class QualityGateUpdater { dbClient.qualityGateDao().update(qualityGateDto, dbSession); } - public void dissociateProject(DbSession dbSession, ComponentDto project) { - dbClient.propertiesDao().deleteProjectProperty(SONAR_QUALITYGATE_PROPERTY, project.getId(), dbSession); - } - - public void associateProject(DbSession dbSession, ComponentDto project, QualityGateDto qualityGate) { - dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() - .setKey(SONAR_QUALITYGATE_PROPERTY) - .setResourceId(project.getId()) - .setValue(String.valueOf(qualityGate.getId()))); - } - private void checkQualityGateDoesNotAlreadyExist(DbSession dbSession, OrganizationDto organizationDto, String name) { QualityGateDto existingQgate = dbClient.qualityGateDao().selectByOrganizationAndName(dbSession, organizationDto, name); checkArgument(existingQgate == null, IS_ALREADY_USED_MESSAGE, "Name"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java index d5a5392df6b..8bcf5a39e78 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java @@ -89,6 +89,7 @@ public class DeselectAction implements QualityGatesWsAction { private void dissociateProject(DbSession dbSession, OrganizationDto organization, ComponentDto project) { wsSupport.checkCanAdminProject(organization, project); dbClient.propertiesDao().deleteProjectProperty(SONAR_QUALITYGATE_PROPERTY, project.getId(), dbSession); + dbClient.projectQgateAssociationDao().deleteByProjectUuid(dbSession, project.uuid()); dbSession.commit(); } @@ -106,7 +107,7 @@ public class DeselectAction implements QualityGatesWsAction { try { long dbId = Long.parseLong(projectId); - return Optional.ofNullable(dbClient.componentDao().selectById(dbSession, dbId).orElse(null)); + return dbClient.componentDao().selectById(dbSession, dbId); } catch (NumberFormatException e) { return Optional.empty(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DestroyAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DestroyAction.java index e9279870565..ce098eaa65f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DestroyAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DestroyAction.java @@ -72,6 +72,7 @@ public class DestroyAction implements QualityGatesWsAction { wsSupport.checkCanEdit(qualityGate); dbClient.propertiesDao().deleteByKeyAndValue(dbSession, SONAR_QUALITYGATE_PROPERTY, String.valueOf(qualityGate.getId())); + dbClient.projectQgateAssociationDao().deleteByQGateUuid(dbSession, qualityGate.getUuid()); dbClient.qualityGateDao().delete(qualityGate, dbSession); dbSession.commit(); response.noContent(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java index 71c38ad77a2..e8fc3e5eb64 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java @@ -31,6 +31,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.QGateWithOrgDto; +import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder.ParamNames; @@ -99,6 +100,16 @@ public class SelectAction implements QualityGatesWsAction { .setResourceId(project.getId()) .setValue(String.valueOf(qualityGate.getId()))); + QualityGateDto currentQualityGate = dbClient.qualityGateDao().selectByProjectUuid(dbSession, project.uuid()); + if (currentQualityGate == null) { + // project uses the default profile + dbClient.projectQgateAssociationDao() + .insertProjectQGateAssociation(dbSession, project.uuid(), qualityGate.getUuid()); + } else if (!qualityGate.getUuid().equals(currentQualityGate.getUuid())) { + dbClient.projectQgateAssociationDao() + .updateProjectQGateAssociation(dbSession, project.uuid(), qualityGate.getUuid()); + } + dbSession.commit(); } response.noContent(); @@ -118,7 +129,7 @@ public class SelectAction implements QualityGatesWsAction { try { long dbId = Long.parseLong(projectId); - return Optional.ofNullable(dbClient.componentDao().selectById(dbSession, dbId).orElse(null)); + return dbClient.componentDao().selectById(dbSession, dbId); } catch (NumberFormatException e) { return Optional.empty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java index e3ced0d69dc..8ef3fedbd48 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.qualitygate.ws; +import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -77,7 +78,7 @@ public class DeselectActionTest { .setParam("organization", organization.getKey()) .execute(); - assertDeselected(project.getId()); + assertDeselected(project); } @Test @@ -93,7 +94,7 @@ public class DeselectActionTest { .setParam("organization", organization.getKey()) .execute(); - assertDeselected(project.getId()); + assertDeselected(project); } @Test @@ -109,7 +110,7 @@ public class DeselectActionTest { .setParam("organization", organization.getKey()) .execute(); - assertDeselected(project.getId()); + assertDeselected(project); } @Test @@ -125,7 +126,7 @@ public class DeselectActionTest { .setParam("organization", organization.getKey()) .execute(); - assertDeselected(project.getId()); + assertDeselected(project); } @Test @@ -134,7 +135,6 @@ public class DeselectActionTest { userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); ComponentDto project = db.components().insertPrivateProject(organization); - String gateId = valueOf(qualityGate.getId()); associateProjectToQualityGate(project, qualityGate); // Another project ComponentDto anotherProject = db.components().insertPrivateProject(organization); @@ -145,8 +145,8 @@ public class DeselectActionTest { .setParam("organization", organization.getKey()) .execute(); - assertDeselected(project.getId()); - assertSelected(gateId, anotherProject.getId()); + assertDeselected(project); + assertSelected(qualityGate, anotherProject); } @Test @@ -161,7 +161,7 @@ public class DeselectActionTest { .setParam("projectKey", project.getKey()) .execute(); - assertDeselected(project.getId()); + assertDeselected(project); } @Test @@ -304,14 +304,26 @@ public class DeselectActionTest { .setResourceId(project.getId()) .setValue(qualityGate.getId().toString()) .setKey(SONAR_QUALITYGATE_PROPERTY)); + db.qualityGates().associateProjectToQualityGate(project, qualityGate); db.commit(); } - private void assertDeselected(long projectId) { - assertThat(dbClient.propertiesDao().selectProjectProperty(projectId, SONAR_QUALITYGATE_PROPERTY)).isNull(); + private void assertDeselected(ComponentDto project) { + Optional<String> qGateUuid = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + assertThat(qGateUuid) + .isNotNull() + .isEmpty(); + + assertThat(dbClient.propertiesDao().selectProjectProperty(project.getId(), SONAR_QUALITYGATE_PROPERTY)).isNull(); } - private void assertSelected(String qGateId, long projectId) { - assertThat(dbClient.propertiesDao().selectProjectProperty(projectId, SONAR_QUALITYGATE_PROPERTY).getValue()).isEqualTo(qGateId); + private void assertSelected(QGateWithOrgDto qualityGate, ComponentDto project) { + Optional<String> qGateUuid = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + assertThat(qGateUuid) + .isNotNull() + .isNotEmpty() + .hasValue(qualityGate.getUuid()); + String qGateId = dbClient.propertiesDao().selectProjectProperty(project.getId(), SONAR_QUALITYGATE_PROPERTY).getValue(); + assertThat(qGateId).isEqualTo(String.valueOf(qualityGate.getId())); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DestroyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DestroyActionTest.java index f11a9191242..9fe569ccaf2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DestroyActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DestroyActionTest.java @@ -119,6 +119,11 @@ public class DestroyActionTest { .isEmpty(); assertThat(db.getDbClient().propertiesDao().selectProjectProperties(prj2.getDbKey())) .isEmpty(); + + assertThat(db.getDbClient().projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, prj1.uuid())) + .isEmpty(); + assertThat(db.getDbClient().projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, prj2.uuid())) + .isEmpty(); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SelectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SelectActionTest.java index 4c53dcb6a7f..d38f148f5c8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SelectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/SelectActionTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.qualitygate.ws; +import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -106,6 +107,51 @@ public class SelectActionTest { } @Test + public void change_quality_gate_for_project() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + QGateWithOrgDto initialQualityGate = db.qualityGates().insertQualityGate(organization); + QGateWithOrgDto secondQualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + ws.newRequest() + .setParam("gateId", initialQualityGate.getId().toString()) + .setParam("projectKey", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); + + ws.newRequest() + .setParam("gateId", secondQualityGate.getId().toString()) + .setParam("projectKey", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); + + assertSelected(secondQualityGate, project); + } + + @Test + public void select_same_quality_gate_for_project_twice() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + QGateWithOrgDto initialQualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + + ws.newRequest() + .setParam("gateId", initialQualityGate.getId().toString()) + .setParam("projectKey", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); + + ws.newRequest() + .setParam("gateId", initialQualityGate.getId().toString()) + .setParam("projectKey", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); + + assertSelected(initialQualityGate, project); + } + + @Test public void project_admin() { OrganizationDto organization = db.organizations().insert(); QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); @@ -179,7 +225,7 @@ public class SelectActionTest { expectedException.expect(NotFoundException.class); ws.newRequest() - .setParam("gateId", String.valueOf("1")) + .setParam("gateId", "1") .setParam("projectKey", project.getKey()) .setParam("organization", organization.getKey()) .execute(); @@ -296,6 +342,11 @@ public class SelectActionTest { private void assertSelected(QualityGateDto qualityGate, ComponentDto project) { assertThat(dbClient.propertiesDao().selectProjectProperty(project.getId(), SONAR_QUALITYGATE_PROPERTY).getValue()).isEqualTo(qualityGate.getId().toString()); + Optional<String> qGateUuid = db.qualityGates().selectQGateUuidByComponentUuid(project.uuid()); + assertThat(qGateUuid) + .isNotNull() + .isNotEmpty() + .hasValue(qualityGate.getUuid()); } } |